[vlc-commits] [Git][videolan/vlc][3.0.x] 11 commits: demux: ogg: keep the map sizes when they are set

Thomas Guillem (@tguillem) gitlab at videolan.org
Thu Nov 13 15:05:06 UTC 2025



Thomas Guillem pushed to branch 3.0.x at VideoLAN / VLC


Commits:
b3a03a21 by Steve Lhomme at 2025-11-13T16:03:22+01:00
demux: ogg: keep the map sizes when they are set

The p_old_map/p_new_map sizes should match their buffer size.

By default the channel counts are zero which considers the old/new maps match.
The channel count read may be updated but not the channel mapping.
We need to check the channel count match and the mapping matches.

Fixes https://code.videolan.org/videolan/vlc/-/issues/29314

- - - - -
82c10bb0 by Steve Lhomme at 2025-11-13T16:03:22+01:00
cvdbsub: don't use metadata offset if bigger than whole SPU

ParseMetaInfo() will not parse anything in that case as p == p_end.
The image will use the whole of the SPU buffer, potentially containing
metadata at the end. But the reading in RenderImage() is constrained by the width/height
so it won't use these data.

Fixes https://code.videolan.org/videolan/vlc/-/issues/29325

- - - - -
b728f207 by Steve Lhomme at 2025-11-13T16:03:23+01:00
demux: ty: stop read chunk if offset is past the data

Fixes https://code.videolan.org/videolan/vlc/-/issues/29316

- - - - -
ee89f0f7 by Steve Lhomme at 2025-11-13T16:03:23+01:00
demux: ty: don't check for S1/S2 if the offsets are too large

- - - - -
fa562df8 by Steve Lhomme at 2025-11-13T16:03:23+01:00
demux: ty: don't look for ES header past the input buffer boundaries

- - - - -
96f32bb6 by Stanislav Fort at 2025-11-13T16:03:23+01:00
cea708: fix OOB write in CEA-708 RTL window scroll

Fixes #29323

- - - - -
7cda527f by Stanislav Fort at 2025-11-13T16:03:23+01:00
cea708: use 4-byte stride for 4-byte character cells

Fixes corruption/OOB.
Fixes #29326

- - - - -
6e585a4d by Stanislav Fort at 2025-11-13T16:03:23+01:00
cea708: fix OOB write in CEA-708 LTR window scroll

Due to wrong bound check

Fixes #29375

- - - - -
e5d01c1a by Stanislav Fort at 2025-11-13T16:03:23+01:00
cea708: fix Window_MinCol()/Window_MaxCol() indexes

Refs #29328

- - - - -
153a13b9 by Stanislav Fort at 2025-11-13T16:03:23+01:00
cea708: fix CEA708_Window_Truncate()

Make truncation actually drop one column in LTR/RTL (adjust
lastcol/firstcol; delete rows only when they become empty).

Fixes #29328

- - - - -
f7745d07 by Stanislav Fort at 2025-11-13T16:03:23+01:00
oggspots: fix OOB read (unchecked image offset)

Fixes #29319

- - - - -


5 changed files:

- modules/codec/cea708.c
- modules/codec/cvdsub.c
- modules/codec/oggspots.c
- modules/demux/ogg.c
- modules/demux/ty.c


Changes:

=====================================
modules/codec/cea708.c
=====================================
@@ -556,7 +556,7 @@ static uint8_t CEA708_Window_MinCol( const cea708_window_t *p_w )
     uint8_t i_min = CEA708_WINDOW_MAX_COLS;
     for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
     {
-        const cea708_text_row_t *p_row = p_w->rows[p_w->row];
+        const cea708_text_row_t *p_row = p_w->rows[i];
         if( p_row && p_row->firstcol < i_min )
             i_min = p_row->firstcol;
     }
@@ -568,7 +568,7 @@ static uint8_t CEA708_Window_MaxCol( const cea708_window_t *p_w )
     uint8_t i_max = 0;
     for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
     {
-        const cea708_text_row_t *p_row = p_w->rows[p_w->row];
+        const cea708_text_row_t *p_row = p_w->rows[i];
         if( p_row && p_row->lastcol > i_max )
             i_max = p_row->lastcol;
     }
@@ -600,6 +600,8 @@ static void CEA708_Window_Truncate( cea708_window_t *p_w, int i_direction )
             for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
             {
                 cea708_text_row_t *row = p_w->rows[i];
+                if (!row)
+                    continue;
                 if( row->lastcol == i_max )
                 {
                     if( row->firstcol >= row->lastcol )
@@ -611,6 +613,12 @@ static void CEA708_Window_Truncate( cea708_window_t *p_w, int i_direction )
                         else if( i == p_w->i_lastrow )
                             p_w->i_lastrow--;
                     }
+                    else
+                    {
+                        /* Drop rightmost column */
+                        row->lastcol--;
+                    }
+                   
                 }
             }
         }
