445 lines
16 KiB
Diff
445 lines
16 KiB
Diff
Backport of:
|
|
|
|
From fe9d5d37234aca04fef7248184177168905a7a69 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
|
|
Date: Fri, 27 Sep 2024 00:12:57 +0300
|
|
Subject: [PATCH] qtdemux: Fix length checks and offsets in stsd entry parsing
|
|
|
|
Thanks to Antonio Morales for finding and reporting the issue.
|
|
|
|
Fixes GHSL-2024-242
|
|
Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3845
|
|
|
|
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
|
|
---
|
|
.../gst-plugins-good/gst/isomp4/qtdemux.c | 218 +++++++-----------
|
|
1 file changed, 79 insertions(+), 139 deletions(-)
|
|
|
|
--- a/gst/isomp4/qtdemux.c
|
|
+++ b/gst/isomp4/qtdemux.c
|
|
@@ -11369,43 +11369,35 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
case FOURCC_avc1:
|
|
case FOURCC_avc3:
|
|
{
|
|
- guint len = QT_UINT32 (stsd_entry_data);
|
|
+ guint32 len = QT_UINT32 (stsd_entry_data);
|
|
len = len <= 0x56 ? 0 : len - 0x56;
|
|
const guint8 *avc_data = stsd_entry_data + 0x56;
|
|
|
|
/* find avcC */
|
|
- while (len >= 0x8) {
|
|
- guint size;
|
|
+ while (len >= 8) {
|
|
+ guint32 size = QT_UINT32 (avc_data);
|
|
|
|
- if (QT_UINT32 (avc_data) <= 0x8)
|
|
- size = 0;
|
|
- else if (QT_UINT32 (avc_data) <= len)
|
|
- size = QT_UINT32 (avc_data) - 0x8;
|
|
- else
|
|
- size = len - 0x8;
|
|
-
|
|
- /* No real data, so skip */
|
|
- if (size < 1) {
|
|
- len -= 8;
|
|
- avc_data += 8;
|
|
- continue;
|
|
- }
|
|
+ if (size < 8 || size > len)
|
|
+ break;
|
|
|
|
- switch (QT_FOURCC (avc_data + 0x4)) {
|
|
+ switch (QT_FOURCC (avc_data + 4)) {
|
|
case FOURCC_avcC:
|
|
{
|
|
/* parse, if found */
|
|
GstBuffer *buf;
|
|
|
|
+ if (size < 8 + 1)
|
|
+ break;
|
|
+
|
|
GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
|
|
|
|
/* First 4 bytes are the length of the atom, the next 4 bytes
|
|
* are the fourcc, the next 1 byte is the version, and the
|
|
* subsequent bytes are profile_tier_level structure like data. */
|
|
gst_codec_utils_h264_caps_set_level_and_profile (entry->caps,
|
|
- avc_data + 8 + 1, size - 1);
|
|
- buf = gst_buffer_new_and_alloc (size);
|
|
- gst_buffer_fill (buf, 0, avc_data + 0x8, size);
|
|
+ avc_data + 8 + 1, size - 8 - 1);
|
|
+ buf = gst_buffer_new_and_alloc (size - 8);
|
|
+ gst_buffer_fill (buf, 0, avc_data + 8, size - 8);
|
|
gst_caps_set_simple (entry->caps,
|
|
"codec_data", GST_TYPE_BUFFER, buf, NULL);
|
|
gst_buffer_unref (buf);
|
|
@@ -11416,6 +11408,9 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
{
|
|
GstBuffer *buf;
|
|
|
|
+ if (size < 8 + 40 + 1)
|
|
+ break;
|
|
+
|
|
GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
|
|
|
|
/* First 4 bytes are the length of the atom, the next 4 bytes
|
|
@@ -11423,17 +11418,14 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
* next 1 byte is the version, and the
|
|
* subsequent bytes are sequence parameter set like data. */
|
|
|
|
- size -= 40; /* we'll be skipping BITMAPINFOHEADER */
|
|
- if (size > 1) {
|
|
- gst_codec_utils_h264_caps_set_level_and_profile
|
|
- (entry->caps, avc_data + 8 + 40 + 1, size - 1);
|
|
+ gst_codec_utils_h264_caps_set_level_and_profile
|
|
+ (entry->caps, avc_data + 8 + 40 + 1, size - 8 - 40 - 1);
|
|
|
|
- buf = gst_buffer_new_and_alloc (size);
|
|
- gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
|
|
- gst_caps_set_simple (entry->caps,
|
|
- "codec_data", GST_TYPE_BUFFER, buf, NULL);
|
|
- gst_buffer_unref (buf);
|
|
- }
|
|
+ buf = gst_buffer_new_and_alloc (size - 8 - 40);
|
|
+ gst_buffer_fill (buf, 0, avc_data + 8 + 40, size - 8 - 40);
|
|
+ gst_caps_set_simple (entry->caps,
|
|
+ "codec_data", GST_TYPE_BUFFER, buf, NULL);
|
|
+ gst_buffer_unref (buf);
|
|
break;
|
|
}
|
|
case FOURCC_btrt:
|
|
@@ -11441,11 +11433,11 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
guint avg_bitrate, max_bitrate;
|
|
|
|
/* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
|
|
- if (size < 12)
|
|
+ if (size < 8 + 12)
|
|
break;
|
|
|
|
- max_bitrate = QT_UINT32 (avc_data + 0xc);
|
|
- avg_bitrate = QT_UINT32 (avc_data + 0x10);
|
|
+ max_bitrate = QT_UINT32 (avc_data + 8 + 4);
|
|
+ avg_bitrate = QT_UINT32 (avc_data + 8 + 8);
|
|
|
|
if (!max_bitrate && !avg_bitrate)
|
|
break;
|
|
@@ -11477,8 +11469,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
break;
|
|
}
|
|
|
|
- len -= size + 8;
|
|
- avc_data += size + 8;
|
|
+ len -= size;
|
|
+ avc_data += size;
|
|
}
|
|
|
|
break;
|
|
@@ -11489,44 +11481,36 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
case FOURCC_dvh1:
|
|
case FOURCC_dvhe:
|
|
{
|
|
- guint len = QT_UINT32 (stsd_entry_data);
|
|
+ guint32 len = QT_UINT32 (stsd_entry_data);
|
|
len = len <= 0x56 ? 0 : len - 0x56;
|
|
const guint8 *hevc_data = stsd_entry_data + 0x56;
|
|
|
|
/* find hevc */
|
|
- while (len >= 0x8) {
|
|
- guint size;
|
|
+ while (len >= 8) {
|
|
+ guint32 size = QT_UINT32 (hevc_data);
|
|
|
|
- if (QT_UINT32 (hevc_data) <= 0x8)
|
|
- size = 0;
|
|
- else if (QT_UINT32 (hevc_data) <= len)
|
|
- size = QT_UINT32 (hevc_data) - 0x8;
|
|
- else
|
|
- size = len - 0x8;
|
|
-
|
|
- /* No real data, so skip */
|
|
- if (size < 1) {
|
|
- len -= 8;
|
|
- hevc_data += 8;
|
|
- continue;
|
|
- }
|
|
+ if (size < 8 || size > len)
|
|
+ break;
|
|
|
|
- switch (QT_FOURCC (hevc_data + 0x4)) {
|
|
+ switch (QT_FOURCC (hevc_data + 4)) {
|
|
case FOURCC_hvcC:
|
|
{
|
|
/* parse, if found */
|
|
GstBuffer *buf;
|
|
|
|
+ if (size < 8 + 1)
|
|
+ break;
|
|
+
|
|
GST_DEBUG_OBJECT (qtdemux, "found hvcC codec_data in stsd");
|
|
|
|
/* First 4 bytes are the length of the atom, the next 4 bytes
|
|
* are the fourcc, the next 1 byte is the version, and the
|
|
* subsequent bytes are sequence parameter set like data. */
|
|
gst_codec_utils_h265_caps_set_level_tier_and_profile
|
|
- (entry->caps, hevc_data + 8 + 1, size - 1);
|
|
+ (entry->caps, hevc_data + 8 + 1, size - 8 - 1);
|
|
|
|
- buf = gst_buffer_new_and_alloc (size);
|
|
- gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
|
|
+ buf = gst_buffer_new_and_alloc (size - 8);
|
|
+ gst_buffer_fill (buf, 0, hevc_data + 8, size - 8);
|
|
gst_caps_set_simple (entry->caps,
|
|
"codec_data", GST_TYPE_BUFFER, buf, NULL);
|
|
gst_buffer_unref (buf);
|
|
@@ -11535,8 +11519,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
default:
|
|
break;
|
|
}
|
|
- len -= size + 8;
|
|
- hevc_data += size + 8;
|
|
+ len -= size;
|
|
+ hevc_data += size;
|
|
}
|
|
break;
|
|
}
|
|
@@ -11916,36 +11900,25 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
}
|
|
case FOURCC_vc_1:
|
|
{
|
|
- guint len = QT_UINT32 (stsd_entry_data);
|
|
+ guint32 len = QT_UINT32 (stsd_entry_data);
|
|
len = len <= 0x56 ? 0 : len - 0x56;
|
|
const guint8 *vc1_data = stsd_entry_data + 0x56;
|
|
|
|
/* find dvc1 */
|
|
while (len >= 8) {
|
|
- guint size;
|
|
+ guint32 size = QT_UINT32 (vc1_data);
|
|
|
|
- if (QT_UINT32 (vc1_data) <= 8)
|
|
- size = 0;
|
|
- else if (QT_UINT32 (vc1_data) <= len)
|
|
- size = QT_UINT32 (vc1_data) - 8;
|
|
- else
|
|
- size = len - 8;
|
|
-
|
|
- /* No real data, so skip */
|
|
- if (size < 1) {
|
|
- len -= 8;
|
|
- vc1_data += 8;
|
|
- continue;
|
|
- }
|
|
+ if (size < 8 || size > len)
|
|
+ break;
|
|
|
|
- switch (QT_FOURCC (vc1_data + 0x4)) {
|
|
+ switch (QT_FOURCC (vc1_data + 4)) {
|
|
case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
|
|
{
|
|
GstBuffer *buf;
|
|
|
|
GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
|
|
- buf = gst_buffer_new_and_alloc (size);
|
|
- gst_buffer_fill (buf, 0, vc1_data + 8, size);
|
|
+ buf = gst_buffer_new_and_alloc (size - 8);
|
|
+ gst_buffer_fill (buf, 0, vc1_data + 8, size - 8);
|
|
gst_caps_set_simple (entry->caps,
|
|
"codec_data", GST_TYPE_BUFFER, buf, NULL);
|
|
gst_buffer_unref (buf);
|
|
@@ -11954,36 +11927,25 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
default:
|
|
break;
|
|
}
|
|
- len -= size + 8;
|
|
- vc1_data += size + 8;
|
|
+ len -= size;
|
|
+ vc1_data += size;
|
|
}
|
|
break;
|
|
}
|
|
case FOURCC_av01:
|
|
{
|
|
- guint len = QT_UINT32 (stsd_entry_data);
|
|
+ guint32 len = QT_UINT32 (stsd_entry_data);
|
|
len = len <= 0x56 ? 0 : len - 0x56;
|
|
const guint8 *av1_data = stsd_entry_data + 0x56;
|
|
|
|
/* find av1C */
|
|
- while (len >= 0x8) {
|
|
- guint size;
|
|
+ while (len >= 8) {
|
|
+ guint32 size = QT_UINT32 (av1_data);
|
|
|
|
- if (QT_UINT32 (av1_data) <= 0x8)
|
|
- size = 0;
|
|
- else if (QT_UINT32 (av1_data) <= len)
|
|
- size = QT_UINT32 (av1_data) - 0x8;
|
|
- else
|
|
- size = len - 0x8;
|
|
-
|
|
- /* No real data, so skip */
|
|
- if (size < 1) {
|
|
- len -= 8;
|
|
- av1_data += 8;
|
|
- continue;
|
|
- }
|
|
+ if (size < 8 || size > len)
|
|
+ break;
|
|
|
|
- switch (QT_FOURCC (av1_data + 0x4)) {
|
|
+ switch (QT_FOURCC (av1_data + 4)) {
|
|
case FOURCC_av1C:
|
|
{
|
|
/* parse, if found */
|
|
@@ -11994,7 +11956,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
"found av1C codec_data in stsd of size %d", size);
|
|
|
|
/* not enough data, just ignore and hope for the best */
|
|
- if (size < 5)
|
|
+ if (size < 8 + 5)
|
|
break;
|
|
|
|
/* Content is:
|
|
@@ -12020,10 +11982,10 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
"presentation-delay", G_TYPE_INT,
|
|
(gint) (pres_delay_field & 0x0F) + 1, NULL);
|
|
}
|
|
- if (size > 5) {
|
|
- buf = gst_buffer_new_and_alloc (size - 5);
|
|
+ if (size > 8 + 5) {
|
|
+ buf = gst_buffer_new_and_alloc (size - 8 - 5);
|
|
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
|
|
- gst_buffer_fill (buf, 0, av1_data + 13, size - 5);
|
|
+ gst_buffer_fill (buf, 0, av1_data + 13, size - 8 - 5);
|
|
gst_caps_set_simple (entry->caps,
|
|
"codec_data", GST_TYPE_BUFFER, buf, NULL);
|
|
gst_buffer_unref (buf);
|
|
@@ -12034,8 +11996,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
break;
|
|
}
|
|
|
|
- len -= size + 8;
|
|
- av1_data += size + 8;
|
|
+ len -= size;
|
|
+ av1_data += size;
|
|
}
|
|
|
|
break;
|
|
@@ -12046,29 +12008,18 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
* vp08, vp09, and vp10 fourcc. */
|
|
case FOURCC_vp09:
|
|
{
|
|
- guint len = QT_UINT32 (stsd_entry_data);
|
|
+ guint32 len = QT_UINT32 (stsd_entry_data);
|
|
len = len <= 0x56 ? 0 : len - 0x56;
|
|
const guint8 *vpcc_data = stsd_entry_data + 0x56;
|
|
|
|
/* find vpcC */
|
|
- while (len >= 0x8) {
|
|
- guint size;
|
|
+ while (len >= 8) {
|
|
+ guint32 size = QT_UINT32 (vpcc_data);
|
|
|
|
- if (QT_UINT32 (vpcc_data) <= 0x8)
|
|
- size = 0;
|
|
- else if (QT_UINT32 (vpcc_data) <= len)
|
|
- size = QT_UINT32 (vpcc_data) - 0x8;
|
|
- else
|
|
- size = len - 0x8;
|
|
-
|
|
- /* No real data, so skip */
|
|
- if (size < 1) {
|
|
- len -= 8;
|
|
- vpcc_data += 8;
|
|
- continue;
|
|
- }
|
|
+ if (size < 8 || size > len)
|
|
+ break;
|
|
|
|
- switch (QT_FOURCC (vpcc_data + 0x4)) {
|
|
+ switch (QT_FOURCC (vpcc_data + 4)) {
|
|
case FOURCC_vpcC:
|
|
{
|
|
const gchar *profile_str = NULL;
|
|
@@ -12084,7 +12035,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
|
|
/* the meaning of "size" is length of the atom body, excluding
|
|
* atom length and fourcc fields */
|
|
- if (size < 12)
|
|
+ if (size < 8 + 12)
|
|
break;
|
|
|
|
/* Content is:
|
|
@@ -12190,8 +12141,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
break;
|
|
}
|
|
|
|
- len -= size + 8;
|
|
- vpcc_data += size + 8;
|
|
+ len -= size;
|
|
+ vpcc_data += size;
|
|
}
|
|
|
|
break;
|
|
@@ -12522,7 +12473,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
}
|
|
case FOURCC_wma_:
|
|
{
|
|
- guint len = QT_UINT32 (stsd_entry_data);
|
|
+ guint32 len = QT_UINT32 (stsd_entry_data);
|
|
len = len <= offset ? 0 : len - offset;
|
|
const guint8 *wfex_data = stsd_entry_data + offset;
|
|
const gchar *codec_name = NULL;
|
|
@@ -12547,21 +12498,10 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
|
|
/* find wfex */
|
|
while (len >= 8) {
|
|
- guint size;
|
|
+ guint32 size = QT_UINT32 (wfex_data);
|
|
|
|
- if (QT_UINT32 (wfex_data) <= 0x8)
|
|
- size = 0;
|
|
- else if (QT_UINT32 (wfex_data) <= len)
|
|
- size = QT_UINT32 (wfex_data) - 8;
|
|
- else
|
|
- size = len - 8;
|
|
-
|
|
- /* No real data, so skip */
|
|
- if (size < 1) {
|
|
- len -= 8;
|
|
- wfex_data += 8;
|
|
- continue;
|
|
- }
|
|
+ if (size < 8 || size > len)
|
|
+ break;
|
|
|
|
switch (QT_FOURCC (wfex_data + 4)) {
|
|
case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
|
|
@@ -12606,12 +12546,12 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
"width", G_TYPE_INT, wfex.wBitsPerSample,
|
|
"depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
|
|
|
|
- if (size > wfex.cbSize) {
|
|
+ if (size > 8 + wfex.cbSize) {
|
|
GstBuffer *buf;
|
|
|
|
- buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
|
|
+ buf = gst_buffer_new_and_alloc (size - 8 - wfex.cbSize);
|
|
gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
|
|
- size - wfex.cbSize);
|
|
+ size - 8 - wfex.cbSize);
|
|
gst_caps_set_simple (entry->caps,
|
|
"codec_data", GST_TYPE_BUFFER, buf, NULL);
|
|
gst_buffer_unref (buf);
|
|
@@ -12628,8 +12568,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
|
|
default:
|
|
break;
|
|
}
|
|
- len -= size + 8;
|
|
- wfex_data += size + 8;
|
|
+ len -= size;
|
|
+ wfex_data += size;
|
|
}
|
|
break;
|
|
}
|