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:
- 9de204d7a0.patch
Resolves: RHEL-184440
This commit was backported by Ymir, a Red Hat Enterprise Linux software maintenance AI agent.
Assisted-by: Ymir
This commit is contained in:
parent
2aae1d71ef
commit
a4e2f84dd8
609
0004-rmdemux-Check-that-enough-data-is-available-before-p.patch
Normal file
609
0004-rmdemux-Check-that-enough-data-is-available-before-p.patch
Normal file
@ -0,0 +1,609 @@
|
||||
From 4a1689252f97ff4dfcd53a076af03b4abe91bf08 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
|
||||
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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/11825>
|
||||
---
|
||||
.../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 */
|
||||
@ -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 <redhat-ymir-agent@redhat.com> - 1.26.7-3
|
||||
- Fix CVE-2026-53703: rmdemux header parsing bounds checks
|
||||
Resolves: RHEL-184440
|
||||
|
||||
* Tue Mar 31 2026 Wim Taymans <wtaymans@redhat.com> - 1.26.7-2
|
||||
- Add patches for CVE-2026-2920 and CVE-2026-2922
|
||||
Resolves: RHEL-156045, RHEL-156176
|
||||
|
||||
Loading…
Reference in New Issue
Block a user