@@ -621,6 +629,8 @@ static void CEA708_Window_Truncate( cea708_window_t *p_w, int i_direction )
             for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
             {
                 cea708_text_row_t *row = p_w->rows[i];
+                if (!row)
+                    continue;
                 if( row->firstcol == i_min )
                 {
                     if( row->firstcol >= row->lastcol )
@@ -632,6 +642,12 @@ static void CEA708_Window_Truncate( cea708_window_t *p_w, int i_direction )
                         else if( i == p_w->i_lastrow )
                             p_w->i_lastrow--;
                     }
+                    else
+                    {
+                        /* Drop leftmost column */
+                        row->firstcol++;
+                    }
+                   
                 }
             }
         }
@@ -662,15 +678,20 @@ static void CEA708_Window_Scroll( cea708_window_t *p_w )
     {
         case CEA708_WA_DIRECTION_LTR:
             /* Move RIGHT */
-            if( CEA708_Window_MaxCol( p_w ) == CEA708_WINDOW_MAX_ROWS - 1 )
+            if( CEA708_Window_MaxCol( p_w ) == CEA708_WINDOW_MAX_COLS - 1 )
                 CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_LTR );
             for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
             {
                 cea708_text_row_t *row = p_w->rows[i];
+                if( !row )
+                    continue;
                 if( row->lastcol < row->firstcol ) /* should not happen */
                     continue;
-                memmove( &row->characters[row->firstcol + 1], &row->characters[row->firstcol],
-                         (row->lastcol - row->firstcol + 1) * 4U );
+
+                size_t start = (size_t) row->firstcol * 4U;
+                size_t count = (size_t) (row->lastcol - row->firstcol + 1) * 4U;
+                memmove( &row->characters[start + 4U], &row->characters[start],
+                         count );
                 memmove( &row->styles[row->firstcol + 1], &row->styles[row->firstcol],
                          (row->lastcol - row->firstcol + 1) * sizeof(cea708_pen_style_t) );
                 row->firstcol++;
@@ -684,14 +705,21 @@ static void CEA708_Window_Scroll( cea708_window_t *p_w )
             for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
             {
                 cea708_text_row_t *row = p_w->rows[i];
+                if( !row )
+                    continue;
                 if( row->lastcol < row->firstcol ) /* should not happen */
                     continue;
-                memmove( &row->characters[row->firstcol - 1], &row->characters[row->firstcol],
-                         (row->lastcol - row->firstcol + 1) * 4U );
-                memmove( &row->styles[row->firstcol - 1], &row->styles[row->firstcol],
-                         (row->lastcol - row->firstcol + 1) * sizeof(cea708_pen_style_t) );
-                row->firstcol--;
-                row->lastcol--;
+                if( row->firstcol > 0 )
+                {
+                    size_t start = (size_t) row->firstcol * 4U;
+                    size_t count = (size_t) (row->lastcol - row->firstcol + 1) * 4U;
+                    memmove( &row->characters[start -4U], &row->characters[start],
+                             count );
+                    memmove( &row->styles[row->firstcol - 1], &row->styles[row->firstcol],
+                             (row->lastcol - row->firstcol + 1) * sizeof(cea708_pen_style_t) );
+                    row->firstcol--;
+                    row->lastcol--;
+                }
             }
             break;
         case CEA708_WA_DIRECTION_TB:


=====================================
modules/codec/cvdsub.c
=====================================
@@ -315,9 +315,11 @@ static void ParseHeader( decoder_t *p_dec, block_t *p_block )
 
     p_sys->i_spu_size = (p[0] << 8) + p[1] + 4; p += 2;
 
-    /* FIXME: check data sanity */
     p_sys->metadata_offset = (p[0] <<  8) +   p[1]; p +=2;
-    p_sys->metadata_length = p_sys->i_spu_size - p_sys->metadata_offset;
+    if ( p_sys->i_spu_size > p_sys->metadata_offset )
+        p_sys->metadata_length = p_sys->i_spu_size - p_sys->metadata_offset;
+    else
+        p_sys->metadata_length = 0; // unusable metadata
 
     p_sys->i_image_offset = 4;
     p_sys->i_image_length = p_sys->metadata_offset - p_sys->i_image_offset;


=====================================
modules/codec/oggspots.c
=====================================
@@ -354,6 +354,12 @@ static picture_t* DecodePacket(decoder_t* p_dec, block_t* p_block)
         goto error;
     }
 
