diff --git a/0001-dvbsuboverlay-Mark-parsed-byte-array-as-const.patch b/0001-dvbsuboverlay-Mark-parsed-byte-array-as-const.patch new file mode 100644 index 0000000..24727bc --- /dev/null +++ b/0001-dvbsuboverlay-Mark-parsed-byte-array-as-const.patch @@ -0,0 +1,94 @@ +From e0eb8a3227b7cba80939aaeb953e5d1229f695a4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= +Date: Wed, 25 Feb 2026 17:22:52 +0200 +Subject: [PATCH 1/4] dvbsuboverlay: Mark parsed byte array as const + +Part-of: +--- + .../gst/dvbsuboverlay/dvb-sub.c | 21 ++++++++++--------- + .../gst/dvbsuboverlay/dvb-sub.h | 2 +- + 2 files changed, 12 insertions(+), 11 deletions(-) + +diff --git a/subprojects/gst-plugins-bad/gst/dvbsuboverlay/dvb-sub.c b/subprojects/gst-plugins-bad/gst/dvbsuboverlay/dvb-sub.c +index c3ed520513..d49828929c 100644 +--- a/subprojects/gst-plugins-bad/gst/dvbsuboverlay/dvb-sub.c ++++ b/subprojects/gst-plugins-bad/gst/dvbsuboverlay/dvb-sub.c +@@ -373,8 +373,8 @@ dvb_sub_init (void) + } + + static void +-_dvb_sub_parse_page_segment (DvbSub * dvb_sub, guint16 page_id, guint8 * buf, +- gint buf_size) ++_dvb_sub_parse_page_segment (DvbSub * dvb_sub, guint16 page_id, ++ const guint8 * buf, gint buf_size) + { /* FIXME: Use guint for buf_size here and in many other places? */ + DVBSubRegionDisplay *display; + DVBSubRegionDisplay *tmp_display_list, **tmp_ptr; +@@ -451,8 +451,8 @@ _dvb_sub_parse_page_segment (DvbSub * dvb_sub, guint16 page_id, guint8 * buf, + } + + static void +-_dvb_sub_parse_region_segment (DvbSub * dvb_sub, guint16 page_id, guint8 * buf, +- gint buf_size) ++_dvb_sub_parse_region_segment (DvbSub * dvb_sub, guint16 page_id, ++ const guint8 * buf, gint buf_size) + { + const guint8 *buf_end = buf + buf_size; + guint8 region_id; +@@ -574,8 +574,8 @@ _dvb_sub_parse_region_segment (DvbSub * dvb_sub, guint16 page_id, guint8 * buf, + } + + static void +-_dvb_sub_parse_clut_segment (DvbSub * dvb_sub, guint16 page_id, guint8 * buf, +- gint buf_size) ++_dvb_sub_parse_clut_segment (DvbSub * dvb_sub, guint16 page_id, ++ const guint8 * buf, gint buf_size) + { + const guint8 *buf_end = buf + buf_size; + guint8 clut_id; +@@ -1076,8 +1076,8 @@ _dvb_sub_parse_pixel_data_block (DvbSub * dvb_sub, + } + + static void +-_dvb_sub_parse_object_segment (DvbSub * dvb_sub, guint16 page_id, guint8 * buf, +- gint buf_size) ++_dvb_sub_parse_object_segment (DvbSub * dvb_sub, guint16 page_id, ++ const guint8 * buf, gint buf_size) + { + const guint8 *buf_end = buf + buf_size; + guint object_id; +@@ -1147,7 +1147,7 @@ _dvb_sub_parse_object_segment (DvbSub * dvb_sub, guint16 page_id, guint8 * buf, + } + + static gint +-_dvb_sub_parse_display_definition_segment (DvbSub * dvb_sub, guint8 * buf, ++_dvb_sub_parse_display_definition_segment (DvbSub * dvb_sub, const guint8 * buf, + gint buf_size) + { + int dds_version, info_byte; +@@ -1386,7 +1386,8 @@ dvb_sub_free (DvbSub * sub) + * 0 or positive if data was handled. If positive, then amount of data consumed on success. FIXME: List the positive return values. + */ + gint +-dvb_sub_feed_with_pts (DvbSub * dvb_sub, guint64 pts, guint8 * data, gint len) ++dvb_sub_feed_with_pts (DvbSub * dvb_sub, guint64 pts, const guint8 * data, ++ gint len) + { + unsigned int pos = 0; + guint8 segment_type; +diff --git a/subprojects/gst-plugins-bad/gst/dvbsuboverlay/dvb-sub.h b/subprojects/gst-plugins-bad/gst/dvbsuboverlay/dvb-sub.h +index c986422a6a..0f78a171b1 100644 +--- a/subprojects/gst-plugins-bad/gst/dvbsuboverlay/dvb-sub.h ++++ b/subprojects/gst-plugins-bad/gst/dvbsuboverlay/dvb-sub.h +@@ -129,7 +129,7 @@ typedef struct { + DvbSub *dvb_sub_new (void); + void dvb_sub_free (DvbSub * sub); + +-gint dvb_sub_feed_with_pts (DvbSub *dvb_sub, guint64 pts, guint8 *data, gint len); ++gint dvb_sub_feed_with_pts (DvbSub *dvb_sub, guint64 pts, const guint8 *data, gint len); + void dvb_sub_set_callbacks (DvbSub *dvb_sub, DvbSubCallbacks *callbacks, gpointer user_data); + void dvb_subtitles_free (DVBSubtitles *sub); + +-- +2.53.0 + diff --git a/0002-dvbsuboverlay-Add-missing-bounds-checks-to-the-parse.patch b/0002-dvbsuboverlay-Add-missing-bounds-checks-to-the-parse.patch new file mode 100644 index 0000000..b67e98a --- /dev/null +++ b/0002-dvbsuboverlay-Add-missing-bounds-checks-to-the-parse.patch @@ -0,0 +1,442 @@ +From 63fa69ec181f5defeb1193458b9322206ced4b85 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= +Date: Wed, 11 Feb 2026 20:45:12 +0200 +Subject: [PATCH 2/4] dvbsuboverlay: Add missing bounds checks to the parser + everywhere + +Fixes SA-2026-0007, ZDI-CAN-28838, CVE-2026-2923. + +Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/4897 + +Part-of: +--- + .../gst/dvbsuboverlay/dvb-sub.c | 186 ++++++++++-------- + 1 file changed, 109 insertions(+), 77 deletions(-) + +diff --git a/subprojects/gst-plugins-bad/gst/dvbsuboverlay/dvb-sub.c b/subprojects/gst-plugins-bad/gst/dvbsuboverlay/dvb-sub.c +index d49828929c..9d7704b86d 100644 +--- a/subprojects/gst-plugins-bad/gst/dvbsuboverlay/dvb-sub.c ++++ b/subprojects/gst-plugins-bad/gst/dvbsuboverlay/dvb-sub.c +@@ -585,6 +585,9 @@ _dvb_sub_parse_clut_segment (DvbSub * dvb_sub, guint16 page_id, + + GST_MEMDUMP ("DVB clut packet", buf, buf_size); + ++ if (buf_size < 1) ++ return; ++ + clut_id = *buf++; + buf += 1; + +@@ -601,7 +604,7 @@ _dvb_sub_parse_clut_segment (DvbSub * dvb_sub, guint16 page_id, + dvb_sub->clut_list = clut; + } + +- while (buf + 4 < buf_end) { ++ while (buf + 2 < buf_end) { + entry_id = *buf++; + + depth = (*buf) & 0xe0; +@@ -614,11 +617,15 @@ _dvb_sub_parse_clut_segment (DvbSub * dvb_sub, guint16 page_id, + full_range = (*buf++) & 1; + + if (full_range) { ++ if (buf + 4 > buf_end) ++ break; + y = *buf++; + cr = *buf++; + cb = *buf++; + alpha = *buf++; + } else { ++ if (buf + 2 > buf_end) ++ break; + y = buf[0] & 0xfc; + cr = (((buf[0] & 3) << 2) | ((buf[1] >> 6) & 3)) << 4; + cb = (buf[1] << 2) & 0xf0; +@@ -633,11 +640,11 @@ _dvb_sub_parse_clut_segment (DvbSub * dvb_sub, guint16 page_id, + GST_DEBUG ("CLUT DEFINITION: clut %d := (%d,%d,%d,%d)", entry_id, y, cb, cr, + alpha); + +- if (depth & 0x80) ++ if ((depth & 0x80) && entry_id < 4) + clut->clut4[entry_id] = AYUV (y, cb, cr, 255 - alpha); +- if (depth & 0x40) ++ if ((depth & 0x40) && entry_id < 16) + clut->clut16[entry_id] = AYUV (y, cb, cr, 255 - alpha); +- if (depth & 0x20) ++ if ((depth & 0x20) && entry_id < 256) + clut->clut256[entry_id] = AYUV (y, cb, cr, 255 - alpha); + } + } +@@ -649,8 +656,6 @@ _dvb_sub_read_2bit_string (guint8 * destbuf, gint dbuf_len, + const guint8 ** srcbuf, gint buf_size, guint8 non_mod, guint8 * map_table) + { + GstBitReader gb = GST_BIT_READER_INIT (*srcbuf, buf_size); +- /* FIXME: Handle FALSE returns from gst_bit_reader_get_* calls? */ +- + gboolean stop_parsing = FALSE; + guint32 bits = 0; + guint32 pixels_read = 0; +@@ -661,23 +666,29 @@ _dvb_sub_read_2bit_string (guint8 * destbuf, gint dbuf_len, + while (!stop_parsing && (gst_bit_reader_get_remaining (&gb) > 1)) { + guint run_length = 0, clut_index = 0; + +- bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 2); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &bits, 2)) ++ goto not_enough_data; + + if (bits) { /* 2-bit_pixel-code */ + run_length = 1; + clut_index = bits; + } else { /* 2-bit_zero */ +- bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 1); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &bits, 1)) ++ goto not_enough_data; + if (bits == 1) { /* switch_1 == '1' */ +- run_length = gst_bit_reader_get_bits_uint32_unchecked (&gb, 3); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &run_length, 3)) ++ goto not_enough_data; + run_length += 3; +- clut_index = gst_bit_reader_get_bits_uint32_unchecked (&gb, 2); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 2)) ++ goto not_enough_data; + } else { /* switch_1 == '0' */ +- bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 1); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &bits, 1)) ++ goto not_enough_data; + if (bits == 1) { /* switch_2 == '1' */ + run_length = 1; /* 1x pseudo-colour '00' */ + } else { /* switch_2 == '0' */ +- bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 2); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &bits, 2)) ++ goto not_enough_data; + switch (bits) { /* switch_3 */ + case 0x0: /* end of 2-bit/pixel_code_string */ + stop_parsing = TRUE; +@@ -686,14 +697,18 @@ _dvb_sub_read_2bit_string (guint8 * destbuf, gint dbuf_len, + run_length = 2; + break; + case 0x2: /* the following 6 bits contain run length coded pixel data */ +- run_length = gst_bit_reader_get_bits_uint32_unchecked (&gb, 4); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &run_length, 4)) ++ goto not_enough_data; + run_length += 12; +- clut_index = gst_bit_reader_get_bits_uint32_unchecked (&gb, 2); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 2)) ++ goto not_enough_data; + break; + case 0x3: /* the following 10 bits contain run length coded pixel data */ +- run_length = gst_bit_reader_get_bits_uint32_unchecked (&gb, 8); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &run_length, 8)) ++ goto not_enough_data; + run_length += 29; +- clut_index = gst_bit_reader_get_bits_uint32_unchecked (&gb, 2); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 2)) ++ goto not_enough_data; + break; + } + } +@@ -727,13 +742,18 @@ _dvb_sub_read_2bit_string (guint8 * destbuf, gint dbuf_len, + pixels_read += run_length; + } + +- // FIXME: Test skip_to_byte instead of adding 7 bits, once everything else is working good +- //gst_bit_reader_skip_to_byte (&gb); +- *srcbuf += (gst_bit_reader_get_pos (&gb) + 7) >> 3; ++ gst_bit_reader_skip_to_byte (&gb); ++ *srcbuf = gb.data + gb.byte; + + GST_TRACE ("PIXEL: returning, read %u pixels", pixels_read); + // FIXME: Shouldn't need this variable if tracking things in the loop better + return pixels_read; ++ ++not_enough_data: ++ GST_WARNING ("Not enough data"); ++ // Go to the end of the buffer so the caller stops parsing ++ *srcbuf += buf_size; ++ return 0; + } + + // FFMPEG-FIXME: The same code in ffmpeg is much more complex, it could use the same +@@ -743,7 +763,6 @@ _dvb_sub_read_4bit_string (guint8 * destbuf, gint dbuf_len, + const guint8 ** srcbuf, gint buf_size, guint8 non_mod, guint8 * map_table) + { + GstBitReader gb = GST_BIT_READER_INIT (*srcbuf, buf_size); +- /* FIXME: Handle FALSE returns from gst_bit_reader_get_* calls? */ + gboolean stop_parsing = FALSE; + guint32 bits = 0; + guint32 pixels_read = 0; +@@ -755,28 +774,35 @@ _dvb_sub_read_4bit_string (guint8 * destbuf, gint dbuf_len, + while (!stop_parsing && (gst_bit_reader_get_remaining (&gb) > 3)) { + guint run_length = 0, clut_index = 0; + +- bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 4); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &bits, 4)) ++ goto not_enough_data; + + if (bits) { + run_length = 1; + clut_index = bits; + } else { +- bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 1); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &bits, 1)) ++ goto not_enough_data; + if (bits == 0) { /* switch_1 == '0' */ +- run_length = gst_bit_reader_get_bits_uint32_unchecked (&gb, 3); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &run_length, 3)) ++ goto not_enough_data; + if (!run_length) { + stop_parsing = TRUE; + } else { + run_length += 2; + } + } else { /* switch_1 == '1' */ +- bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 1); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &bits, 1)) ++ goto not_enough_data; + if (bits == 0) { /* switch_2 == '0' */ +- run_length = gst_bit_reader_get_bits_uint32_unchecked (&gb, 2); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &run_length, 2)) ++ goto not_enough_data; + run_length += 4; +- clut_index = gst_bit_reader_get_bits_uint32_unchecked (&gb, 4); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 4)) ++ goto not_enough_data; + } else { /* switch_2 == '1' */ +- bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 2); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &bits, 2)) ++ goto not_enough_data; + switch (bits) { + case 0x0: /* switch_3 == '00' */ + run_length = 1; /* 1 pixel of pseudo-color 0 */ +@@ -785,14 +811,18 @@ _dvb_sub_read_4bit_string (guint8 * destbuf, gint dbuf_len, + run_length = 2; /* 2 pixels of pseudo-color 0 */ + break; + case 0x2: /* switch_3 == '10' */ +- run_length = gst_bit_reader_get_bits_uint32_unchecked (&gb, 4); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &run_length, 4)) ++ goto not_enough_data; + run_length += 9; +- clut_index = gst_bit_reader_get_bits_uint32_unchecked (&gb, 4); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 4)) ++ goto not_enough_data; + break; + case 0x3: /* switch_3 == '11' */ +- run_length = gst_bit_reader_get_bits_uint32_unchecked (&gb, 8); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &run_length, 8)) ++ goto not_enough_data; + run_length += 25; +- clut_index = gst_bit_reader_get_bits_uint32_unchecked (&gb, 4); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 4)) ++ goto not_enough_data; + break; + } + } +@@ -826,14 +856,19 @@ _dvb_sub_read_4bit_string (guint8 * destbuf, gint dbuf_len, + pixels_read += run_length; + } + +- // FIXME: Test skip_to_byte instead of adding 7 bits, once everything else is working good +- //gst_bit_reader_skip_to_byte (&gb); +- *srcbuf += (gst_bit_reader_get_pos (&gb) + 7) >> 3; ++ gst_bit_reader_skip_to_byte (&gb); ++ *srcbuf = gb.data + gb.byte; + + GST_LOG ("Returning with %u pixels read", pixels_read); + + // FIXME: Shouldn't need this variable if tracking things in the loop better + return pixels_read; ++ ++not_enough_data: ++ GST_WARNING ("Not enough data"); ++ // Go to the end of the buffer so the caller stops parsing ++ *srcbuf += buf_size; ++ return 0; + } + + static int +@@ -841,8 +876,6 @@ _dvb_sub_read_8bit_string (guint8 * destbuf, gint dbuf_len, + const guint8 ** srcbuf, gint buf_size, guint8 non_mod, guint8 * map_table) + { + GstBitReader gb = GST_BIT_READER_INIT (*srcbuf, buf_size); +- /* FIXME: Handle FALSE returns from gst_bit_reader_get_* calls? */ +- + gboolean stop_parsing = FALSE; + guint32 bits = 0; + guint32 pixels_read = 0; +@@ -858,23 +891,29 @@ _dvb_sub_read_8bit_string (guint8 * destbuf, gint dbuf_len, + /* Rephrased - it's better to work with bytes with default value '0' instead of reading from memory we don't own. */ + while (!stop_parsing && (gst_bit_reader_get_remaining (&gb) > 7)) { + guint run_length = 0, clut_index = 0; +- bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 8); ++ ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &bits, 8)) ++ goto not_enough_data; + + if (bits) { /* 8-bit_pixel-code */ + run_length = 1; + clut_index = bits; + } else { /* 8-bit_zero */ +- bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 1); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &bits, 1)) ++ goto not_enough_data; + if (bits == 0) { /* switch_1 == '0' */ + /* run_length_1-127 for pseudo-colour _entry) '0x00' */ +- run_length = gst_bit_reader_get_bits_uint32_unchecked (&gb, 7); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &run_length, 7)) ++ goto not_enough_data; + if (run_length == 0) { /* end_of_string_signal */ + stop_parsing = TRUE; + } + } else { /* switch_1 == '1' */ + /* run_length_3-127 */ +- run_length = gst_bit_reader_get_bits_uint32_unchecked (&gb, 7); +- clut_index = gst_bit_reader_get_bits_uint32_unchecked (&gb, 8); ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &run_length, 7)) ++ goto not_enough_data; ++ if (!gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 8)) ++ goto not_enough_data; + + if (run_length < 3) { + GST_WARNING ("runlength value was %u, but the spec requires it " +@@ -912,10 +951,17 @@ _dvb_sub_read_8bit_string (guint8 * destbuf, gint dbuf_len, + + GST_LOG ("Returning with %u pixels read", pixels_read); + +- *srcbuf += (gst_bit_reader_get_pos (&gb) + 7) >> 3; ++ gst_bit_reader_skip_to_byte (&gb); ++ *srcbuf = gb.data + gb.byte; + + // FIXME: Shouldn't need this variable if tracking things in the loop better + return pixels_read; ++ ++not_enough_data: ++ GST_WARNING ("Not enough data"); ++ // Go to the end of the buffer so the caller stops parsing ++ *srcbuf += buf_size; ++ return 0; + } + + static void +@@ -928,7 +974,6 @@ _dvb_sub_parse_pixel_data_block (DvbSub * dvb_sub, + guint8 *pbuf; + int x_pos, y_pos; + int i; +- gboolean dest_buf_filled = FALSE; + + guint8 map2to4[] = { 0x0, 0x7, 0x8, 0xf }; + guint8 map2to8[] = { 0x00, 0x77, 0x88, 0xff }; +@@ -963,24 +1008,13 @@ _dvb_sub_parse_pixel_data_block (DvbSub * dvb_sub, + + // FFMPEG-FIXME: ffmpeg doesn't check for equality and so can overflow destination buffer later on with bad input data + // FFMPEG-FIXME: However that makes it warn on end_of_object_line and map tables as well, so we add the dest_buf_filled tracking +- // FIXME: Removed x_pos checking here, because we don't want to turn dest_buf_filled to TRUE permanently in that case +- // FIXME: We assume that region->width - x_pos as dbuf_len to read_nbit_string will take care of that case nicely; +- // FIXME: That is, that read_nbit_string never scribbles anything if dbuf_len passed to it is zero due to this. +- if (y_pos >= region->height) { +- dest_buf_filled = TRUE; ++ if (x_pos >= region->width || y_pos >= region->height) { ++ GST_WARNING ("Invalid object location for data_type 0x%x!", *buf); ++ return; + } + + switch (*buf++) { + case 0x10: +- if (dest_buf_filled) { +- /* FIXME: Be more verbose */ +- GST_WARNING ("Invalid object location for data_type 0x%x!", +- *(buf - 1)); +- GST_MEMDUMP ("Remaining data after invalid object location:", buf, +- (guint) (buf_end - buf)); +- return; +- } +- + if (region->depth == 8) + map_table = map2to8; + else if (region->depth == 4) +@@ -995,15 +1029,6 @@ _dvb_sub_parse_pixel_data_block (DvbSub * dvb_sub, + region->width - x_pos, &buf, buf_end - buf, non_mod, map_table); + break; + case 0x11: +- if (dest_buf_filled) { +- /* FIXME: Be more verbose */ +- GST_WARNING ("Invalid object location for data_type 0x%x!", +- *(buf - 1)); +- GST_MEMDUMP ("Remaining data after invalid object location:", buf, +- buf_end - buf); +- return; // FIXME: Perhaps tell read_nbit_string that dbuf_len is zero and let it walk the bytes regardless? (Same FIXME for 2bit and 8bit) +- } +- + if (region->depth < 4) { + GST_WARNING ("4-bit pixel string in %d-bit region!", region->depth); + return; +@@ -1024,15 +1049,6 @@ _dvb_sub_parse_pixel_data_block (DvbSub * dvb_sub, + GST_DEBUG ("READ_4BIT_STRING finished: buf pointer now %p", buf); + break; + case 0x12: +- if (dest_buf_filled) { +- /* FIXME: Be more verbose */ +- GST_WARNING ("Invalid object location for data_type 0x%x!", +- *(buf - 1)); +- GST_MEMDUMP ("Remaining data after invalid object location:", +- buf, (guint) (buf_end - buf)); +- return; +- } +- + if (region->depth < 8) { + GST_WARNING ("8-bit pixel string in %d-bit region!", region->depth); + return; +@@ -1046,19 +1062,29 @@ _dvb_sub_parse_pixel_data_block (DvbSub * dvb_sub, + + case 0x20: + GST_DEBUG ("handling map2to4 table data"); +- /* FIXME: I don't see any guards about buffer size here - buf++ happens with the switch, but +- * FIXME: buffer is walked without length checks? Same deal in other map table cases */ ++ if (buf + 2 > buf_end) { ++ GST_WARNING ("map2to4 table too short"); ++ return; ++ } + map2to4[0] = (*buf) >> 4; + map2to4[1] = (*buf++) & 0xf; + map2to4[2] = (*buf) >> 4; + map2to4[3] = (*buf++) & 0xf; + break; + case 0x21: ++ if (buf + 4 > buf_end) { ++ GST_WARNING ("map2to8 table too short"); ++ return; ++ } + GST_DEBUG ("handling map2to8 table data"); + for (i = 0; i < 4; i++) + map2to8[i] = *buf++; + break; + case 0x22: ++ if (buf + 16 > buf_end) { ++ GST_WARNING ("map4to8 table too short"); ++ return; ++ } + GST_DEBUG ("handling map4to8 table data"); + for (i = 0; i < 16; i++) + map4to8[i] = *buf++; +@@ -1085,6 +1111,9 @@ _dvb_sub_parse_object_segment (DvbSub * dvb_sub, guint16 page_id, + + guint8 coding_method, non_modifying_color; + ++ if (buf_size < 3) ++ return; ++ + object_id = GST_READ_UINT16_BE (buf); + buf += 2; + +@@ -1107,6 +1136,9 @@ _dvb_sub_parse_object_segment (DvbSub * dvb_sub, guint16 page_id, + DVBSubObjectDisplay *display; + guint16 top_field_len, bottom_field_len; + ++ if (buf + 4 > buf_end) ++ return; ++ + top_field_len = GST_READ_UINT16_BE (buf); + buf += 2; + bottom_field_len = GST_READ_UINT16_BE (buf); +-- +2.53.0 + diff --git a/0003-dvbsuboverlay-Avoid-integer-overflows-and-unreasonab.patch b/0003-dvbsuboverlay-Avoid-integer-overflows-and-unreasonab.patch new file mode 100644 index 0000000..740df04 --- /dev/null +++ b/0003-dvbsuboverlay-Avoid-integer-overflows-and-unreasonab.patch @@ -0,0 +1,55 @@ +From ff291516bd4f51610b2914ebc72a7d238233bad2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= +Date: Thu, 12 Feb 2026 09:50:23 +0200 +Subject: [PATCH 3/4] dvbsuboverlay: Avoid integer overflows and unreasonably + large displays/regions + +Part-of: +--- + .../gst/dvbsuboverlay/dvb-sub.c | 23 +++++++++++++++++++ + 1 file changed, 23 insertions(+) + +diff --git a/subprojects/gst-plugins-bad/gst/dvbsuboverlay/dvb-sub.c b/subprojects/gst-plugins-bad/gst/dvbsuboverlay/dvb-sub.c +index 9d7704b86d..e44b8e2631 100644 +--- a/subprojects/gst-plugins-bad/gst/dvbsuboverlay/dvb-sub.c ++++ b/subprojects/gst-plugins-bad/gst/dvbsuboverlay/dvb-sub.c +@@ -483,6 +483,17 @@ _dvb_sub_parse_region_segment (DvbSub * dvb_sub, guint16 page_id, + region->height = GST_READ_UINT16_BE (buf); + buf += 2; + ++ /* Avoid integer overflows and also clamp to a reasonable size of 8kx8k for ++ * the region size. We allow 16kx16k display sizes. */ ++ if (region->width > 8192 || region->height > 8192) { ++ GST_WARNING ("too large region of %ux%x", region->width, region->height); ++ g_free (region->pbuf); ++ region->pbuf = NULL; ++ region->buf_size = 0; ++ region->width = region->height = 0; ++ return; ++ } ++ + if (region->width * region->height != region->buf_size) { /* FIXME: Read closer from spec what happens when dimensions change */ + g_free (region->pbuf); + +@@ -1196,6 +1207,18 @@ _dvb_sub_parse_display_definition_segment (DvbSub * dvb_sub, const guint8 * buf, + display_height = GST_READ_UINT16_BE (buf) + 1; + buf += 2; + ++ /* Avoid integer overflows and also clamp to a reasonable size of 16kx16k */ ++ if (display_width > 16384 || display_height > 16384) { ++ GST_WARNING ("too large display size of %ux%x", display_width, ++ display_height); ++ /* Reset to the initial values */ ++ dvb_sub->display_def.version = -1; ++ dvb_sub->display_def.window_flag = 0; ++ dvb_sub->display_def.display_width = 720; ++ dvb_sub->display_def.display_height = 576; ++ return -1; ++ } ++ + if ((display_width != dvb_sub->display_def.display_width) + || (display_height != dvb_sub->display_def.display_height)) { + dvb_sub->display_def.display_width = display_width; +-- +2.53.0 + diff --git a/0004-libs-jpegparser-boundary-checks-before-copying-it.patch b/0004-libs-jpegparser-boundary-checks-before-copying-it.patch new file mode 100644 index 0000000..d841d4b --- /dev/null +++ b/0004-libs-jpegparser-boundary-checks-before-copying-it.patch @@ -0,0 +1,42 @@ +From d569264a87740fc5fac702a90d8546ce5dfb15e4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?V=C3=ADctor=20Manuel=20J=C3=A1quez=20Leal?= + +Date: Wed, 11 Feb 2026 22:07:49 +0100 +Subject: [PATCH 4/4] libs: jpegparser: boundary checks before copying it + +READ_BYTES macro reads data from a byte reader and then copy it to a storage +variable. This patch adds a validation that the length to read cannot be bigger +than the storage size. + +This macro right now is used only for storage variables of guint8 arrays. + +We have validated in the specification (sections F.1.2.1.2 and F.1.2.2.1 in ITU +T.81) that Huffman tables (both AC and DC) aren't bigger than 256. + +Fixes SA-2026-0003, CVE-2026-3082, ZDI-CAN-28840. + +Fixes: + +Part-of: +--- + .../gst-plugins-bad/gst-libs/gst/codecparsers/gstjpegparser.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gstjpegparser.c b/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gstjpegparser.c +index 0cb5d0b739..fd66f6cfdf 100644 +--- a/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gstjpegparser.c ++++ b/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gstjpegparser.c +@@ -77,6 +77,10 @@ ensure_debug_category (void) + + #define READ_BYTES(reader, buf, length) G_STMT_START { \ + const guint8 *vals; \ ++ if (length > sizeof (buf)) { \ ++ GST_WARNING ("data size is bigger than its storage"); \ ++ goto failed; \ ++ } \ + if (!gst_byte_reader_get_data (reader, length, &vals)) { \ + GST_WARNING ("failed to read bytes, size:%d", length); \ + goto failed; \ +-- +2.53.0 + diff --git a/gstreamer1-plugins-bad-free.spec b/gstreamer1-plugins-bad-free.spec index f3e5440..d942aba 100644 --- a/gstreamer1-plugins-bad-free.spec +++ b/gstreamer1-plugins-bad-free.spec @@ -26,7 +26,7 @@ Name: gstreamer1-plugins-bad-free Version: 1.26.7 -Release: 1%{?dist} +Release: 2%{?dist} Summary: GStreamer streaming media framework "bad" plugins License: LGPLv2+ and LGPLv2 @@ -40,7 +40,11 @@ Source: https://gstreamer.freedesktop.org/src/gst-plugins-bad/gst-plugin %endif # https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5622 -Patch0: openh264-add-license-file.patch +Patch: openh264-add-license-file.patch +Patch: 0001-dvbsuboverlay-Mark-parsed-byte-array-as-const.patch +Patch: 0002-dvbsuboverlay-Add-missing-bounds-checks-to-the-parse.patch +Patch: 0003-dvbsuboverlay-Avoid-integer-overflows-and-unreasonab.patch +Patch: 0004-libs-jpegparser-boundary-checks-before-copying-it.patch BuildRequires: meson >= 0.48.0 BuildRequires: gcc-c++ @@ -868,6 +872,10 @@ EOF %changelog +* Tue Mar 31 2026 Wim Taymans - 1.26.7-2 +- Add patches for CVE-2026-2923 and CVE-2026-3082 + Resolves: RHEL-156118, RHEL-156189 + * Tue Nov 04 2025 Wim Taymans - 1.26.7-1 - Update to 1.26.7 Resolves: RHEL-126057