CCExtractor / CCExtractor/ccextractor

[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)

Open
#2,348 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C
Stars
903
Forks
589
Avg merge
3d 2h
Merged PRs (30d)
10

Description

CCExtractor version: master (2364994a)

# 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

```c
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

```c
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.

```bash
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:

```bash
# 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`:

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

PR follows.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.