+    if (i_img_offset > p_block->i_buffer) {
+        msg_Dbg(p_dec, "Invalid byte offset: %u exceeds packet size %zu",
+                i_img_offset, p_block->i_buffer);
+        goto error;
+    }
+
     /* Image format */
     if ( !memcmp(&p_block->p_buffer[4], "PNG", 3) ) {
         p_dec->fmt_in.video.i_chroma = VLC_CODEC_PNG;


=====================================
modules/demux/ogg.c
=====================================
@@ -2424,9 +2424,11 @@ static bool Ogg_IsOpusFormatCompatible( const es_format_t *p_new,
         int i_new_stream_count;
         int i_old_coupled_count;
         int i_new_coupled_count;
+        size_t i_old_map_size, i_new_map_size;
         p_old_head = pp_old_data[0];
         i_old_channel_count = i_old_stream_count = i_old_coupled_count = 0;
         p_old_map = default_map;
+        i_old_map_size = ARRAY_SIZE(default_map);
         if( pi_old_size[0] >= 19 && p_old_head[8] <= 15 )
         {
             i_old_channel_count = p_old_head[9];
@@ -2442,6 +2444,7 @@ static bool Ogg_IsOpusFormatCompatible( const es_format_t *p_new,
                         i_old_stream_count = p_old_head[19];
                         i_old_coupled_count = p_old_head[20];
                         p_old_map = p_old_head + 21;
+                        i_old_map_size = i_old_channel_count;
                     }
                     break;
             }
@@ -2449,6 +2452,7 @@ static bool Ogg_IsOpusFormatCompatible( const es_format_t *p_new,
         p_new_head = (unsigned char *)pp_new_data[0];
         i_new_channel_count = i_new_stream_count = i_new_coupled_count = 0;
         p_new_map = default_map;
+        i_new_map_size = ARRAY_SIZE(default_map);
         if( pi_new_size[0] >= 19 && p_new_head[8] <= 15 )
         {
             i_new_channel_count = p_new_head[9];
@@ -2464,6 +2468,7 @@ static bool Ogg_IsOpusFormatCompatible( const es_format_t *p_new,
                         i_new_stream_count = p_new_head[19];
                         i_new_coupled_count = p_new_head[20];
                         p_new_map = p_new_head+21;
+                        i_new_map_size = i_new_channel_count;
                     }
                     break;
             }
@@ -2471,8 +2476,9 @@ static bool Ogg_IsOpusFormatCompatible( const es_format_t *p_new,
         b_match = i_old_channel_count == i_new_channel_count &&
                   i_old_stream_count == i_new_stream_count &&
                   i_old_coupled_count == i_new_coupled_count &&
+                  i_old_map_size == i_new_map_size &&
                   memcmp(p_old_map, p_new_map,
-                      i_new_channel_count*sizeof(*p_new_map)) == 0;
+                      i_new_map_size*sizeof(*p_new_map)) == 0;
     }
 
     return b_match;


=====================================
modules/demux/ty.c
=====================================
@@ -265,7 +265,7 @@ struct demux_sys_t
 static int get_chunk_header(demux_t *);
 static vlc_tick_t get_pts( const uint8_t *buf );
 static int find_es_header( const uint8_t *header,
-                           const uint8_t *buffer, int i_search_len );
+                           const uint8_t *buffer, size_t buffer_len, size_t i_search_len );
 static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct);
 static int ty_stream_seek_time(demux_t *, uint64_t);
 
