diff --git a/0004-CVE-2026-53704.patch b/0004-CVE-2026-53704.patch new file mode 100644 index 0000000..18dc50a --- /dev/null +++ b/0004-CVE-2026-53704.patch @@ -0,0 +1,1241 @@ +From 71bbc0de8089d56ebc4d5f5bf1e0c7a2c8f7b9b6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= +Date: Thu, 11 Jun 2026 12:40:05 +0300 +Subject: [PATCH 1/7] realmedia: Use gsize for indexes and avoid integer + overflows + +One length variable stays a signed integer together with range checks because +it's used in decreasing loops that can end up with negative numbers in valid +situations. + +Part-of: +--- + gst/realmedia/rmdemux.c | 10 +++++++--- + gst/realmedia/rmutils.c | 36 ++++++++++++++++++++++-------------- + gst/realmedia/rmutils.h | 12 ++++++------ + 3 files changed, 35 insertions(+), 23 deletions(-) + +diff --git a/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c b/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c +index 19beb51..33a2b36 100644 +--- a/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c ++++ b/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c +@@ -1666,7 +1666,7 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, int length) + GstRMDemuxStream *stream; + char *stream1_type_string; + char *stream2_type_string; +- guint str_len = 0; ++ gsize str_len = 0; + int stream_type; + int offset; + guint32 max_bitrate; +@@ -2194,8 +2194,12 @@ gst_rmdemux_descramble_sipr_audio (GstRMDemux * rmdemux, + } + + outbuf = gst_rm_utils_descramble_sipr_buffer (outbuf); +- +- ret = gst_pad_push (stream->pad, outbuf); ++ if (outbuf) { ++ ret = gst_pad_push (stream->pad, outbuf); ++ } else { ++ GST_WARNING_OBJECT (rmdemux, "failed to descramble buffer"); ++ ret = GST_FLOW_OK; ++ } + + gst_rmdemux_stream_clear_cached_subpackets (rmdemux, stream); + +diff --git a/subprojects/gst-plugins-ugly/gst/realmedia/rmutils.c b/subprojects/gst-plugins-ugly/gst/realmedia/rmutils.c +index c9bc098..42f4dbe 100644 +--- a/subprojects/gst-plugins-ugly/gst/realmedia/rmutils.c ++++ b/subprojects/gst-plugins-ugly/gst/realmedia/rmutils.c +@@ -25,10 +25,10 @@ + #include "rmutils.h" + + gchar * +-gst_rm_utils_read_string8 (const guint8 * data, guint datalen, +- guint * p_total_len) ++gst_rm_utils_read_string8 (const guint8 * data, gsize datalen, ++ gsize * p_total_len) + { +- gint length; ++ gsize length; + + if (p_total_len) + *p_total_len = 0; +@@ -47,10 +47,10 @@ gst_rm_utils_read_string8 (const guint8 * data, guint datalen, + } + + gchar * +-gst_rm_utils_read_string16 (const guint8 * data, guint datalen, +- guint * p_total_len) ++gst_rm_utils_read_string16 (const guint8 * data, gsize datalen, ++ gsize * p_total_len) + { +- gint length; ++ gsize length; + + if (p_total_len) + *p_total_len = 0; +@@ -69,7 +69,7 @@ gst_rm_utils_read_string16 (const guint8 * data, guint datalen, + } + + GstTagList * +-gst_rm_utils_read_tags (const guint8 * data, guint datalen, ++gst_rm_utils_read_tags (const guint8 * data, gsize datalen, + GstRmUtilsStringReadFunc read_string_func) + { + const gchar *gst_tags[] = { GST_TAG_TITLE, GST_TAG_ARTIST, +@@ -80,13 +80,13 @@ gst_rm_utils_read_tags (const guint8 * data, guint datalen, + + g_assert (read_string_func != NULL); + +- GST_DEBUG ("File Content : (CONT) len = %d", datalen); ++ GST_DEBUG ("File Content : (CONT) len = %" G_GSIZE_FORMAT, datalen); + + tags = gst_tag_list_new_empty (); + + for (i = 0; i < G_N_ELEMENTS (gst_tags); ++i) { + gchar *str = NULL; +- guint total_length = 0; ++ gsize total_length = 0; + + str = read_string_func (data, datalen, &total_length); + data += total_length; +@@ -146,7 +146,7 @@ gst_rm_utils_descramble_dnet_buffer (GstBuffer * buf) + } + + static void +-gst_rm_utils_swap_nibbles (guint8 * data, gint idx1, gint idx2, gint len) ++gst_rm_utils_swap_nibbles (guint8 * data, gsize idx1, gsize idx2, gint len) + { + guint8 *d1, *d2, tmp1 = 0, tmp2, tmp1n, tmp2n; + +@@ -207,7 +207,7 @@ gst_rm_utils_swap_nibbles (guint8 * data, gint idx1, gint idx2, gint len) + } + } + +-static const gint sipr_swap_index[38][2] = { ++static const gsize sipr_swap_index[38][2] = { + {0, 63}, {1, 22}, {2, 44}, {3, 90}, + {5, 81}, {7, 31}, {8, 86}, {9, 58}, + {10, 36}, {12, 68}, {13, 39}, {14, 73}, +@@ -224,15 +224,23 @@ GstBuffer * + gst_rm_utils_descramble_sipr_buffer (GstBuffer * buf) + { + GstMapInfo map; +- gint n, bs; +- gsize size; ++ gint n; ++ gsize size, bs; + + size = gst_buffer_get_size (buf); + + /* split the packet in 96 blocks of nibbles */ ++ if (size > G_MAXSIZE / 2) { ++ gst_buffer_unref (buf); ++ return NULL; ++ } + bs = size * 2 / 96; + if (bs == 0) + return buf; ++ if (bs > G_MAXINT) { ++ gst_buffer_unref (buf); ++ return NULL; ++ } + + buf = gst_buffer_make_writable (buf); + +@@ -240,7 +248,7 @@ gst_rm_utils_descramble_sipr_buffer (GstBuffer * buf) + + /* we need to perform 38 swaps on the blocks */ + for (n = 0; n < 38; n++) { +- gint idx1, idx2; ++ gsize idx1, idx2; + + /* get the indexes of the blocks of nibbles that need swapping */ + idx1 = bs * sipr_swap_index[n][0]; +diff --git a/subprojects/gst-plugins-ugly/gst/realmedia/rmutils.h b/subprojects/gst-plugins-ugly/gst/realmedia/rmutils.h +index cf8bbb5..760459f 100644 +--- a/subprojects/gst-plugins-ugly/gst/realmedia/rmutils.h ++++ b/subprojects/gst-plugins-ugly/gst/realmedia/rmutils.h +@@ -24,18 +24,18 @@ + + G_BEGIN_DECLS + +-typedef gchar * (*GstRmUtilsStringReadFunc) (const guint8 * data, guint datalen, guint * p_strlen); ++typedef gchar * (*GstRmUtilsStringReadFunc) (const guint8 * data, gsize datalen, gsize * p_strlen); + + gchar *gst_rm_utils_read_string8 (const guint8 * data, +- guint datalen, +- guint * p_totallen); ++ gsize datalen, ++ gsize * p_totallen); + + gchar *gst_rm_utils_read_string16 (const guint8 * data, +- guint datalen, +- guint * p_totallen); ++ gsize datalen, ++ gsize * p_totallen); + + GstTagList *gst_rm_utils_read_tags (const guint8 * data, +- guint datalen, ++ gsize datalen, + GstRmUtilsStringReadFunc func); + + GstBuffer *gst_rm_utils_descramble_dnet_buffer (GstBuffer * buf); + +From 82fa85a12f491dd9d0c9d14fd7a79d7c56e9778c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= +Date: Thu, 11 Jun 2026 13:51:03 +0300 +Subject: [PATCH 2/7] rademux: Make sure enough data is actually available to + parse the packet header + +Unlikely that we don't have 64 bytes more in the adapter but better safe than sorry. + +Also add FIXME about broken but harmless code. + +Part-of: +--- + gst/realmedia/rademux.c | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/subprojects/gst-plugins-ugly/gst/realmedia/rademux.c b/subprojects/gst-plugins-ugly/gst/realmedia/rademux.c +index aface50..08fd64c 100644 +--- a/subprojects/gst-plugins-ugly/gst/realmedia/rademux.c ++++ b/subprojects/gst-plugins-ugly/gst/realmedia/rademux.c +@@ -342,6 +342,30 @@ gst_real_audio_demux_parse_header (GstRealAudioDemux * demux) + return GST_FLOW_OK; + } + ++ /* Check if enough data is available to parse the header */ ++ switch (demux->ra_version) { ++ case 3: ++ /* Nothing is parsed */ ++ break; ++ case 4: ++ if (avail < demux->data_offset - 6 + 63) { ++ GST_DEBUG_OBJECT (demux, "Need %u bytes, but only %u available now", ++ demux->data_offset - 6 + 63, avail); ++ return GST_FLOW_OK; ++ } ++ break; ++ case 5: ++#if 0 ++ /* Unsupported for now */ ++ if (avail < demux->data_offset - 6 + 64) { ++ GST_DEBUG_OBJECT (demux, "Need %u bytes, but only %u available now", ++ demux->data_offset - 6 + 64, avail); ++ return GST_FLOW_OK; ++ } ++#endif ++ break; ++ } ++ + data = gst_adapter_map (demux->adapter, demux->data_offset - 6); + g_assert (data); + +@@ -366,6 +390,9 @@ gst_real_audio_demux_parse_header (GstRealAudioDemux * demux) + demux->sample_width = GST_READ_UINT16_BE (data + 46); + demux->channels = GST_READ_UINT16_BE (data + 48); + demux->fourcc = GST_READ_UINT32_LE (data + 56); ++ // FIXME: This is broken. We need to make sure that the whole packet ++ // is available to be sure we can actually read all tags. This now ++ // randomly cuts off the tags depending on input buffer chunking. + demux->pending_tags = gst_rm_utils_read_tags (data + 63, + demux->data_offset - 63, gst_rm_utils_read_string8); + if (demux->pending_tags) + +From aa63879f9a9a2d07cff8a12974de100ed53bfd9d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= +Date: Thu, 11 Jun 2026 14:17:05 +0300 +Subject: [PATCH 3/7] rmdemux: Catch short reads instead of reading from less + data than expected + +Part-of: +--- + gst/realmedia/rmdemux.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c b/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c +index 33a2b36..ff22048 100644 +--- a/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c ++++ b/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c +@@ -373,6 +373,11 @@ gst_rmdemux_validate_offset (GstRMDemux * rmdemux) + rmdemux->offset); + return FALSE; + } ++ if (gst_buffer_get_size (buffer) < 4) { ++ gst_buffer_unref (buffer); ++ GST_DEBUG_OBJECT (rmdemux, "Short read at offset %d", rmdemux->offset); ++ return FALSE; ++ } + /* TODO: Can we also be seeking to a 'DATA' chunk header? Check this. + * Also, for the case we currently handle, can we check any more? It's pretty + * sucky to not be validating a little more heavily than this... */ + +From aa7ffd7f9fac6ab866c482f98323677c2bd94db9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= +Date: Thu, 11 Jun 2026 14:25:10 +0300 +Subject: [PATCH 4/7] rmdemux: Use gsize for length parameters + +Part-of: +--- + gst/realmedia/rmdemux.c | 33 +++++++++++++++++---------------- + 1 file changed, 17 insertions(+), 16 deletions(-) + +diff --git a/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c b/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c +index ff22048..2ffd0cf 100644 +--- a/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c ++++ b/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c +@@ -151,21 +151,21 @@ static gboolean gst_rmdemux_perform_seek (GstRMDemux * rmdemux, + GstEvent * event); + + static void gst_rmdemux_parse__rmf (GstRMDemux * rmdemux, const guint8 * data, +- int length); ++ gsize length); + static void gst_rmdemux_parse_prop (GstRMDemux * rmdemux, const guint8 * data, +- int length); ++ gsize length); + static void gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, +- const guint8 * data, int length); ++ const guint8 * data, gsize length); + static guint gst_rmdemux_parse_indx (GstRMDemux * rmdemux, const guint8 * data, +- int length); ++ gsize length); + static void gst_rmdemux_parse_data (GstRMDemux * rmdemux, const guint8 * data, +- int length); ++ gsize length); + static void gst_rmdemux_parse_cont (GstRMDemux * rmdemux, const guint8 * data, +- int length); ++ gsize length); + static GstFlowReturn gst_rmdemux_parse_packet (GstRMDemux * rmdemux, + GstBuffer * in, guint16 version); + static void gst_rmdemux_parse_indx_data (GstRMDemux * rmdemux, +- const guint8 * data, int length); ++ const guint8 * data, gsize length); + static void gst_rmdemux_stream_clear_cached_subpackets (GstRMDemux * rmdemux, + GstRMDemuxStream * stream); + static GstRMDemuxStream *gst_rmdemux_get_stream_by_id (GstRMDemux * rmdemux, +@@ -1633,14 +1633,14 @@ re_skip_pascal_string (const guint8 * ptr) + } + + static void +-gst_rmdemux_parse__rmf (GstRMDemux * rmdemux, const guint8 * data, int length) ++gst_rmdemux_parse__rmf (GstRMDemux * rmdemux, const guint8 * data, gsize length) + { + GST_LOG_OBJECT (rmdemux, "file_version: %d", RMDEMUX_GUINT32_GET (data)); + GST_LOG_OBJECT (rmdemux, "num_headers: %d", RMDEMUX_GUINT32_GET (data + 4)); + } + + static void +-gst_rmdemux_parse_prop (GstRMDemux * rmdemux, const guint8 * data, int length) ++gst_rmdemux_parse_prop (GstRMDemux * rmdemux, const guint8 * data, gsize length) + { + GST_LOG_OBJECT (rmdemux, "max bitrate: %d", RMDEMUX_GUINT32_GET (data)); + GST_LOG_OBJECT (rmdemux, "avg bitrate: %d", RMDEMUX_GUINT32_GET (data + 4)); +@@ -1666,7 +1666,7 @@ gst_rmdemux_parse_prop (GstRMDemux * rmdemux, const guint8 * data, int length) + } + + static void +-gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, int length) ++gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, gsize length) + { + GstRMDemuxStream *stream; + char *stream1_type_string; +@@ -1902,7 +1902,7 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, int length) + } + + static guint +-gst_rmdemux_parse_indx (GstRMDemux * rmdemux, const guint8 * data, int length) ++gst_rmdemux_parse_indx (GstRMDemux * rmdemux, const guint8 * data, gsize length) + { + int n; + int id; +@@ -1911,8 +1911,9 @@ gst_rmdemux_parse_indx (GstRMDemux * rmdemux, const guint8 * data, int length) + id = RMDEMUX_GUINT16_GET (data + 4); + rmdemux->index_offset = RMDEMUX_GUINT32_GET (data + 6); + +- GST_DEBUG_OBJECT (rmdemux, "Number of indices=%d Stream ID=%d length=%d", n, +- id, length); ++ GST_DEBUG_OBJECT (rmdemux, ++ "Number of indices=%d Stream ID=%d length=%" G_GSIZE_FORMAT, n, id, ++ length); + + /* Point to the next index_stream */ + rmdemux->index_stream = gst_rmdemux_get_stream_by_id (rmdemux, id); +@@ -1923,7 +1924,7 @@ gst_rmdemux_parse_indx (GstRMDemux * rmdemux, const guint8 * data, int length) + + static void + gst_rmdemux_parse_indx_data (GstRMDemux * rmdemux, const guint8 * data, +- int length) ++ gsize length) + { + int i; + int n; +@@ -1958,7 +1959,7 @@ gst_rmdemux_parse_indx_data (GstRMDemux * rmdemux, const guint8 * data, + } + + static void +-gst_rmdemux_parse_data (GstRMDemux * rmdemux, const guint8 * data, int length) ++gst_rmdemux_parse_data (GstRMDemux * rmdemux, const guint8 * data, gsize length) + { + rmdemux->n_chunks = RMDEMUX_GUINT32_GET (data); + rmdemux->data_offset = RMDEMUX_GUINT32_GET (data + 4); +@@ -1968,7 +1969,7 @@ gst_rmdemux_parse_data (GstRMDemux * rmdemux, const guint8 * data, int length) + } + + static void +-gst_rmdemux_parse_cont (GstRMDemux * rmdemux, const guint8 * data, int length) ++gst_rmdemux_parse_cont (GstRMDemux * rmdemux, const guint8 * data, gsize length) + { + GstTagList *tags; + + +From 0a941061a192bc4f2e5502c28fac0adb3013c540 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 5/7] 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/realmedia/rmdemux.c | 301 ++++++++++++++++++++++++++++------------ + 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 2ffd0cf..e0ec64f 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, +- gsize length); +-static void gst_rmdemux_parse_prop (GstRMDemux * rmdemux, const guint8 * data, +- gsize length); +-static void gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, ++static gboolean gst_rmdemux_parse__rmf (GstRMDemux * rmdemux, ++ const guint8 * data, gsize length); ++static gboolean gst_rmdemux_parse_prop (GstRMDemux * rmdemux, ++ const guint8 * data, gsize length); ++static gboolean gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, ++ const guint8 * data, gsize length); ++static gboolean gst_rmdemux_parse_indx (GstRMDemux * rmdemux, ++ const guint8 * data, gsize length, gsize * index_length); ++static gboolean gst_rmdemux_parse_data (GstRMDemux * rmdemux, ++ const guint8 * data, gsize length); ++static gboolean gst_rmdemux_parse_cont (GstRMDemux * rmdemux, + const guint8 * data, gsize length); +-static guint gst_rmdemux_parse_indx (GstRMDemux * rmdemux, const guint8 * data, +- gsize length); +-static void gst_rmdemux_parse_data (GstRMDemux * rmdemux, const guint8 * data, +- gsize length); +-static void gst_rmdemux_parse_cont (GstRMDemux * rmdemux, const guint8 * data, +- gsize 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, gsize length); + static void gst_rmdemux_stream_clear_cached_subpackets (GstRMDemux * rmdemux, + GstRMDemuxStream * stream); +@@ -1065,13 +1065,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 */ +@@ -1090,7 +1099,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); + +@@ -1141,7 +1150,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 { +@@ -1156,7 +1166,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); + +@@ -1169,7 +1180,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); + +@@ -1182,7 +1194,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); + +@@ -1191,6 +1204,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"); +@@ -1199,6 +1214,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)); +@@ -1206,7 +1222,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); + +@@ -1219,7 +1242,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); +@@ -1235,7 +1266,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); + } +@@ -1622,26 +1654,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, gsize 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, gsize 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", +@@ -1663,9 +1703,11 @@ gst_rmdemux_parse_prop (GstRMDemux * rmdemux, const guint8 * data, gsize 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, gsize length) + { + GstRMDemuxStream *stream; +@@ -1673,10 +1715,12 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, gsize length) + char *stream2_type_string; + gsize 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); +@@ -1707,13 +1751,18 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, gsize 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 : +@@ -1723,6 +1772,13 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, gsize 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); + +@@ -1753,27 +1809,36 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, gsize 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; +@@ -1792,7 +1857,11 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, gsize 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: +@@ -1806,40 +1875,48 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, gsize 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; +@@ -1862,32 +1939,60 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, gsize 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--; + } +@@ -1899,14 +2004,20 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, gsize length) + } + + gst_rmdemux_add_stream (rmdemux, stream); ++ ++ return TRUE; + } + +-static guint +-gst_rmdemux_parse_indx (GstRMDemux * rmdemux, const guint8 * data, gsize length) ++static gboolean ++gst_rmdemux_parse_indx (GstRMDemux * rmdemux, const guint8 * data, gsize 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); +@@ -1919,10 +2030,13 @@ gst_rmdemux_parse_indx (GstRMDemux * rmdemux, const guint8 * data, gsize 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, + gsize length) + { +@@ -1934,16 +2048,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; + +@@ -1956,19 +2070,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, gsize 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, gsize length) + { + GstTagList *tags; +@@ -1989,6 +2110,8 @@ gst_rmdemux_parse_cont (GstRMDemux * rmdemux, const guint8 * data, gsize 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 */ + +From eb766874e22ad107612ae856fd3bcf6e4bcc793f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= +Date: Thu, 11 Jun 2026 16:23:34 +0300 +Subject: [PATCH 6/7] rmdemux: Check for enough data when descrambling audio + packets + +And discard broken packets instead of erroring out. + +Part-of: +--- + gst/realmedia/rmdemux.c | 54 +++++++++++++++++++++++++++++++++++------ + 1 file changed, 46 insertions(+), 8 deletions(-) + +diff --git a/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c b/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c +index e0ec64f..c99ace9 100644 +--- a/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c ++++ b/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c +@@ -2145,8 +2145,15 @@ gst_rmdemux_descramble_audio (GstRMDemux * rmdemux, GstRMDemuxStream * stream) + packet_size, leaf_size, height); + + if (!g_uint_checked_mul (&size, height, packet_size)) { +- GST_ERROR_OBJECT (rmdemux, "overflowing audio packet size"); +- return GST_FLOW_ERROR; ++ GST_WARNING_OBJECT (rmdemux, "overflowing audio packet size"); ++ gst_rmdemux_stream_clear_cached_subpackets (rmdemux, stream); ++ return GST_FLOW_OK; ++ } ++ ++ if (stream->leaf_size == 0) { ++ GST_WARNING_OBJECT (rmdemux, "invalid audio leaf packet size"); ++ gst_rmdemux_stream_clear_cached_subpackets (rmdemux, stream); ++ return GST_FLOW_OK; + } + + outbuf = gst_buffer_new_and_alloc (size); +@@ -2156,6 +2163,14 @@ gst_rmdemux_descramble_audio (GstRMDemux * rmdemux, GstRMDemuxStream * stream) + GstBuffer *b = g_ptr_array_index (stream->subpackets, p); + GstMapInfo map; + ++ if (gst_buffer_get_size (b) < packet_size) { ++ GST_ERROR_OBJECT (rmdemux, "too small audio subpacket"); ++ gst_buffer_unmap (outbuf, &outmap); ++ gst_buffer_unref (outbuf); ++ gst_rmdemux_stream_clear_cached_subpackets (rmdemux, stream); ++ return GST_FLOW_OK; ++ } ++ + gst_buffer_map (b, &map, GST_MAP_READ); + + if (p == 0) { +@@ -2243,12 +2258,32 @@ gst_rmdemux_descramble_mp4a_audio (GstRMDemux * rmdemux, + gst_buffer_map (buf, &map, GST_MAP_READ); + timestamp = GST_BUFFER_PTS (buf); + ++ if (map.size < 2) { ++ GST_WARNING_OBJECT (rmdemux, "Too small mp4a buffer"); ++ gst_buffer_unmap (buf, &map); ++ gst_buffer_unref (buf); ++ return GST_FLOW_OK; ++ } + frames = (map.data[1] & 0xf0) >> 4; + index = 2 * frames + 2; + ++ if (map.size < 2 * frames + 2) { ++ GST_WARNING_OBJECT (rmdemux, "Too small mp4a buffer"); ++ gst_buffer_unmap (buf, &map); ++ gst_buffer_unref (buf); ++ return GST_FLOW_OK; ++ } ++ + for (i = 0; i < frames; i++) { + guint len = (map.data[i * 2 + 2] << 8) | map.data[i * 2 + 3]; + ++ if (map.size < index + len) { ++ GST_WARNING_OBJECT (rmdemux, "Too small mp4a buffer"); ++ gst_buffer_unmap (buf, &map); ++ gst_buffer_unref (buf); ++ return GST_FLOW_OK; ++ } ++ + outbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, index, len); + if (i == 0) { + GST_BUFFER_PTS (outbuf) = timestamp; +@@ -2288,8 +2323,9 @@ gst_rmdemux_descramble_sipr_audio (GstRMDemux * rmdemux, + packet_size, stream->leaf_size, height); + + if (!g_uint_checked_mul (&size, height, packet_size)) { +- GST_ERROR_OBJECT (rmdemux, "overflowing SIPR audio packet size"); +- return GST_FLOW_ERROR; ++ GST_WARNING_OBJECT (rmdemux, "overflowing SIPR audio packet size"); ++ gst_rmdemux_stream_clear_cached_subpackets (rmdemux, stream); ++ return GST_FLOW_OK; + } + + outbuf = gst_buffer_new_and_alloc (size); +@@ -2303,13 +2339,15 @@ gst_rmdemux_descramble_sipr_audio (GstRMDemux * rmdemux, + GST_BUFFER_PTS (outbuf) = GST_BUFFER_PTS (b); + } + +- if (gst_buffer_extract (b, 0, outmap.data + packet_size * p, +- packet_size) != packet_size) { +- GST_ERROR_OBJECT (rmdemux, "not enough SIPR audio data available"); ++ if (gst_buffer_get_size (b) < packet_size) { ++ GST_ERROR_OBJECT (rmdemux, "too small sipr audio subpacket"); + gst_buffer_unmap (outbuf, &outmap); + gst_buffer_unref (outbuf); +- return GST_FLOW_ERROR; ++ gst_rmdemux_stream_clear_cached_subpackets (rmdemux, stream); ++ return GST_FLOW_OK; + } ++ ++ gst_buffer_extract (b, 0, outmap.data + packet_size * p, packet_size); + } + gst_buffer_unmap (outbuf, &outmap); + + +From 713c54dfa3d422d847718a24c7ff92944a94d8b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= +Date: Thu, 11 Jun 2026 15:38:36 +0300 +Subject: [PATCH 7/7] rmdemux: Keep of copy of the stream's extra_data + +The pointer will become invalid after unmapping the adapter. While it is only +accessed before unmapping, let's avoid future mistakes. + +Part-of: +--- + gst/realmedia/rmdemux.c | 41 +++++++++++++++++++++++++---------------- + 1 file changed, 25 insertions(+), 16 deletions(-) + +diff --git a/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c b/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c +index c99ace9..ef53f13 100644 +--- a/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c ++++ b/subprojects/gst-plugins-ugly/gst/realmedia/rmdemux.c +@@ -74,8 +74,9 @@ struct _GstRMDemuxStream + guint16 leaf_size; /* subpacket_size */ + guint32 packet_size; /* coded_frame_size */ + guint16 version; +- guint32 extra_data_size; /* codec_data_length */ ++ gsize extra_data_size; /* codec_data_length */ + guint8 *extra_data; /* extras */ ++ gsize extra_data_offset; /* offset into extra_data */ + guint32 bitrate; + + gboolean needs_descrambling; +@@ -732,6 +733,7 @@ gst_rmdemux_free_stream (GstRMDemux * rmdemux, GstRMDemuxStream * stream) + if (stream->subpackets) + g_ptr_array_free (stream->subpackets, TRUE); + g_free (stream->index); ++ g_free (stream->extra_data); + g_free (stream); + } + +@@ -1487,8 +1489,7 @@ gst_rmdemux_add_stream (GstRMDemux * rmdemux, GstRMDemuxStream * stream) + (int) 4, "framed", G_TYPE_BOOLEAN, TRUE, NULL); + if (stream->extra_data_size > 0) { + /* strip off an unknown byte in the extra data */ +- stream->extra_data_size--; +- stream->extra_data++; ++ stream->extra_data_offset++; + } + stream->needs_descrambling = TRUE; + stream->subpackets_needed = 1; +@@ -1581,15 +1582,21 @@ gst_rmdemux_add_stream (GstRMDemux * rmdemux, GstRMDemuxStream * stream) + if (stream->pad && stream_caps) { + GstEvent *event; + +- GST_LOG_OBJECT (rmdemux, "%d bytes of extra data for stream %s", +- stream->extra_data_size, GST_PAD_NAME (stream->pad)); ++ GST_LOG_OBJECT (rmdemux, ++ "%" G_GSIZE_FORMAT " bytes of extra data for stream %s", ++ stream->extra_data_size - stream->extra_data_offset, ++ GST_PAD_NAME (stream->pad)); + + /* add codec_data if there is any */ +- if (stream->extra_data_size > 0) { ++ if (stream->extra_data_size - stream->extra_data_offset > 0) { + GstBuffer *buffer; + +- buffer = gst_buffer_new_and_alloc (stream->extra_data_size); +- gst_buffer_fill (buffer, 0, stream->extra_data, stream->extra_data_size); ++ buffer = ++ gst_buffer_new_and_alloc (stream->extra_data_size - ++ stream->extra_data_offset); ++ gst_buffer_fill (buffer, 0, ++ stream->extra_data + stream->extra_data_offset, ++ stream->extra_data_size - stream->extra_data_offset); + + gst_caps_set_simple (stream_caps, "codec_data", GST_TYPE_BUFFER, + buffer, NULL); +@@ -1832,7 +1839,7 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, gsize length) + 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; ++ stream->extra_data = g_memdup2 (data + 26, length - 26); + /* Natural way to represent framerates here requires unsigned 32 bit + * numerator, which we don't have. For the nasty case, approximate... + */ +@@ -1850,9 +1857,9 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, gsize length) + + GST_DEBUG_OBJECT (rmdemux, + "Video stream with fourcc=%" GST_FOURCC_FORMAT +- " width=%d height=%d rate=%d framerate=%d/%d subformat=%x format=%x extra_data_size=%d", +- GST_FOURCC_ARGS (stream->fourcc), stream->width, stream->height, +- stream->rate, stream->framerate_numerator, ++ " width=%d height=%d rate=%d framerate=%d/%d subformat=%x format=%x extra_data_size=%" ++ G_GSIZE_FORMAT, GST_FOURCC_ARGS (stream->fourcc), stream->width, ++ stream->height, stream->rate, stream->framerate_numerator, + stream->framerate_denominator, stream->subformat, stream->format, + stream->extra_data_size); + break; +@@ -1889,10 +1896,11 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, gsize length) + 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", ++ GST_DEBUG_OBJECT (rmdemux, ++ "%" G_GSIZE_FORMAT " bytes of extra codec data", + stream->extra_data_size); + if (length - 73 >= stream->extra_data_size) { +- stream->extra_data = (guint8 *) data + 73; ++ stream->extra_data = g_memdup2 (data + 73, stream->extra_data_size); + } else { + GST_WARNING_OBJECT (rmdemux, "codec data runs beyond MDPR chunk"); + stream->extra_data_size = 0; +@@ -1913,10 +1921,11 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, gsize length) + 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", ++ GST_DEBUG_OBJECT (rmdemux, ++ "%" G_GSIZE_FORMAT " bytes of extra codec data", + stream->extra_data_size); + if (length - 78 >= stream->extra_data_size) { +- stream->extra_data = (guint8 *) data + 78; ++ stream->extra_data = g_memdup2 (data + 78, stream->extra_data_size); + } else { + GST_WARNING_OBJECT (rmdemux, "codec data runs beyond MDPR chunk"); + stream->extra_data_size = 0; diff --git a/gstreamer1-plugins-ugly-free.spec b/gstreamer1-plugins-ugly-free.spec index 20539ce..a729c75 100644 --- a/gstreamer1-plugins-ugly-free.spec +++ b/gstreamer1-plugins-ugly-free.spec @@ -13,7 +13,7 @@ Name: gstreamer1-plugins-ugly-free Version: 1.22.12 -Release: 6%{?dist} +Release: 7%{?dist} Summary: GStreamer streaming media framework "ugly" plugins License: LGPL-2.0-or-later AND LGPL-2.1-or-later AND CC0-1.0 @@ -29,6 +29,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/-/merge_requests/11832 +Patch: 0004-CVE-2026-53704.patch BuildRequires: meson >= 0.48.0 BuildRequires: gcc @@ -168,6 +170,9 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %endif %changelog +* Wed Jul 29 2026 RHEL Packaging Agent - 1.22.12-7 +- Fix OOB reads in the realmedia demuxer (CVE-2026-53703, CVE-2026-53704) + * Tue Mar 31 2026 Veronika Kabatova - 1.22.12-6 - Rebuild to fix missing binaries due to buildsystem oversight