Skip to content

[BUG] Three out-of-bounds reads in TS parsing when a packet's adaptation field leaves a short payload (heap overflow in parse_PAT via uint32_t underflow) #2348

Closed

Description

@pranayr710

CCExtractor version: master (2364994)

Necessary information

  • Is this a regression (i.e. did it work before)? NO — long-standing.
  • What platform did you use? Linux (Debian bookworm, x86-64, built from docker/Dockerfile). Platform-independent.
  • What were the used arguments? --out=srt (any output format reproduces it)

Video links

Inline below — the reproducer is 1,692 bytes (9 TS packets).

Additional information

Summary

A transport stream packet whose adaptation field leaves a very short payload drives three out-of-bounds reads. All are reachable from stream data, and all are caught by AddressSanitizer.

1. parse_PAT() — heap-buffer-overflow (the serious one)

https://github.com/CCExtractor/ccextractor/blob/master/src/lib_ccx/ts_tables.c#L646-L653

pointer_field  = *(ctx->PID_buffers[0]->buffer);
payload_start  = ctx->PID_buffers[0]->buffer + pointer_field + 1;
payload_length = ctx->PID_buffers[0]->buffer_length - (pointer_field + 1);

if (payload_length < 8)
        return 0;

section_number = payload_start[6];

pointer_field is a raw byte from the stream and buffer_length is uint32_t. When pointer_field + 1 > buffer_length the subtraction underflows to ~4×10⁹, sails through the < 8 guard, and payload_start is then dereferenced far outside the allocation.

ts_buffer_psi_packet() makes a one-byte buffer whenever adaptation_field_length is 182 (payload_length = tspacket + 188 - payload_start = 1). With pointer_field = 0xB7, ASan reports:

==1==ERROR: AddressSanitizer: heap-buffer-overflow
READ of size 1
    #0 parse_PAT       ../src/lib_ccx/ts_tables.c:655
    #1 ts_readstream   ../src/lib_ccx/ts_functions.c:796
    #2 ts_get_more_data ../src/lib_ccx/ts_functions.c:1092
0x602000004cc9 is located 184 bytes to the right of 1-byte region [0x602000004c10,0x602000004c11)

This is the same shape as #2322 (unsigned underflow defeating a bounds check).

2. parse_SDT() — same underflow

https://github.com/CCExtractor/ccextractor/blob/master/src/lib_ccx/ts_tables.c#L951-L953

Identical buffer_length - (pointer_field + 1) underflow, and unlike parse_PAT there is no length check at all before payload_start[0], [1], [2] are read.

3. ts_readstream() — global-buffer-overflow

https://github.com/CCExtractor/ccextractor/blob/master/src/lib_ccx/ts_functions.c#L895-L900

if (payload.pesstart)
{
        uint64_t pes_prefix = (payload.start[0] << 16) | (payload.start[1] << 8) | payload.start[2];
        uint8_t  pes_stream_id = payload.start[3];

No payload.length check. In ts_readpacket(), adaptation_field_length of 183 passes the adaptation_field_length < payload->length test (183 < 184), so payload.start advances to tspacket + 188 — one past the end of the 188-byte global — and payload.length becomes 0.

==1==ERROR: AddressSanitizer: global-buffer-overflow
    #0 ts_readstream ../src/lib_ccx/ts_functions.c:899
0x... is located 0 bytes to the right of global variable 'tspacket'
  defined in '../src/lib_ccx/ts_functions.c:18:15' of size 188

Reproducer

1,692 bytes, 9 TS packets. This is the minimal prefix that still triggers #1 — 8 packets is clean.

base64 -d > repro.ts <<'B64EOF'
R0AAMLYA////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
AACwDQABwQAAAAHwACqxBLJHUAAwtgD/////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////AAKwEgABwQAA8AHwAALwAfAABc5VM0dQATC2AP//////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////AAAB4AA/gYAFIQALfkEAAAGz
LQHgJP//4AAAAAG4ggAgQAAAAQAAD//4AAABskdBOTQDQf/8lCD/AAABAQAAAAAAAAAAR1ABMbYA
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
/////////////////////////wAAAeAAK4GABSEAC5W3AAABAABP//gAAAGyR0E5NANB//yUrv8A
AAEBAAAAAAAAAABHUAEytgD/////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////AAAB4AArgYAFIQALrS0AAAEAAI//
+AAAAbJHQTk0A0H//JRw/wAAAQEAAAAAAAAAAEdQATO2AP//////////////////////////////
////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////8AAAHg
ACuBgAUhAAvEowAAAQAAz//4AAABskdBOTQDQf/8Rkn/AAABAQAAAAAAAAAAR1ABNLYA////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
/////////////////wAAAeAAK4GABSEAC9wZAAABAAEP//gAAAGyR0E5NANB//xS0/8AAAEBAAAA
AAAAAABHUAE1tgD/////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////AAAB4AArgYAFIQAL848AAAEAAU//+AAAAbJH
QTk0A0H//FQg/wAAAQEAAAAAAAAAAEdQATa2AP//////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////8AAAHgACuBgAUh
AA0LBQAAAQABj//4AAABskdBOTQDQf/8VNP/AAABAQAAAAAAAAAA
B64EOF
ccextractor repro.ts --out=srt -o out.srt

sha256 e0e1507623a14e4d938f0f96bfaee75dc6ec3b681e2fc5ee25f00adac8ab76ea

It is a synthetic MPEG-2 TS (PAT + PMT + CEA-608 in ATSC A/53 GA94 user_data) with every adaptation field set to 182. A plain build processes it without complaint — the reads are silent — so a sanitizer build is needed to see it:

# in linux/
./build -debug     # adds -fsanitize=address

For #3, the same file with adaptation_field_length set to 183 instead of 182 triggers the tspacket overflow. Happy to attach that variant too, or the generator script, if useful for the regression suite.

Suggested fix

Bounds-check pointer_field against buffer_length before the subtraction in both parse_PAT and parse_SDT, and require payload.length >= 14 before the PES/PTS read in ts_readstream (get_pts() reads through buffer[13]).

The tree already has exactly this guard at ts_tables_epg.c:1642:

if ((size_t)pointer_field + 1 < (size_t)ctx->epg_buffers[i].buffer_length)

PR follows.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions