From a4e2f84dd89455a2edb38ad2dc5d53110a6f8c8f Mon Sep 17 00:00:00 2001 From: RHEL Packaging Agent Date: Wed, 29 Jul 2026 09:18:07 +0000 Subject: [PATCH] Fix CVE-2026-53703: rmdemux header parsing bounds checks Backport upstream commit 9de204d7a0d7 to fix CVE-2026-53703 in the RealMedia demuxer (rmdemux). The patch adds length validation checks to header parsing functions (parse__rmf, parse_prop, parse_mdpr, parse_indx, parse_data, parse_cont, parse_indx_data), changes their return types to gboolean to allow callers to detect and skip invalid header packets, and uses overflow-safe arithmetic for index size calculations. Conflict resolution was needed because the RHEL codebase uses int length parameters rather than the upstream gsize refactoring. CVE: CVE-2026-53703 Upstream patches: - https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/9de204d7a0d7f584cddfc922f3b9fbe4058ae22a.patch Resolves: RHEL-184440 This commit was backported by Ymir, a Red Hat Enterprise Linux software maintenance AI agent. Assisted-by: Ymir --- ...at-enough-data-is-available-before-p.patch | 609 ++++++++++++++++++ gstreamer1-plugins-ugly-free.spec | 8 +- 2 files changed, 616 insertions(+), 1 deletion(-) create mode 100644 0004-rmdemux-Check-that-enough-data-is-available-before-p.patch diff --git a/0004-rmdemux-Check-that-enough-data-is-available-before-p.patch b/0004-rmdemux-Check-that-enough-data-is-available-before-p.patch new file mode 100644 index 0000000..d46a015 --- /dev/null +++ b/0004-rmdemux-Check-that-enough-data-is-available-before-p.patch @@ -0,0 +1,609 @@ +From 4a1689252f97ff4dfcd53a076af03b4abe91bf08 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= +Date: Thu, 11 Jun 2026 15:31:55 +0300 +Subject: [PATCH] rmdemux: Check that enough data is available before parsing + header data + +And skip over invalid header packets. + +Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/work_items/5039 +Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/work_items/5038 + +Part-of: +--- + .../gst-plugins-ugly/gst/realmedia/rmdemux.c | 301 ++++++++++++++++++++++++++++------------ + .../gst-plugins-ugly/gst/realmedia/rmdemux.h | 2 +- + 2 files changed, 213 insertions(+), 90 deletions(-) + +diff --git a/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c b/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c +index 19beb51..a453230 100644 +--- a/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c ++++ b/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c +@@ -150,21 +150,21 @@ static gboolean gst_rmdemux_src_query (GstPad * pad, GstObject * parent, + static gboolean gst_rmdemux_perform_seek (GstRMDemux * rmdemux, + GstEvent * event); + +-static void gst_rmdemux_parse__rmf (GstRMDemux * rmdemux, const guint8 * data, +- int length); +-static void gst_rmdemux_parse_prop (GstRMDemux * rmdemux, const guint8 * data, +- int length); +-static void gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, ++static gboolean gst_rmdemux_parse__rmf (GstRMDemux * rmdemux, ++ const guint8 * data, int length); ++static gboolean gst_rmdemux_parse_prop (GstRMDemux * rmdemux, ++ const guint8 * data, int length); ++static gboolean gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, ++ const guint8 * data, int length); ++static gboolean gst_rmdemux_parse_indx (GstRMDemux * rmdemux, ++ const guint8 * data, int length, gsize * index_length); ++static gboolean gst_rmdemux_parse_data (GstRMDemux * rmdemux, ++ const guint8 * data, int length); ++static gboolean gst_rmdemux_parse_cont (GstRMDemux * rmdemux, + const guint8 * data, int length); +-static guint gst_rmdemux_parse_indx (GstRMDemux * rmdemux, const guint8 * data, +- int length); +-static void gst_rmdemux_parse_data (GstRMDemux * rmdemux, const guint8 * data, +- int length); +-static void gst_rmdemux_parse_cont (GstRMDemux * rmdemux, const guint8 * data, +- int length); + static GstFlowReturn gst_rmdemux_parse_packet (GstRMDemux * rmdemux, + GstBuffer * in, guint16 version); +-static void gst_rmdemux_parse_indx_data (GstRMDemux * rmdemux, ++static gboolean gst_rmdemux_parse_indx_data (GstRMDemux * rmdemux, + const guint8 * data, int length); + static void gst_rmdemux_stream_clear_cached_subpackets (GstRMDemux * rmdemux, + GstRMDemuxStream * stream); +@@ -1060,13 +1060,22 @@ gst_rmdemux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) + switch (rmdemux->state) { + case RMDEMUX_STATE_HEADER: + { ++ guint32 size; + if (gst_adapter_available (rmdemux->adapter) < HEADER_SIZE) + goto unlock; + + data = gst_adapter_map (rmdemux->adapter, HEADER_SIZE); + + rmdemux->object_id = RMDEMUX_FOURCC_GET (data + 0); +- rmdemux->size = RMDEMUX_GUINT32_GET (data + 4) - HEADER_SIZE; ++ size = RMDEMUX_GUINT32_GET (data + 4); ++ if (size < HEADER_SIZE) { ++ GST_WARNING_OBJECT (rmdemux, "Bogus looking header, too small size"); ++ gst_adapter_unmap (rmdemux->adapter); ++ gst_adapter_flush (rmdemux->adapter, 4); ++ ++ break; ++ } ++ rmdemux->size = size - HEADER_SIZE; + rmdemux->object_version = RMDEMUX_GUINT16_GET (data + 8); + + /* Sanity-check. We assume that the FOURCC is printable ASCII */ +@@ -1085,7 +1094,7 @@ gst_rmdemux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) + + GST_LOG_OBJECT (rmdemux, "header found with object_id=%" + GST_FOURCC_FORMAT +- " size=%08x object_version=%d", ++ " size=%08" G_GSIZE_FORMAT " object_version=%d", + GST_FOURCC_ARGS (rmdemux->object_id), rmdemux->size, + rmdemux->object_version); + +@@ -1136,7 +1145,8 @@ gst_rmdemux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) + + if ((rmdemux->object_version == 0) || (rmdemux->object_version == 1)) { + data = gst_adapter_map (rmdemux->adapter, rmdemux->size); +- gst_rmdemux_parse__rmf (rmdemux, data, rmdemux->size); ++ if (!gst_rmdemux_parse__rmf (rmdemux, data, rmdemux->size)) ++ GST_WARNING_OBJECT (rmdemux, "Failed parsing _rmf"); + gst_adapter_unmap (rmdemux->adapter); + gst_adapter_flush (rmdemux->adapter, rmdemux->size); + } else { +@@ -1151,7 +1161,8 @@ gst_rmdemux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) + goto unlock; + + data = gst_adapter_map (rmdemux->adapter, rmdemux->size); +- gst_rmdemux_parse_prop (rmdemux, data, rmdemux->size); ++ if (!gst_rmdemux_parse_prop (rmdemux, data, rmdemux->size)) ++ GST_WARNING_OBJECT (rmdemux, "Failed parsing prop"); + gst_adapter_unmap (rmdemux->adapter); + gst_adapter_flush (rmdemux->adapter, rmdemux->size); + +@@ -1164,7 +1175,8 @@ gst_rmdemux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) + goto unlock; + + data = gst_adapter_map (rmdemux->adapter, rmdemux->size); +- gst_rmdemux_parse_mdpr (rmdemux, data, rmdemux->size); ++ if (!gst_rmdemux_parse_mdpr (rmdemux, data, rmdemux->size)) ++ GST_WARNING_OBJECT (rmdemux, "Failed parsing mdpr"); + gst_adapter_unmap (rmdemux->adapter); + gst_adapter_flush (rmdemux->adapter, rmdemux->size); + +@@ -1177,7 +1189,8 @@ gst_rmdemux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) + goto unlock; + + data = gst_adapter_map (rmdemux->adapter, rmdemux->size); +- gst_rmdemux_parse_cont (rmdemux, data, rmdemux->size); ++ if (!gst_rmdemux_parse_cont (rmdemux, data, rmdemux->size)) ++ GST_WARNING_OBJECT (rmdemux, "Failed parsing cont"); + gst_adapter_unmap (rmdemux->adapter); + gst_adapter_flush (rmdemux->adapter, rmdemux->size); + +@@ -1186,6 +1199,8 @@ gst_rmdemux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) + } + case RMDEMUX_STATE_HEADER_DATA: + { ++ gsize orig_size; ++ + /* If we haven't already done so then signal there are no more pads */ + if (!rmdemux->have_pads) { + GST_LOG_OBJECT (rmdemux, "no more pads"); +@@ -1194,6 +1209,7 @@ gst_rmdemux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) + } + + /* The actual header is only 8 bytes */ ++ orig_size = rmdemux->size; + rmdemux->size = DATA_SIZE; + GST_LOG_OBJECT (rmdemux, "data available %" G_GSIZE_FORMAT, + gst_adapter_available (rmdemux->adapter)); +@@ -1201,7 +1217,14 @@ gst_rmdemux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) + goto unlock; + + data = gst_adapter_map (rmdemux->adapter, rmdemux->size); +- gst_rmdemux_parse_data (rmdemux, data, rmdemux->size); ++ if (!gst_rmdemux_parse_data (rmdemux, data, rmdemux->size)) { ++ GST_WARNING_OBJECT (rmdemux, "Failed parsing header data"); ++ gst_adapter_unmap (rmdemux->adapter); ++ /* skip over whole packet size */ ++ gst_adapter_flush (rmdemux->adapter, orig_size); ++ rmdemux->state = RMDEMUX_STATE_HEADER; ++ break; ++ } + gst_adapter_unmap (rmdemux->adapter); + gst_adapter_flush (rmdemux->adapter, rmdemux->size); + +@@ -1214,7 +1237,15 @@ gst_rmdemux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) + goto unlock; + + data = gst_adapter_map (rmdemux->adapter, rmdemux->size); +- rmdemux->size = gst_rmdemux_parse_indx (rmdemux, data, rmdemux->size); ++ if (!gst_rmdemux_parse_indx (rmdemux, data, rmdemux->size, ++ &rmdemux->size)) { ++ GST_WARNING_OBJECT (rmdemux, "Failed parsing header indx"); ++ gst_adapter_unmap (rmdemux->adapter); ++ /* skip over whole packet size */ ++ gst_adapter_flush (rmdemux->adapter, rmdemux->size); ++ rmdemux->state = RMDEMUX_STATE_HEADER; ++ break; ++ } + /* Only flush the header */ + gst_adapter_unmap (rmdemux->adapter); + gst_adapter_flush (rmdemux->adapter, HEADER_SIZE); +@@ -1230,7 +1261,8 @@ gst_rmdemux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) + goto unlock; + + data = gst_adapter_map (rmdemux->adapter, rmdemux->size); +- gst_rmdemux_parse_indx_data (rmdemux, data, rmdemux->size); ++ if (!gst_rmdemux_parse_indx_data (rmdemux, data, rmdemux->size)) ++ GST_WARNING_OBJECT (rmdemux, "Failed parsing indx data"); + gst_adapter_unmap (rmdemux->adapter); + gst_adapter_flush (rmdemux->adapter, rmdemux->size); + } +@@ -1617,26 +1649,34 @@ beach: + gst_caps_unref (stream_caps); + } + +-static int ++static gsize + re_skip_pascal_string (const guint8 * ptr) + { +- int length; ++ gsize length; + + length = ptr[0]; + + return length + 1; + } + +-static void ++static gboolean + gst_rmdemux_parse__rmf (GstRMDemux * rmdemux, const guint8 * data, int length) + { ++ if (length < 8) ++ return FALSE; ++ + GST_LOG_OBJECT (rmdemux, "file_version: %d", RMDEMUX_GUINT32_GET (data)); + GST_LOG_OBJECT (rmdemux, "num_headers: %d", RMDEMUX_GUINT32_GET (data + 4)); ++ ++ return TRUE; + } + +-static void ++static gboolean + gst_rmdemux_parse_prop (GstRMDemux * rmdemux, const guint8 * data, int length) + { ++ if (length < 40) ++ return FALSE; ++ + GST_LOG_OBJECT (rmdemux, "max bitrate: %d", RMDEMUX_GUINT32_GET (data)); + GST_LOG_OBJECT (rmdemux, "avg bitrate: %d", RMDEMUX_GUINT32_GET (data + 4)); + GST_LOG_OBJECT (rmdemux, "max packet size: %d", +@@ -1658,9 +1698,11 @@ gst_rmdemux_parse_prop (GstRMDemux * rmdemux, const guint8 * data, int length) + rmdemux->data_offset); + GST_LOG_OBJECT (rmdemux, "n streams: %d", RMDEMUX_GUINT16_GET (data + 36)); + GST_LOG_OBJECT (rmdemux, "flags: 0x%04x", RMDEMUX_GUINT16_GET (data + 38)); ++ ++ return TRUE; + } + +-static void ++static gboolean + gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, int length) + { + GstRMDemuxStream *stream; +@@ -1668,10 +1710,12 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, int length) + char *stream2_type_string; + guint str_len = 0; + int stream_type; +- int offset; + guint32 max_bitrate; + guint32 avg_bitrate; + ++ if (length < 10) ++ return FALSE; ++ + stream = g_new0 (GstRMDemuxStream, 1); + + stream->id = RMDEMUX_GUINT16_GET (data); +@@ -1702,13 +1746,18 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, int length) + GST_TAG_BITRATE, avg_bitrate, NULL); + } + +- offset = 30; +- stream1_type_string = gst_rm_utils_read_string8 (data + offset, +- length - offset, &str_len); +- offset += str_len; +- stream2_type_string = gst_rm_utils_read_string8 (data + offset, +- length - offset, &str_len); +- offset += str_len; ++ if (length < 30) { ++ gst_rmdemux_free_stream (rmdemux, stream); ++ return FALSE; ++ } ++ data += 30; ++ length -= 30; ++ stream1_type_string = gst_rm_utils_read_string8 (data, length, &str_len); ++ data += str_len; ++ length -= str_len; ++ stream2_type_string = gst_rm_utils_read_string8 (data, length, &str_len); ++ data += str_len; ++ length -= str_len; + + /* stream1_type_string for audio and video stream is a "put_whatever_you_want" field : + * observed values : +@@ -1718,6 +1767,13 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, int length) + * so, we should not rely on it to know which stream type it is + */ + ++ if (!stream1_type_string || !stream2_type_string) { ++ g_free (stream1_type_string); ++ g_free (stream2_type_string); ++ gst_rmdemux_free_stream (rmdemux, stream); ++ return FALSE; ++ } ++ + GST_LOG_OBJECT (rmdemux, "stream type: %s", stream1_type_string); + GST_LOG_OBJECT (rmdemux, "MIME type=%s", stream2_type_string); + +@@ -1748,27 +1804,36 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, int length) + g_free (stream1_type_string); + g_free (stream2_type_string); + +- offset += 4; ++ if (length < 4) { ++ gst_rmdemux_free_stream (rmdemux, stream); ++ return FALSE; ++ } ++ data += 4; ++ length -= 4; + + stream->subtype = stream_type; + switch (stream_type) { + + case GST_RMDEMUX_STREAM_VIDEO: ++ if (length < 34) { ++ gst_rmdemux_free_stream (rmdemux, stream); ++ return FALSE; ++ } + /* RV10/RV20/RV30/RV40 => video/x-pn-realvideo, version=1,2,3,4 */ +- stream->fourcc = RMDEMUX_FOURCC_GET (data + offset + 8); +- stream->width = RMDEMUX_GUINT16_GET (data + offset + 12); +- stream->height = RMDEMUX_GUINT16_GET (data + offset + 14); +- stream->rate = RMDEMUX_GUINT16_GET (data + offset + 16); +- stream->subformat = RMDEMUX_GUINT32_GET (data + offset + 26); +- stream->format = RMDEMUX_GUINT32_GET (data + offset + 30); +- stream->extra_data_size = length - (offset + 26); +- stream->extra_data = (guint8 *) data + offset + 26; ++ stream->fourcc = RMDEMUX_FOURCC_GET (data + 8); ++ stream->width = RMDEMUX_GUINT16_GET (data + 12); ++ stream->height = RMDEMUX_GUINT16_GET (data + 14); ++ stream->rate = RMDEMUX_GUINT16_GET (data + 16); ++ stream->subformat = RMDEMUX_GUINT32_GET (data + 26); ++ stream->format = RMDEMUX_GUINT32_GET (data + 30); ++ stream->extra_data_size = length - 26; ++ stream->extra_data = (guint8 *) data + 26; + /* Natural way to represent framerates here requires unsigned 32 bit + * numerator, which we don't have. For the nasty case, approximate... + */ + { +- guint32 numerator = RMDEMUX_GUINT16_GET (data + offset + 22) * 65536 + +- RMDEMUX_GUINT16_GET (data + offset + 24); ++ guint32 numerator = RMDEMUX_GUINT16_GET (data + 22) * 65536 + ++ RMDEMUX_GUINT16_GET (data + 24); + if (numerator > G_MAXINT) { + stream->framerate_numerator = (gint) (numerator >> 1); + stream->framerate_denominator = 32768; +@@ -1787,7 +1852,11 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, int length) + stream->extra_data_size); + break; + case GST_RMDEMUX_STREAM_AUDIO:{ +- stream->version = RMDEMUX_GUINT16_GET (data + offset + 4); ++ if (length < 6) { ++ gst_rmdemux_free_stream (rmdemux, stream); ++ return FALSE; ++ } ++ stream->version = RMDEMUX_GUINT16_GET (data + 4); + GST_INFO ("stream version = %u", stream->version); + switch (stream->version) { + case 3: +@@ -1801,40 +1870,48 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, int length) + stream->height = 0; + break; + case 4: +- stream->flavor = RMDEMUX_GUINT16_GET (data + offset + 22); +- stream->packet_size = RMDEMUX_GUINT32_GET (data + offset + 24); +- /* stream->frame_size = RMDEMUX_GUINT32_GET (data + offset + 42); */ +- stream->leaf_size = RMDEMUX_GUINT16_GET (data + offset + 44); +- stream->height = RMDEMUX_GUINT16_GET (data + offset + 40); +- stream->rate = RMDEMUX_GUINT16_GET (data + offset + 48); +- stream->sample_width = RMDEMUX_GUINT16_GET (data + offset + 52); +- stream->n_channels = RMDEMUX_GUINT16_GET (data + offset + 54); +- stream->fourcc = RMDEMUX_FOURCC_GET (data + offset + 62); +- stream->extra_data_size = RMDEMUX_GUINT32_GET (data + offset + 69); ++ if (length < 73) { ++ gst_rmdemux_free_stream (rmdemux, stream); ++ return FALSE; ++ } ++ stream->flavor = RMDEMUX_GUINT16_GET (data + 22); ++ stream->packet_size = RMDEMUX_GUINT32_GET (data + 24); ++ /* stream->frame_size = RMDEMUX_GUINT32_GET (data + 42); */ ++ stream->leaf_size = RMDEMUX_GUINT16_GET (data + 44); ++ stream->height = RMDEMUX_GUINT16_GET (data + 40); ++ stream->rate = RMDEMUX_GUINT16_GET (data + 48); ++ stream->sample_width = RMDEMUX_GUINT16_GET (data + 52); ++ stream->n_channels = RMDEMUX_GUINT16_GET (data + 54); ++ stream->fourcc = RMDEMUX_FOURCC_GET (data + 62); ++ stream->extra_data_size = RMDEMUX_GUINT32_GET (data + 69); + GST_DEBUG_OBJECT (rmdemux, "%u bytes of extra codec data", + stream->extra_data_size); +- if (length - (offset + 73) >= stream->extra_data_size) { +- stream->extra_data = (guint8 *) data + offset + 73; ++ if (length - 73 >= stream->extra_data_size) { ++ stream->extra_data = (guint8 *) data + 73; + } else { + GST_WARNING_OBJECT (rmdemux, "codec data runs beyond MDPR chunk"); + stream->extra_data_size = 0; + } + break; + case 5: +- stream->flavor = RMDEMUX_GUINT16_GET (data + offset + 22); +- stream->packet_size = RMDEMUX_GUINT32_GET (data + offset + 24); +- /* stream->frame_size = RMDEMUX_GUINT32_GET (data + offset + 42); */ +- stream->leaf_size = RMDEMUX_GUINT16_GET (data + offset + 44); +- stream->height = RMDEMUX_GUINT16_GET (data + offset + 40); +- stream->rate = RMDEMUX_GUINT16_GET (data + offset + 54); +- stream->sample_width = RMDEMUX_GUINT16_GET (data + offset + 58); +- stream->n_channels = RMDEMUX_GUINT16_GET (data + offset + 60); +- stream->fourcc = RMDEMUX_FOURCC_GET (data + offset + 66); +- stream->extra_data_size = RMDEMUX_GUINT32_GET (data + offset + 74); ++ if (length < 78) { ++ gst_rmdemux_free_stream (rmdemux, stream); ++ return FALSE; ++ } ++ stream->flavor = RMDEMUX_GUINT16_GET (data + 22); ++ stream->packet_size = RMDEMUX_GUINT32_GET (data + 24); ++ /* stream->frame_size = RMDEMUX_GUINT32_GET (data + 42); */ ++ stream->leaf_size = RMDEMUX_GUINT16_GET (data + 44); ++ stream->height = RMDEMUX_GUINT16_GET (data + 40); ++ stream->rate = RMDEMUX_GUINT16_GET (data + 54); ++ stream->sample_width = RMDEMUX_GUINT16_GET (data + 58); ++ stream->n_channels = RMDEMUX_GUINT16_GET (data + 60); ++ stream->fourcc = RMDEMUX_FOURCC_GET (data + 66); ++ stream->extra_data_size = RMDEMUX_GUINT32_GET (data + 74); + GST_DEBUG_OBJECT (rmdemux, "%u bytes of extra codec data", + stream->extra_data_size); +- if (length - (offset + 78) >= stream->extra_data_size) { +- stream->extra_data = (guint8 *) data + offset + 78; ++ if (length - 78 >= stream->extra_data_size) { ++ stream->extra_data = (guint8 *) data + 78; + } else { + GST_WARNING_OBJECT (rmdemux, "codec data runs beyond MDPR chunk"); + stream->extra_data_size = 0; +@@ -1857,32 +1934,60 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, int length) + { + int element_nb; + ++ if (length < 12) { ++ gst_rmdemux_free_stream (rmdemux, stream); ++ return FALSE; ++ } ++ + /* Length of this section */ +- GST_DEBUG_OBJECT (rmdemux, "length2: 0x%08x", +- RMDEMUX_GUINT32_GET (data + offset)); +- offset += 4; ++ GST_DEBUG_OBJECT (rmdemux, "length2: 0x%08x", RMDEMUX_GUINT32_GET (data)); + + /* Unknown : 00 00 00 00 */ +- offset += 4; + + /* Number of variables that would follow (loop iterations) */ +- element_nb = RMDEMUX_GUINT32_GET (data + offset); +- offset += 4; ++ element_nb = RMDEMUX_GUINT32_GET (data + 8); ++ ++ data += 12; ++ length -= 12; + + while (element_nb) { ++ gsize str_len; ++ + /* Category Id : 00 00 00 XX 00 00 */ +- offset += 6; ++ if (length < 6 + 1) { ++ gst_rmdemux_free_stream (rmdemux, stream); ++ return FALSE; ++ } ++ data += 6; ++ length -= 6; + + /* Variable Name */ +- offset += re_skip_pascal_string (data + offset); ++ str_len = re_skip_pascal_string (data); ++ if (length < str_len) { ++ gst_rmdemux_free_stream (rmdemux, stream); ++ return FALSE; ++ } ++ data += str_len; ++ length -= str_len; + + /* Variable Value Type */ + /* 00 00 00 00 00 => integer/boolean, preceded by length */ + /* 00 00 00 02 00 => pascal string, preceded by length, no trailing \0 */ +- offset += 5; ++ if (length < 5 + 1) { ++ gst_rmdemux_free_stream (rmdemux, stream); ++ return FALSE; ++ } ++ data += 5; ++ length -= 5; + + /* Variable Value */ +- offset += re_skip_pascal_string (data + offset); ++ str_len = re_skip_pascal_string (data); ++ if (length < str_len) { ++ gst_rmdemux_free_stream (rmdemux, stream); ++ return FALSE; ++ } ++ data += str_len; ++ length -= str_len; + + element_nb--; + } +@@ -1894,14 +1999,20 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, int length) + } + + gst_rmdemux_add_stream (rmdemux, stream); ++ ++ return TRUE; + } + +-static guint +-gst_rmdemux_parse_indx (GstRMDemux * rmdemux, const guint8 * data, int length) ++static gboolean ++gst_rmdemux_parse_indx (GstRMDemux * rmdemux, const guint8 * data, int length, ++ gsize * index_length) + { + int n; + int id; + ++ if (length < 10) ++ return FALSE; ++ + n = RMDEMUX_GUINT32_GET (data); + id = RMDEMUX_GUINT16_GET (data + 4); + rmdemux->index_offset = RMDEMUX_GUINT32_GET (data + 6); +@@ -1913,10 +2024,13 @@ gst_rmdemux_parse_indx (GstRMDemux * rmdemux, const guint8 * data, int length) + rmdemux->index_stream = gst_rmdemux_get_stream_by_id (rmdemux, id); + + /* Return the length of the index */ +- return 14 * n; ++ if (!g_size_checked_mul (index_length, 14, n)) ++ return FALSE; ++ ++ return TRUE; + } + +-static void ++static gboolean + gst_rmdemux_parse_indx_data (GstRMDemux * rmdemux, const guint8 * data, + int length) + { +@@ -1928,16 +2042,16 @@ gst_rmdemux_parse_indx_data (GstRMDemux * rmdemux, const guint8 * data, + n = length / 14; + + if (rmdemux->index_stream == NULL) +- return; ++ return TRUE; + + /* don't parse the index a second time when operating pull-based and + * reaching the end of the file */ + if (rmdemux->index_stream->index_length > 0) { + GST_DEBUG_OBJECT (rmdemux, "Already have an index for this stream"); +- return; ++ return TRUE; + } + +- index = g_malloc (sizeof (GstRMDemuxIndex) * n); ++ index = g_new (GstRMDemuxIndex, n); + rmdemux->index_stream->index = index; + rmdemux->index_stream->index_length = n; + +@@ -1950,19 +2064,26 @@ gst_rmdemux_parse_indx_data (GstRMDemux * rmdemux, const guint8 * data, + index[i].offset); + data += 14; + } ++ ++ return TRUE; + } + +-static void ++static gboolean + gst_rmdemux_parse_data (GstRMDemux * rmdemux, const guint8 * data, int length) + { ++ if (length < 8) ++ return FALSE; ++ + rmdemux->n_chunks = RMDEMUX_GUINT32_GET (data); + rmdemux->data_offset = RMDEMUX_GUINT32_GET (data + 4); + rmdemux->chunk_index = 0; + GST_DEBUG_OBJECT (rmdemux, "Data chunk found with %d packets " + "(next data at 0x%08x)", rmdemux->n_chunks, rmdemux->data_offset); ++ ++ return TRUE; + } + +-static void ++static gboolean + gst_rmdemux_parse_cont (GstRMDemux * rmdemux, const guint8 * data, int length) + { + GstTagList *tags; +@@ -1983,6 +2104,8 @@ gst_rmdemux_parse_cont (GstRMDemux * rmdemux, const guint8 * data, int length) + + gst_tag_list_set_scope (rmdemux->pending_tags, GST_TAG_SCOPE_GLOBAL); + } ++ ++ return TRUE; + } + + static void +diff --git a/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.h b/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.h +index 40e1a0b..81e2f9d 100644 +--- a/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.h ++++ b/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.h +@@ -128,7 +128,7 @@ struct _GstRMDemux { + int chunk_index; + + guint32 object_id; +- guint32 size; ++ gsize size; + guint16 object_version; + + /* container tags for all streams */ diff --git a/gstreamer1-plugins-ugly-free.spec b/gstreamer1-plugins-ugly-free.spec index 8064865..b8cb6b1 100644 --- a/gstreamer1-plugins-ugly-free.spec +++ b/gstreamer1-plugins-ugly-free.spec @@ -6,7 +6,7 @@ Name: gstreamer1-plugins-ugly-free Version: 1.26.7 -Release: 2%{?dist} +Release: 3%{?dist} Summary: GStreamer streaming media framework "ugly" plugins License: LGPL-2.0-or-later AND LGPL-2.1-or-later AND CC0-1.0 @@ -22,6 +22,8 @@ Source0: https://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugi Patch: 0001-asfdemux-Error-out-on-files-with-more-than-32-stream.patch Patch: 0002-rmdemux-Check-if-new-video-fragment-overflows-the-fr.patch Patch: 0003-rmdemux-Avoid-integer-overflow-when-checking-if-enou.patch +# https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/9de204d7a0d7f584cddfc922f3b9fbe4058ae22a +Patch: 0004-rmdemux-Check-that-enough-data-is-available-before-p.patch BuildRequires: meson >= 0.48.0 BuildRequires: gcc @@ -152,6 +154,10 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %endif %changelog +* Wed Jul 29 2026 RHEL Packaging Agent - 1.26.7-3 +- Fix CVE-2026-53703: rmdemux header parsing bounds checks + Resolves: RHEL-184440 + * Tue Mar 31 2026 Wim Taymans - 1.26.7-2 - Add patches for CVE-2026-2920 and CVE-2026-2922 Resolves: RHEL-156045, RHEL-156176