Repository navigation
[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
Description
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.srtsha256 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=addressFor #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