Skip to content

[FIX] Prevent 32-bit integer overflow in process_avc_sample() bounds … - #2453

Open
yasar710 wants to merge 2 commits into
CCExtractor:masterfrom
yasar710:patch-2
Open

yasar710 wants to merge 2 commits into
CCExtractor:masterfrom
yasar710:patch-2

Conversation

@yasar710

@yasar710 yasar710 commented Nov 4, 2026

Copy link
Copy Markdown

Fixes #2443
Fixes #2449

In process_avc_sample(), the bounds check i + nal_length <= previous_index || i + nal_length > s->dataLength was evaluated in 32-bit unsigned arithmetic. A crafted nal_length near UINT32_MAX wraps around into (previous_index, s->dataLength], bypassing both checks and causing a heap-buffer-overflow read at s->data[i] (src/lib_ccx/mp4.c:99) and inside do_NAL() (src/lib_ccx/mp4.c:100).

Perform the bounds-check addition in 64-bit unsigned arithmetic (uint64_t) so i + nal_length cannot wrap around.

In raising this pull request, I confirm the following (please check boxes):

Reason for this PR:

  • This PR adds new functionality.
  • This PR fixes a bug that I have personally experienced or that a real user has reported and for which a sample exists.
  • This PR is porting code from C to Rust.

Sanity check:

  • I have read and understood the contributors guide.
  • I have checked that another pull request for this purpose does not exist.
  • If the PR adds new functionality, I've added it to the changelog. If it's just a bug fix, I have NOT added it to the changelog.
  • I am NOT adding new C code unless it's to fix an existing, reproducible bug.

Repro instructions:

  1. Build ccextractor with AddressSanitizer (-fsanitize=address).
  2. Run ccextractor crash-input.bin using the malformed MP4 sample attached in [Security] Heap buffer overflow (OOB read past GPAC sample buffer) in process_avc_sample() at mp4.c:99 when parsing a crafted MP4 file #2443 / [Security] Heap buffer overflow (OOB read past GPAC sample buffer) in process_avc_sample() at mp4.c:99 when parsing a crafted MP4 file #2449.
  3. Without this fix, process_avc_sample() aborts with a heap-buffer-overflow at src/lib_ccx/mp4.c:99. With this fix, the 64-bit bounds check catches the overflowed nal_length, logs the corrupted sample message, and exits cleanly.

…check (CCExtractor#2443)

Fixes CCExtractor#2443
Fixes CCExtractor#2449

In `process_avc_sample()`, the bounds check `i + nal_length <= previous_index || i + nal_length > s->dataLength` was evaluated in 32-bit unsigned arithmetic. A crafted `nal_length` near `UINT32_MAX` wraps around into `(previous_index, s->dataLength]`, bypassing both checks and causing a heap-buffer-overflow at `s->data[i]` (line 99) and inside `do_NAL()` (line 100).

Perform the addition in 64-bit unsigned arithmetic (`uint64_t`) so `i + nal_length` cannot wrap around.
@yasar710

yasar710 commented Nov 4, 2026

Copy link
Copy Markdown
Author

Hi @cfsmp3, just a quick note here as well:

The code fix is fully tested and ready for review (Docker, Mac, and Windows checks are all green).

The Linux CI checks are failing early here too because of the same ubuntu-latest / libgpac-dev infrastructure issue that I mentioned in my other PR (#2448).

Let me know if any changes are needed in the code!

@cfsmp3 cfsmp3 closed this Nov 10, 2026
@cfsmp3 cfsmp3 reopened this Nov 10, 2026

@cfsmp3 cfsmp3 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is Carlos' claude doing the first pass. Before Carlos takes a look, please address these issues.

The AVC fix works. An MP4 whose AVC sample ends in a 4-byte NAL length of 0xFFFFFFFF gives ASan heap-buffer-overflow at mp4.c:99 on master; this branch reports the corrupted sample and stops reading.

The fix is incomplete. The same 32-bit check, and the same overflow, exists in two more places:

  1. process_hevc_sample(), src/lib_ccx/mp4.c:154: if (i + nal_length <= previous_index || i + nal_length > s->dataLength). The same crafted file with an HEVC track still gives ASan heap-buffer-overflow at mp4.c:168 on this branch.
  2. The FFmpeg MP4 path, src/lib_ccx/mp4_rust_bridge.c:66: if (i + nal_length <= prev || i + nal_length > data_length) with unsigned int operands.

Please apply the same (uint64_t) widening to both (or the equivalent nal_length > data_length - i form), so all three NAL loops share one safe check.

@yasar710

Copy link
Copy Markdown
Author

@cfsmp3 I have applied the (uint64_t) widening to all three NAL loops as requested. Please take a look!

@ccextractor-bot

Copy link
Copy Markdown
Collaborator
CCExtractor CI platform finished running the test files on linux. 172/238 tests matched the approved output:
Report Name Tests Passed
Broken 9/13
CEA-708 2/14
DVB 0/7
DVD 3/3
DVR-MS 2/2
General 26/28
Hardsubx 1/1
Hauppage 3/3
MP4 3/3
NoCC 10/10
Options 69/86
Teletext 0/21
WTV 12/13
XDS 32/34

66 tests do not match the approved output. That is the pass/fail verdict. Whether this branch caused it is a separate question, answered below.


Compared with the tip of master — test 9878, commit 185fe3f:

  • 0 pass there and fail here
  • 0 fail there and pass here
  • 0 fail on both, with different output
  • 66 fail on both, byte for byte the same

Compared with the commit this branch was cut from: the same run as the tip of master (test 9878), so the comparison above already covers it.


No test changes behaviour relative to the tip of master: every failure above fails there too, byte for byte. The approved output for those tests is out of date, which is a baseline to review rather than a regression in this branch.

@ccextractor-bot

Copy link
Copy Markdown
Collaborator
CCExtractor CI platform finished running the test files on windows. 172/238 tests matched the approved output:
Report Name Tests Passed
Broken 9/13
CEA-708 2/14
DVB 0/7
DVD 3/3
DVR-MS 2/2
General 26/28
Hardsubx 1/1
Hauppage 3/3
MP4 3/3
NoCC 10/10
Options 69/86
Teletext 0/21
WTV 12/13
XDS 32/34

66 tests do not match the approved output. That is the pass/fail verdict. Whether this branch caused it is a separate question, answered below.


Compared with the tip of master — test 9693, commit af4b28d:

  • 0 pass there and fail here
  • 157 fail there and pass here
  • 42 fail on both, with different output
  • 24 fail on both, byte for byte the same
  • 1 have no result in that run

Fail on both but produce different output — behaviour moved even though the verdict did not:

Fail there, pass here:


Compared with the commit this branch was cut from: the same run as the tip of master (test 9693), so the comparison above already covers it.


This branch changes the behaviour of 42 test(s) relative to the tip of master. Those are the ones worth looking at; anything else in the list fails the same way on both sides.

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

Labels

None yet

Projects

None yet

3 participants