https://bugs.gentoo.org/970993

From 1b12d63b4414de80ebf5561823b6a0ac8b734eb1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
Date: Wed, 25 Feb 2026 17:22:52 +0200
Subject: [PATCH 1/3] dvbsuboverlay: Mark parsed byte array as const

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/10884>
--- a/gst/dvbsuboverlay/dvb-sub.c
+++ b/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;
--- a/gst/dvbsuboverlay/dvb-sub.h
+++ b/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);
 
-- 
GitLab


From 3b8253f447bcc9831dbf643d2c69b205fedbe086 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
Date: Wed, 11 Feb 2026 20:45:12 +0200
Subject: [PATCH 2/3] 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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/10884>
--- a/gst/dvbsuboverlay/dvb-sub.c
+++ b/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);
-- 
GitLab


From f0a84752aaa09457fcf736c93cecdff34ec0bfb2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
Date: Thu, 12 Feb 2026 09:50:23 +0200
Subject: [PATCH 3/3] dvbsuboverlay: Avoid integer overflows and unreasonably
 large displays/regions

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/10884>
--- a/gst/dvbsuboverlay/dvb-sub.c
+++ b/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;
-- 
GitLab

