[vlc-commits] [Git][videolan/vlc][master] 13 commits: cc: OOB read in CEA-708 CDP time_code_section

Thomas Guillem (@tguillem) gitlab at videolan.org
Tue Dec 2 08:16:34 UTC 2025



Thomas Guillem pushed to branch master at VideoLAN / VLC


Commits:
13d407e1 by Stanislav Fort at 2025-12-02T09:05:06+01:00
cc: OOB read in CEA-708 CDP time_code_section

Fixes #29322

- - - - -
0fc607e0 by Stanislav Fort at 2025-12-02T09:05:06+01:00
cea708: fix OOB write in CEA-708 RTL window scroll

Fixes #29323

- - - - -
8c0c8804 by Stanislav Fort at 2025-12-02T09:05:06+01:00
cea708: use 4-byte stride for 4-byte character cells

Fixes corruption/OOB.
Fixes #29326

- - - - -
1a9556cd by Stanislav Fort at 2025-12-02T09:05:06+01:00
cea708: fix OOB write in CEA-708 LTR window scroll

Due to wrong bound check

Fixes #29375

- - - - -
5dc06dab by Stanislav Fort at 2025-12-02T09:05:06+01:00
cea708: fix Window_MinCol()/Window_MaxCol() indexes

Refs #29328

- - - - -
c86e8910 by Stanislav Fort at 2025-12-02T09:05:06+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

- - - - -
ec79e365 by Steve Lhomme at 2025-12-02T09:05:06+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

- - - - -
995e77e7 by Steve Lhomme at 2025-12-02T09:05:06+01:00
demux: aiff: return early if the allocation is not possible

We need to allocate i_data_size + 1. So i_data_size cannot be SIZE_MAX or higher.

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

- - - - -
28f65576 by Steve Lhomme at 2025-12-02T09:05:06+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

- - - - -
49ba7d18 by Steve Lhomme at 2025-12-02T09:05:06+01:00
demux: ty: stop read chunk if offset is past the data

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

- - - - -
39e3dc58 by Steve Lhomme at 2025-12-02T09:05:06+01:00
demux: ty: don't check for S1/S2 if the offsets are too large

- - - - -
46da8289 by Steve Lhomme at 2025-12-02T09:05:06+01:00
demux: ty: don't look for ES header past the input buffer boundaries

- - - - -
1f75ed8f by Stanislav Fort at 2025-12-02T09:05:06+01:00
oggspots: fix OOB read (unchecked image offset)

Fixes #29319

- - - - -


7 changed files:

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


Changes:

=====================================
modules/codec/cc.h
=====================================
@@ -410,7 +410,7 @@ static inline void cc_Extract( cc_data_t *c, enum cc_payload_type_e i_payload_ty
         {
             if( i_src < 5 ) // Shall be 5 bytes
                 return;
-            p_src += 5; i_src += 5;
+            p_src += 5; i_src -= 5;
         }
         /* 5.4 ccdata_section */
         if( cdp_flags & CDP_FLAG_CC_DATA_PRESENT )


=====================================
modules/codec/cea708.c
=====================================
@@ -561,7 +561,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;
     }
@@ -573,7 +573,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;
     }
@@ -605,6 +605,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 )
@@ -616,6 +618,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--;
+                    }
+                   
                 }
             }
         }
@@ -626,6 +634,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 )
@@ -637,6 +647,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++;
+                    }
+                   
                 }
             }
         }
@@ -667,15 +683,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++;
@@ -689,14 +710,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;
 


=====================================
modules/codec/oggspots.c
=====================================
@@ -350,6 +350,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 */
     es_format_t fmt_in = *p_dec->fmt_in;
     if ( !memcmp(&p_block->p_buffer[4], "PNG", 3) ) {


=====================================
modules/demux/aiff.c
=====================================
@@ -114,6 +114,9 @@ static int ReadTextChunk( demux_t *p_demux,  uint64_t i_chunk_size, uint32_t i_d
     demux_sys_t *p_sys = p_demux->p_sys;
     const uint8_t *p_peek;
 
+    if ( i_data_size >= SIZE_MAX )
+        return VLC_ENOMEM;
+
     ssize_t ret = vlc_stream_Peek( p_demux->s, &p_peek, i_chunk_size );
     if( ret == -1 || (size_t) ret != i_chunk_size )
         return VLC_EGENERIC;


=====================================
modules/demux/ogg.c
=====================================
@@ -2491,9 +2491,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];
@@ -2509,6 +2511,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;
             }
@@ -2516,6 +2519,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];
@@ -2531,6 +2535,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;
             }
@@ -2538,8 +2543,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 @@ typedef struct
 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);
 
@@ -582,11 +582,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;
@@ -695,7 +695,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",
@@ -872,10 +872,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 */
@@ -911,7 +911,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],
@@ -970,7 +970,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 "
@@ -1765,6 +1765,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;
@@ -1782,6 +1783,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))
@@ -1846,12 +1848,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" );
@@ -1867,6 +1870,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/0aa985159e5d6c609085e62f302678f6805d2940...1f75ed8f520d134be4f0799eedc5baec2a0c4f0b

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/0aa985159e5d6c609085e62f302678f6805d2940...1f75ed8f520d134be4f0799eedc5baec2a0c4f0b
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