@@ -585,11 +585,11 @@ static vlc_tick_t get_pts( const uint8_t *buf )
 
 /* =========================================================================== */
 static int find_es_header( const uint8_t *header,
-                           const uint8_t *buffer, int i_search_len )
+                           const uint8_t *buffer, size_t buffer_len, size_t i_search_len )
 {
-    int count;
+    size_t count;
 
-    for( count = 0; count < i_search_len; count++ )
+    for( count = 0; count < i_search_len && count + 4 < buffer_len; count++ )
     {
         if( !memcmp( &buffer[count], header, 4 ) )
             return count;
@@ -700,7 +700,7 @@ static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl
          * on S1, only 0x06 has PES.  On S2, however, most all do.
          * Do NOT Pass the PES Header to the MPEG2 codec */
         size_t search_len = __MIN(l_rec_size - sizeof(ty_VideoPacket), 5);
-        esOffset1 = find_es_header( ty_VideoPacket, p_block_in->p_buffer, search_len );
+        esOffset1 = find_es_header( ty_VideoPacket, p_block_in->p_buffer, p_block_in->i_buffer, search_len );
         if( esOffset1 != -1 )
         {
             //msg_Dbg(p_demux, "Video PES hdr in pkt type 0x%02x at offset %d",
@@ -871,10 +871,10 @@ static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl
             /* get the PTS out of this PES header (MPEG or AC3) */
             if (p_sys->audio_type == TIVO_AUDIO_MPEG)
                 esOffset1 = find_es_header(ty_MPEGAudioPacket,
-                        p_sys->pes_buffer, 5);
+                        p_sys->pes_buffer, ARRAY_SIZE(p_sys->pes_buffer), 5);
             else
                 esOffset1 = find_es_header(ty_AC3AudioPacket,
-                        p_sys->pes_buffer, 5);
+                        p_sys->pes_buffer, ARRAY_SIZE(p_sys->pes_buffer), 5);
             if (esOffset1 < 0)
             {
                 /* god help us; something's really wrong */
@@ -910,7 +910,7 @@ static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl
         /* MPEG Audio with PES Header, either SA or DTiVo   */
         /* ================================================ */
         esOffset1 = find_es_header( ty_MPEGAudioPacket,
-                p_block_in->p_buffer, 5 );
+                p_block_in->p_buffer, p_block_in->i_buffer, 5 );
 
         /*msg_Dbg(p_demux, "buffer has %#02x %#02x %#02x %#02x",
            p_block_in->p_buffer[0], p_block_in->p_buffer[1],
@@ -971,7 +971,7 @@ static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl
         /* DTiVo AC3 Audio Data with PES Header             */
         /* ================================================ */
         esOffset1 = find_es_header( ty_AC3AudioPacket,
-                p_block_in->p_buffer, 5 );
+                p_block_in->p_buffer, p_block_in->i_buffer, 5 );
 
 #if 0
         msg_Dbg(p_demux, "buffer has "
@@ -1770,6 +1770,7 @@ static int analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk)
 {
     demux_sys_t *p_sys = p_demux->p_sys;
     int i_num_recs, i;
+    size_t chunk_size = CHUNK_SIZE;
     ty_rec_hdr_t *p_hdrs;
     int i_num_6e0, i_num_be0, i_num_9c0, i_num_3c0;
     int i_payload_size;
@@ -1787,6 +1788,7 @@ static int analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk)
     }
 
     p_chunk += CHUNK_HEADER_SIZE;       /* skip past rec count & SEQ bytes */
+    chunk_size -= CHUNK_HEADER_SIZE;
     //msg_Dbg(p_demux, "probe: chunk has %d recs", i_num_recs);
     p_hdrs = parse_chunk_headers(p_chunk, i_num_recs, &i_payload_size);
     if (unlikely(p_hdrs == NULL))
@@ -1851,12 +1853,13 @@ static int analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk)
                     p_hdrs[i].l_rec_size > 15) {
                 /* first make sure we're aligned */
                 int i_pes_offset = find_es_header(ty_MPEGAudioPacket,
-                        &p_chunk[i_data_offset], 5);
+                        &p_chunk[i_data_offset], chunk_size - i_data_offset, 5);
                 if (i_pes_offset >= 0) {
                     /* pes found. on SA, PES has hdr data at offset 6, not PTS. */
                     //msg_Dbg(p_demux, "probe: mpeg es header found in rec %d at offset %d",
                             //i, i_pes_offset);
-                    if ((p_chunk[i_data_offset + 6 + i_pes_offset] & 0x80) == 0x80) {
+                    if (i_data_offset + 6 + i_pes_offset < chunk_size &&
+                        (p_chunk[i_data_offset + 6 + i_pes_offset] & 0x80) == 0x80) {
                         /* S1SA or S2(any) Mpeg Audio (PES hdr, not a PTS start) */
                         if (p_sys->tivo_series == TIVO_SERIES1)
                             msg_Dbg(p_demux, "detected Stand-Alone Tivo" );
@@ -1872,6 +1875,11 @@ static int analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk)
                 }
             }
             i_data_offset += p_hdrs[i].l_rec_size;
+            if (i_data_offset > chunk_size)
+            {
+                msg_Dbg(p_demux, "rec[%d] overflows the size of the records %ld, aborting", i, p_hdrs[i].l_rec_size);
+                break;
+            }
         }
     }
     free(p_hdrs);



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/d0adf4fbb02555447fa6880a53c4f29a1629d7d5...f7745d071ba5b7164bb06c22322dcfd278e384da

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/d0adf4fbb02555447fa6880a53c4f29a1629d7d5...f7745d071ba5b7164bb06c22322dcfd278e384da
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list