Skip to content

feat(fuzz): fuzz the media parsers untrusted publishers reach - #7313

Merged
kixelated merged 20 commits into
mainfrom
quest/m2/media-fuzz
Nov 3, 2026
Merged

kixelated merged 20 commits into
mainfrom
quest/m2/media-fuzz

Conversation

@kixelated

@kixelated kixelated commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator

Fuzz targets for the media parsers an untrusted publisher reaches, each asserting bounded heap work per input byte, wired into the nightly fuzz job, with committed regressions replayed in the merge gate. It does the fuzz-target work of quest/m2/media-fuzz.md. It also takes the parse-amplification measurement main added to that quest on 2026-10-31. The fixes that measurement calls for move to a new quest, quest/m1/fmp4-metadata-limits.md (see Base merge (2026-11-02) and Decision 7), and media-fuzz.md is deleted.

Changes

  • rs/moq-mux/fuzz (moq-mux-fuzz): fmp4, mkv, ts, flv (container importers, fed in socket-sized writes chosen by a leading byte), annexb (H.264/H.265 Annex-B and AV1 OBU splitters through TrackStream), cmaf (subscriber-side fmp4::Wire::from_init + fragment decode), catalog (hang and MSF catalog consumers read after each frame, then each rendition's container built).
  • rs/moq-rtmp/fuzz (moq-rtmp-fuzz): amf0, which reads a message through MessagePayload::amf0(), walks every value, and asserts zero allocations.
  • Bounded-work invariant: moq_mux::fuzz::run counts all heap traffic (cumulative bytes and allocation count, not just live memory) on the target's thread and fails an input above 4 MiB + 1024 B/byte or 4096 + 8 allocations/byte. Because it counts every allocation, it catches rebuffering or reparsing as well as holding memory. The constants come from what was measured: about 1.7 MB fixed setup (including the first compressed catalog and HLS groups), and about 4.6 KB per group opened by a 14-byte keyframe.
  • Target bodies and seeds live in hidden fuzz modules behind a new fuzz feature, the same pattern as moq-net and moq-quic. Seeds come from the crates' existing fixtures. fuzz::tests::{seeds_are_accepted, regressions} replay them on stable under cfg(test), so they run in the merge gate.
  • moq-mux's test-only counting allocator moved to src/allocation.rs and gained cumulative counters. The import release tests and the fuzz targets now share it. moq-rtmp's existing AMF0 allocator is reused the same way.
  • nightly.yml fuzz matrix gained the 8 targets. Both crates were added to the workspace and to every --exclude *-fuzz list (rs/justfile, sh/rs/select.sh, sh/rs/coverage.sh, and their tests).

Public API: none (hidden fuzz modules behind an off-by-default feature). Wire: none.

Findings from local fuzz runs

These come from short local runs (about 3 minutes per target, coverage-guided, stable toolchain, no ASan). Minimized inputs are in a PR comment. None are fixed here.

Target(s) Site Kind Release build
cmaf, fmp4 mp4-atom 0.16.2 sgpd decode 22-byte init grows a Vec to >3 GB RSS (OOM) memory DoS, reachable from a catalog init or fMP4 ingest
cmaf mp4-atom 0.16.2 iloc decode 31 bytes allocate 6 MB bound violation
cmaf, fmp4 mp4-atom 0.16.2 meta/iref.rs:37 subtract overflow wraps
mkv webm-iterable 0.7.1 blocks/block.rs:136,141 slice/index out of bounds panics; fixed on main by moq-dev/moq#7234, regression committed here
ts rs/moq-mux/src/container/ts/step.rs:431 (self.scan - self.at) subtract overflow, scan < at wraps, then cuts the look-ahead short
fmp4, ts, annexb h264-parser 0.4.2 sps.rs:186,197,204 add/mul overflow on SPS crop wraps
annexb, ts scuffle-h265 0.2.2 sps/mod.rs:303, sps/scaling_list.rs; scuffle-expgolomb 0.1.5 subtract overflow wraps
annexb, ts, flv moq-net group DEFLATE, from moq-mux catalog and HLS publishes each catalog republish opens a fresh compressed group with a ~200 KB zlib-rs encoder: 202 bytes of Annex-B cause 24 encoders and about 4.9 MB of allocation bound violation (work amplification)

amf0 (60M runs) and catalog (1M runs) found nothing.

Decisions

Decisions 1 to 6 were decided by the maintainer on 2026-10-30.

  1. Nightly with targets that already fail. Six of the eight new targets (fmp4, mkv, ts, cmaf, annexb, flv) fail within a minute on the findings above, and libFuzzer stops at the first failure, so until the findings are fixed those nightly jobs stay red and find nothing new.

    • ✅ (a) Land as-is. The quest's Plan puts every target in nightly, and the red jobs track the follow-ups below.
    • (b) Land, but list only catalog and amf0 in the nightly matrix, adding each other target once its finding is fixed.
    • (c) Hold this PR until the findings are fixed.
  2. DEFLATE encoder per catalog update. Is a fresh ~200 KB encoder per catalog or HLS republish a bug, or an accepted cost?

    • ✅ (a) It is a bug. Fix it here if small, otherwise in a quest. It became a quest: the encoder lives on each compressed group, so reusing it means parking a reset encoder on the track's cache account (cache::Track) and billing it to the pool while it waits. That is a change to moq-net's memory accounting on every compressed track, not a small fix to bundle into a fuzz PR.
    • (b) Accept it, and give the bound a per-catalog-update allowance.
  3. Third-party parser bugs.

  4. mp4-atom iloc (mp4-atom-iloc.md).

    • ✅ Reject zero-size extents after an item's first one, so valid files still decode.
  5. webm-iterable panic and h264/h265 SPS overflows (mkv-block-header.md, sps-overflow.md).

    • ✅ Fix them in moq-mux: validate SPS fields before calling the crate, and replace webm-iterable's block parsing with moq-mux's own block-header reader.
    • ✅ Also report them upstream (approved for these crates only): austinleroy/webm-iterable#11, ScuffleCloud/scuffle#653 (scuffle-h265), ScuffleCloud/scuffle#654 (scuffle-expgolomb). h264-parser already had therishidesai/h264-parser#9 for the same crop and width overflows, filed by another moq-mux user, so I linked it instead of filing a duplicate.
    • ✅ H.264/H.265 SPS parsing stays on its current crates, not mp4-atom; mp4-atom stays box-level. The decision named h264-reader, but moq-mux parses the H.264 SPS with h264-parser (rs/moq-mux/src/codec/h264/mod.rs:169) and uses h264-reader only for TS video timing, so the quest says "the crates moq-mux uses now". If you meant a move to h264-reader, that needs a quest edit.
  6. Merge fix: reject sgpd entry counts past the box kixelated/mp4-atom#264 (sgpd count bound).

    • ✅ Merged now without a release: squash 06a532aa. mp4-atom's release.yml runs release-plz on push, but Cargo.toml is at the already-published 0.17.0, so nothing was published. mp4-atom-bump.md now waits on a 0.17.x release containing Maybe close on disconnected element. #264.
  7. Where the fMP4 parse-amplification fixes land (decided by the maintainer on 2026-11-03). The measurement main added to the quest found 116 bytes planning 584 MB, an 8 MiB moof peaking at 667 MB, a 32 MiB moof parsing 369 MB before its refusal, and quadratic CPU in a moov's trak count.

    • ✅ (a) Land this PR as-is, with the fixes in an m1 quest, because 116 bytes cost 584 MB on any fMP4 or HLS ingest.
    • (a') Land as-is, with the fixes staying in m2.
    • (b) Fix all three here.
    • (c) Accept the numbers.

    The quest is now quest/m1/fmp4-metadata-limits.md, ranked in m1 after the media fuzz findings epic. quest mv refused the move: "refusing to move claimed quests: quest/m2/media-fuzz (feat(fuzz): fuzz the media parsers untrusted publishers reach #7313); their branches would no longer name them, so move them once the work merges or the claim is released". The move was done with git mv and the links rewritten by hand. This branch now deletes media-fuzz.md, which finishes it, and quest check --base origin/main accepts that. The new quest sets the limit values itself, rather than settling them with the m2 import-metadata-policy.md, so m1 work doesn't wait on m2.

Quests added

Under quest/m1/fuzz-findings/README.md, listed in m1's Required. I used a new epic instead of m1/security-audit, whose Goal is one external audit's findings; this epic's Goal is the nightly jobs going green.

  • mp4-atom-bump.md: waiting on a 0.17.x release containing fix: reject sgpd entry counts past the box kixelated/mp4-atom#264 (merged), then the move off 0.16.2 and its breaking changes.
  • mp4-atom-iloc.md: zero-size iloc extents (31 bytes allocate 6 MB). Fixed in mp4-atom by rejecting zero-size extents after an item's first (Decision 4).
  • mkv-block-header.md: deleted in the 2026-11-01 base merge. moq-dev/moq#7234 already replaced webm-iterable's block parsing with moq-mux's own header reader, so the quest's reproducer is committed here as a regression instead.
  • ts-step-scan.md: Ahead::scan's scan < at.
  • sps-overflow.md: the h264-parser and scuffle-h265/expgolomb overflows, validated in moq-mux before the crates run and reported upstream (Decision 5). Building the reproducers showed that the scuffle-h265 scaling-list underflow also panics in release builds, on the wrapped index.
  • catalog-encoder.md: the DEFLATE encoder per update (Decision 2).
  • quest/m1/fmp4-metadata-limits.md (not in the epic): the three fMP4 parse-amplification fixes, with the measured numbers (Decision 7).

Choices made without asking

  • One fuzz crate per package (moq-mux-fuzz, moq-rtmp-fuzz) to match the existing rs/<crate>/fuzz layout that sh/rs/fuzz.sh resolves. hang catalog parsing is fuzzed from moq-mux, which drives hang::catalog::Consumer.
  • The catalog target uses an uncompressed catalog track: inflating is moq-net's layer, ahead of any catalog parser.
  • The bound counts cumulative allocation rather than peak live memory, so it covers work as well as memory. The constants are in rs/moq-mux/src/fuzz.rs. If a regression input trips the bound once, run measures it a second time, because lazy statics allocate once per process.
  • Exposed h265::fixtures and catalog::to_msf crate-internally under the fuzz feature, for seeds.
  • Crash inputs are not committed under regressions/, because each would fail the merge gate until its fix lands. They go in with their fixes. The one exception is regressions/mkv/short-block-header, whose fix landed on main first.

Checks

On 8234e5b410 (main at f68512665e). Since then, the head changed only by the quest move and a clean merge of main's be39188000 (moq-net, merged without conflict). On c1b09990da, quest check --base origin/main reports 558 documents ok.

  • quest gate just check origin/main: every module passed except test rs, and the flake check was skipped (this host's Nix builder cannot create user namespaces). test rs stopped at the first failure each run, in tests this PR does not touch: moq-c test::raw_track_subscription_options_and_update twice, which passes 6 of 6 times alone on both this branch and main and is plausibly what open moq-dev/moq#7385 fixes, and then a moq-relay uring test that hit the host's RLIMIT_MEMLOCK.
  • So I ran just rs test --workspace --no-fail-fast (excluding the fuzz crates): 11045 passed, and the 11 failures are all moq-uring integration tests failing io_uring setup on the same shared RLIMIT_MEMLOCK (8 MiB). This includes both crates' fuzz::tests::{seeds_are_accepted,regressions}; moq-mux and moq-rtmp alone pass 2234 of 2234.
  • quest check --base origin/main: 555 documents ok.
  • Nightly CI's just rs fuzz path (nightly toolchain plus ASan) has still not run on this branch.

Base merge (2026-11-02)

The PR conflicted with main from 2026-11-01 until now. Auto-merge was disabled before pushing, and the maintainer requeues the reviewed head. Merged origin/main three times (c107623a9d at 41f60ec46e, 8234e5b410 at f68512665e, then c1b09990da at be39188000, which merged cleanly):

  • quest/m2/media-fuzz.md (the only conflict). moq-dev/moq#7468 added a requirement: record the peak allocation for a maximal moov, a maximal moof of 4-byte entries, and a field-less moof at the sample bound, then fix each or accept it with the number. The fuzz targets cannot measure this, because libFuzzer inputs stay a few KiB, far below the 32 MiB bound. So I measured it by hand: a throwaway test fed each input through fmp4::Import::decode, with moq-mux's counting allocator extended to track peak live bytes. The test is not committed.

    Input Size Peak live heap
    field-less truns declaring 2^21 samples (MAX_FRAGMENT_SAMPLES), then an mdat header 116 B 584 MB (the plan alone, before any mdat byte)
    2^21 4-byte trun entries, then an mdat header 8 MiB 667 MB (80x); 684 MB with passthrough
    4-byte entries filling MAX_HELD_BYTES 32 MiB 369 MB (11x), refused as TooManySamples only after mp4-atom parses it
    moov of repeated audio traks (477 B each) at 0.24, 0.48, and 0.95 MB 35x peak; quadratic CPU: 0.25 s, 0.9 s, 3.9 s, and 96 MB, 360 MB, 1.4 GB allocated (extrapolates to over an hour and above 1 GB at 32 MiB)

    The fixes became quest/m1/fmp4-metadata-limits.md (Decision 7), linked from import-metadata-policy.md in place of media-fuzz.md. This branch's sentence in that quest, which claimed the fuzz targets measure this amplification, was dropped.

  • .config/hakari.toml: moq-dev/moq#6985 added the workspace-hack. moq-mux-fuzz and moq-rtmp-fuzz join moq-net-fuzz and moq-quic-fuzz in traversal-excludes; cargo hakari generate --diff and sh/rs/hakari.py pass.

  • sps-overflow.md: moq-dev/moq#7398 moved H.264 SPS parsing from h264-parser to h264-reader. The quest now says so, and points the H.264 half at sps-fuzz-h264-reader.md.

  • mp4-atom-bump.md: its prose wait for an mp4-atom 0.17.x release moved into a ## Waiting section, to match quest's new format. crates.io's latest is still 0.17.0.

  • The fuzz bodies compile unchanged against main's moq-mux (fMP4 refusals that stay terminal, MKV leaf bounds, h264-reader).

Base merge (2026-11-01)

quest land ejected bb2bcbeb8c with a conflict, and the optional Quest check failed on links to quests main had finished. Merged origin/main twice (206a5fccb9 at d8288e6934, then c3eccc4603 at d52d55ac69):

  • Took main's deletions of quest/m1/security-audit/fmp4-stream-mdat.md (moq-dev/moq#7232), quest/m1/mkv-truncated-finish.md (moq-dev/moq#7608), and the quest/m1/security-audit epic (moq-dev/moq#7611). This branch had only edited their links to media-fuzz.md.
  • fuzz-findings/README.md dropped its Related links to the finished MKV and fMP4 streaming quests.
  • moq-dev/moq#7234 replaced webm-iterable's block parsing with moq-mux's own block_header (rs/moq-mux/src/container/mkv/import.rs), so the mkv reproducer (u6OBgA==) now fails with InvalidSimpleBlock. Committed it as rs/moq-mux/fuzz/regressions/mkv/short-block-header and deleted mkv-block-header.md with quest rm.
  • quest/m2/import-metadata-policy.md and quest/m2/sps-fuzz-h264-reader.md stopped linking media-fuzz.md: the first names the fuzz targets in its Plan, and the second drops it from Required, since this PR finishes it.
  • da4ac94df3: an earlier mechanical merge left a 153-character line in sh/rs/select.test.py, which failed check python. It now builds the --exclude *-fuzz list from a FUZZ_EXCLUDES constant.
  • CodeRabbit's thread on quest/m1/security-audit/README.md was valid, and e29bc0d820 restored the link. The second merge then took main's deletion of that whole epic, so the file no longer exists.

Base merge (2026-10-31)

quest land ejected bd91a2d749 with a conflict in .github/workflows/nightly.yml. Merged origin/main (a1169021b6) in 65647df8b7:

  • nightly.yml: main added moq-net's ingest and request targets to the fuzz matrix on the same line; resolved by hand to the union. Target names remain unique across rs/*/fuzz/fuzz_targets/.
  • moq-dev/moq#7308 moved moq-mux to mp4-atom 0.17.0, which already fixes the iref overflow in the findings table. c03b3088fc rewrites mp4-atom-bump.md to match: it still waits on a 0.17.x release with fix: reject sgpd entry counts past the box kixelated/mp4-atom#264 (crates.io's latest is 0.17.0), but no longer carries 0.17's breaking changes, so it is now [XS].
  • moq-dev/moq#7320 added quest/m2/wire-fuzz-coverage.md, linking the media-fuzz.md quest this PR deletes. 6bcdc65970 points that link at the quest/m1/fuzz-findings epic.

Review

  • Harness: Codex via quest review, gpt-6.1-sol, medium effort, session 01a19fab.
  • Round 1 on 8234e5b410 (both 2026-11-02 base merges, the hakari excludes, and the quest rewrites; base f68512665e): 0 findings.
  • Round 2 on c1b09990da (the quest move to m1, Decision 7, and the clean merge of main at be39188000; base be39188000): 0 findings.
  • GitHub: every review thread is resolved. The latest automated reviews, on c3eccc4603, raised nothing new.

(Written by Claude Opus 5.5)

🤖 Generated with Claude Code

kixelated and others added 5 commits October 30, 2026 01:15
Quest-Claim: ee02af7b1680a382
Add moq-mux-fuzz (fmp4, mkv, ts, flv, cmaf, annexb, catalog) and
moq-rtmp-fuzz (amf0). Each media target bounds heap traffic linearly in
the input through a per-thread counting allocator; amf0 asserts the
borrowed receive views allocate nothing. The bodies live in hidden `fuzz`
modules so each crate's tests replay seeds and committed regressions on
stable, and nightly.yml runs every target.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	sh/rs/select.test.py
@kixelated kixelated changed the title Untrusted media parsers have fuzz targets feat(fuzz): fuzz the media parsers untrusted publishers reach Oct 30, 2026
@kixelated

Copy link
Copy Markdown
Collaborator Author

Minimized reproduction inputs for the findings in the PR body, base64. Write one to a file and replay it with just rs fuzz <target> -- <file> (or the target's stable test body). Each one includes the target's selector bytes.

cmaf: mp4-atom-sgpd (22 bytes)
ABQAAAAAc2dwZAAAAAD/AP////8AAA==
cmaf: mp4-atom-iloc (31 bytes)
AB0AAAAcaWxvYwAAcwAAACAAyP////8AAAD///8AAA==
cmaf: mp4-atom-iref (514 bytes)
AgAAAAG7dHJhawAAAGi3f4EAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAABAAAAAP//////////AAAAAAAAAAAAAAAAUAAAAAABAAAA
AAAAACwAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAABAAFAAAAA8AAAAAABS21kaWEAAAAsbWRoZAEAAAAAAAAAAAAAAAAAAAAAAAAA
AAAD6AAAAAAAAAAAAAAAAAAAACFoZGxyAAABAAAAAAB2aWRlAAAAAAAAAAAAAAAAAAAAAPZtaW5mAAAAJGRpbmYAAAAcaXJlZgAA
AAAAAQAAAAAFABRzdHN6AAAA2CcAAAAAAAB2bWhyYWYAAAAAAAAAAAAAdLYAAQIDBAUDBAUEBQAAFHMAANgnAAAAAAAAdm1ocmFm
AAAAAAAAAAAAAHS2AAECAwQFAAAUc3RzegAAANgnAAAAAAAAdm1ocmFmAAAAAAAAAAAAAHS2AAECAwQFAwQFBAUAABRzdHN6AAAA
2CcAAAAAAAB2bWhyYWYAAAAAAAAAAAAAdLYAAQIDBAUAAAAAAAAAAAAAdAAAAAAAAAAAdLYAAQIDBAUA2CcAAAAAAAB2bWhyYWYA
AAAAAAAAAAAAdLYAAQIDBAUDBAUEBQAAFHN0c3oAAADYJwAAAAAAAHZtaHJhZgAAAAAAAAAAAAB0tgABAgMEBQ==
mkv: webm-iterable (4 bytes)
u6OBgA==
ts: ts-step (567 bytes)
PEdAABAAALANAAHBAAAAAfAAKrEEsv//////////////////////f///OAAAAAAAAAD/////////////////////////////////
/////////////////////wEAAAAAAAAU//////////////8AAAAAAAAAAP//////////////////////////////////////////
////////////////////////////////////////////////////R1AAEAACsBgAAcEAAOEA8AAk4QDwBgUESEVWQ8ueAFL/////
/////////////////////////////////////////////////////////////////////////////yIAAAAAAACA////////////
//////////////////////////////////////////8BABH0////////////////////////////////////////////////////
////R0EAMIuUdhzZie6TxqWXqpci5EdMSRcCZK7nS1QWesoxb8BuAQAAAAAAAAAR9PCL////7pPGpZeqlyLkR0xJFwJkrudLVBZ6
yjFvwG7YVfnqaEcBABH0////8UwAK9/8oJiAgAQiDExhdmM2My4xLjEwMdwATGF2YzYzLjEuMU3atxFQChtKO0xB0TjJ41LL1UuR
ciOmJJ8XAmSu50tUFnrKMW/AbthV+epo9PCL8Iv////uk8all6qXIuRH
fmp4: h264-parser (1284 bytes)
MQAABQNtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAAAAABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAA
AAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACFXRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAEA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAFAAAAAtAAAAAAAbFtZGlh
AAAAIG1kaGQAAAAAAAAAAAAAAAAAAF3AAAAAAFXEAAAAAABCaGRscgAAAAAAAAAAdmlkZQAAAAAAAAAAAAAAAChDKSAyMDA3IEdv
ZmdsZSBJbmMuIHYwOC4xMy4yMDA3LgAAAAFHbWluZgAAABR2bWhkAAAAAQAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAAB
AAAADHVybCAAAAABAAABB3N0YmwAAAC7c3RzZAAAAAAAAAABAAAAq2F2YzEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAFAALQAEgA
AABIAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY//8AAAAxYXZjQwFkAB//4QAZZ2QAH6wkhP9AFuwx
Lm1wNC0SAAAAAAAAAAEABWgNzciwAAAAEHBhc3AAAAABAAAAAQAAABRidHJ0AAAAAAAeYncAHmJ3AAAAEHN0dHMAAAAAAAAAAAAA
ABBzdHNjAAAAAAAAAAAAAAAUc3RzegAAAAAAAAAAAAAAAAAAABBzdGNvAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEA
AAAMdXJsIAAAAAAAAAAAAABibAAAAHtzdHNkAAAAAAAAAAEAAABrbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAz
ZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAeqTAAfqkwWAgIACEhAGgICAAQIAAAAUYnRydAAAAAAAAeqTAAHqkwAAABBzdHRz
AAAAAAAAAAAAAAAQc3RzYwAAAAAAAAAAAAAAFHN0c3oAAAAAAAAAAAAAAAAAAAAQc3RjbwAAAAAAAAAAAAAASG12ZXgAAAAgdHJl
eAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAFAALQAEgAAABIAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY//8A
AAAxYXZjQwFkAB//4QAZZ2QAH6wkhAFAFuwxLm1wNC0SAAAAAAAAAAEABWgNzciwAAAAEHBhc3AAAAABAAAAAQAAABRidHJ0AAAA
AAAeYncAHmIAAAAQc3R0cwAAAAAAAAAAAAAAEHN0c2MAAAAAAAAAADoAAAACAAAAAQAABAAAAAAJAgAAAAAAAAABAAAAAAAAAAAA
AAAAAAAAAAUAAtAASAAAAEgAAAAAAAAAAQAAAHBsAAAAAAAAAAAALGlsc3QAAAAkqXRvAG8AABxkYXRhAAAAAQAAAABMYXZmNjEu
MS4xMDAAAABsbW9vZgAAABBtZmhkAAAAAAAAAAEAAABUdHJ0AAAAAAAeYncAHmJ3AAAAEHN0dHMAAAAAAAAAAAAAABBzdHNjAAAA
AAAAAAAAACb0
annexb: scuffle-h265 (31 bytes)
FgAAAUIBAQD///8AAAQAgABdcHRzypo7/wAAASavgA==
annexb: scuffle-expgolomb (196 bytes)
FgAAAAFGAVAuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLgAAAPxAAQwB//8BYAAAAwCQAeobPQP//wFf+AADAJAAAAMAAwBdJgAAAC0W
WVmkkyvAWgIAAAMACQAAAAFCAQEBYAAAAyeQAAADAAADAF2gAoCALRZZWTrAK5NaWgIAAAMAAAAAAAAAABACAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAANIAAwA8EAAAAID/////////AUQBwXK0YkAAAAABJgGvgA==
annexb: deflate-catalog (202 bytes)
AAAAAAEJ8OZ5AAAAAAAAAAAAAHSlcgAAAAD///89//9ifv8AACQAAAAAAWdlAAAI/////2WIhPgAAMoAAAAAAAAAAAABZYiE+AAA
AAEJ8AAAAAFnZQAACP////9/7Pj4+PgA/v8BAAAvIQj//wFGAVAAAAD/AWAAckACAScAAAABRgE3kwHCsAAAAAFliIT4AAAAAQnw
AAAAAWdlAP0I/////3/s+Pj4+AD+/wEAAGdlCP////8AAMYA7jPIsAAAAAFliIQhAADGAA==

(Written by Claude Opus 5.5)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated marked this pull request as ready for review October 30, 2026 20:51
@coderabbitai

coderabbitai Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Walkthrough

Adds seven media fuzz targets to moq-mux and an AMF0 target to moq-rtmp. Both crates gain seed generation and stable regression replay; the media targets also check allocation and input-size bounds. Cargo Fuzz packages, seed-export examples, and nightly workflow entries connect the targets to fuzzing runs. Workspace and coverage tooling now excludes the new fuzz packages from general checks. Quest documents record related findings and update links.

Merge Risk: Low · up to 6bcdc

Restore the audit page’s link to the media fuzz findings. The change is otherwise mergeable with the documented fuzzing follow-up.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 73.91% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely identifies the main change: adding fuzzing for media parsers reachable by untrusted publishers.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The pull request description directly matches the changeset. It describes the new media-parser fuzz targets, allocation bounds, nightly integration, regression tests, findings, and related quest updat…
Full details: Description check

Explanation

The pull request description directly matches the changeset. It describes the new media-parser fuzz targets, allocation bounds, nightly integration, regression tests, findings, and related quest updates.

Full details: Docstring Coverage

Explanation

Docstring coverage is 73.91% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 23 functions across 26 files. (23 skipped: 23 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Simplify code
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.
Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 683ef2ba-1c9b-47c4-b3a3-bf30fbdf0a73
Commits

Reviewing files that changed from the base of the PR and between a116902 and 6bcdc65.

Files ignored due to path filters: 1
  • Cargo.lock is excluded by !**/*.lock
Files selected for processing: 53
  • .github/workflows/nightly.yml
  • Cargo.toml
  • quest/m1/README.md
  • quest/m1/cut-audit/README.md
  • quest/m1/fuzz-findings/README.md
  • quest/m1/fuzz-findings/catalog-encoder.md
  • quest/m1/fuzz-findings/mkv-block-header.md
  • quest/m1/fuzz-findings/mp4-atom-bump.md
  • quest/m1/fuzz-findings/mp4-atom-iloc.md
  • quest/m1/fuzz-findings/sps-overflow.md
  • quest/m1/fuzz-findings/ts-step-scan.md
  • quest/m1/security-audit/README.md
  • quest/m1/security-audit/fmp4-stream-mdat.md
  • quest/m2/README.md
  • quest/m2/media-fuzz.md
  • quest/m2/wire-fuzz-coverage.md
  • rs/justfile
  • rs/moq-mux/Cargo.toml
  • rs/moq-mux/examples/fuzz-seeds.rs
  • rs/moq-mux/fuzz/.gitignore
  • rs/moq-mux/fuzz/Cargo.toml
  • rs/moq-mux/fuzz/README.md
  • rs/moq-mux/fuzz/fuzz_targets/annexb.rs
  • rs/moq-mux/fuzz/fuzz_targets/catalog.rs
  • rs/moq-mux/fuzz/fuzz_targets/cmaf.rs
  • rs/moq-mux/fuzz/fuzz_targets/flv.rs
  • rs/moq-mux/fuzz/fuzz_targets/fmp4.rs
  • rs/moq-mux/fuzz/fuzz_targets/mkv.rs
  • rs/moq-mux/fuzz/fuzz_targets/ts.rs
  • rs/moq-mux/fuzz/regressions/README.md
  • rs/moq-mux/src/allocation.rs
  • rs/moq-mux/src/catalog/mod.rs
  • rs/moq-mux/src/catalog/producer.rs
  • rs/moq-mux/src/codec/h265/mod.rs
  • rs/moq-mux/src/fuzz.rs
  • rs/moq-mux/src/import/mod.rs
  • rs/moq-mux/src/lib.rs
  • rs/moq-rtmp/Cargo.toml
  • rs/moq-rtmp/examples/fuzz-seeds.rs
  • rs/moq-rtmp/fuzz/.gitignore
  • rs/moq-rtmp/fuzz/Cargo.toml
  • rs/moq-rtmp/fuzz/README.md
  • rs/moq-rtmp/fuzz/fuzz_targets/amf0.rs
  • rs/moq-rtmp/fuzz/regressions/README.md
  • rs/moq-rtmp/src/fuzz.rs
  • rs/moq-rtmp/src/lib.rs
  • rs/moq-rtmp/src/rml/amf0/allocation.rs
  • rs/moq-rtmp/src/rml/amf0/mod.rs
  • sh/coverage.test.py
  • sh/rs/coverage.sh
  • sh/rs/fuzz.sh
  • sh/rs/select.sh
  • sh/rs/select.test.py
Files with no reviewable changes: 4
  • quest/m2/README.md
  • quest/m1/security-audit/fmp4-stream-mdat.md
  • quest/m2/media-fuzz.md
  • quest/m1/cut-audit/README.md

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 1 remain after this review.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-30T21:46:04.071025Z bd91a2d New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Automated review by review (OpenAI)

Reviewed commit: c2bfb3f5e6289905db1f4c9fb69a515d29d6b29d.

No new actionable bugs found in the changed code. Sharing target bodies between libFuzzer and stable regression tests is a sound direction; the thread-local allocation accounting and existing target-discovery workflow avoid unnecessary parallel harnesses.

The already documented parser failures and allocation amplification remain follow-up work, not new findings from this review (see the reproduction inputs). The acknowledged red nightly targets will stop at their first failure until those fixes land.

Verification: static inspection of target bodies, allocation accounting, seeds/replay, feature/workspace wiring and nightly integration. I did not run tests, builds, fuzzing or the nightly ASan path.

kixelated added a commit to kixelated/mp4-atom that referenced this pull request Oct 30, 2026
* fix: reject sgpd entry counts past the box

A version 0 entry of an unknown grouping type reads to the end of the
box, so every later entry decodes empty for free, and a 20-byte sgpd
declaring 0xffff0000 entries grew a Vec past 3 GB. Every entry takes at
least a byte, so a count above the remaining bytes is now OutOfBounds.

Found by moq's media fuzz targets (moq-dev/moq#7313).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep the sgpd entry capacity cap

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
iloc rejects zero-size extents after an item's first; webm-iterable and
the SPS parsers are fixed in moq-mux and reported upstream; mp4-atom#264
merged, so the bump waits on a 0.17.x release containing it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

Landing bd91a2d7495f39a043876845b01bb70e9f845740 via quest land.

  • Checks: 15/15 passed on the head. quest check --base origin/main (main at 1d8bf24998): 673 documents ok.
  • Reviews: the automated OpenAI review on c2bfb3f5e6 reported no actionable findings; Codex reacted 👍 on the head. No unresolved threads, so nothing changed in this merge pass.
  • Local review record in the body names this head (Codex, 0 findings).
  • Decisions 1–6 in the body were settled by the maintainer on 2026-10-30; the six nightly fuzz targets stay red until the quest/m1/fuzz-findings quests land.

(Written by Claude Opus 5.5)

@kixelated
kixelated enabled auto-merge (squash) October 31, 2026 04:59
@moq-bot
moq-bot Bot disabled auto-merge October 31, 2026 09:24
@moq-bot moq-bot Bot removed the quest:merge label Oct 31, 2026
@moq-bot

moq-bot Bot commented Oct 31, 2026

Copy link
Copy Markdown
Contributor

quest land removed this pull request from the queue: Conflicts with the base: .github/workflows/nightly.yml
.

kixelated and others added 3 commits November 1, 2026 20:50
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take main's deletions of quest/m1/mkv-truncated-finish.md (finished by
#7608) and the quest/m1/security-audit epic (finished by
#7611); this branch had only edited their media-fuzz links.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Automated review by review (OpenAI)

Reviewed commit: c3eccc46032566cdf24bef9b3efe92f2e475b417, compared with my prior review at c2bfb3f5e6289905db1f4c9fb69a515d29d6b29d, accounting for the main merges.

No new actionable bugs found. The meaningful addition is rs/moq-mux/fuzz/regressions/mkv/short-block-header: replaying the formerly crashing input through the existing stable regression harness is the right direction, without a second test framework. The inherited MKV block-header fix bounds the header reads; the shared fuzz bodies and allocation accounting are unchanged. Workspace exclusions and the nightly matrix preserve the media targets alongside main's additions.

The earlier documented MKV panic is now covered by the new regression; the other documented parser/allocation issues remain tracked follow-up work, not new findings. The prior CodeRabbit documentation finding is addressed by the removal of its owning audit file (see discussion_r4431284114).

Verification: static comparison of PR-relative patches, regression discovery and MKV header handling. I did not run tests, builds or fuzzing. The GitHub connector refused the non-UTF-8 regression blob, so its exact bytes and runtime result were not independently verified.

@kixelated
kixelated enabled auto-merge (squash) November 2, 2026 05:44
@kixelated
kixelated disabled auto-merge November 3, 2026 03:58
kixelated and others added 2 commits November 2, 2026 20:17
main's #7468 added a parse-amplification measurement to quest/m2/media-fuzz.md,
which this branch deletes. The measurement found three fMP4 cases that need
fixing, so the quest now keeps that remaining work with the numbers instead
of being deleted, and its m2 and import-metadata-policy links stay.

- .config/hakari.toml: exclude moq-mux-fuzz and moq-rtmp-fuzz from the
  workspace-hack (#6985), like moq-net-fuzz and moq-quic-fuzz.
- sps-overflow.md: H.264 now parses with h264-reader (#7398).
- mp4-atom-bump.md: its prose wait moves into a ## Waiting section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Automated review by review (OpenAI)

Reviewed commit: 8234e5b, compared with c3eccc4 accounting for main merges.

No new actionable bugs found. Adding the media fuzz crates to Hakari's traversal exclusions matches existing harness isolation. Retaining the fMP4 metadata quest with explicit memory and time goals is sensible: bounded input alone does not bound parsed state or planning cost. The harness and MKV regression remain unchanged; prior parser/allocation issues remain follow-up work.

Verification: static comparison of PR-relative patches, Hakari configuration, manifests and follow-up design. Tests, builds, fuzzing and allocation measurements were not independently run.

kixelated and others added 2 commits November 2, 2026 21:19
The maintainer picked landing #7313 as-is on 2026-11-03, with the parse
amplification fixes in an m1 quest, because 116 bytes cost 584 MB on any fMP4
or HLS ingest. This branch now finishes quest/m2/media-fuzz.md and adds
quest/m1/fmp4-metadata-limits.md for the three fixes. `quest mv` refused the
move while #7313 claims the old path, so it was done with git mv and the links
rewritten by hand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated added this pull request to the merge queue Nov 3, 2026

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Automated review by review (OpenAI)

Reviewed commit: c1b0999

No new actionable bugs found. Promoting the measured fMP4 amplification fixes to m1 and letting that quest set bounds independently of the later documentation policy is a coherent response to the reported exposure. The fuzz targets and previously documented failures are unchanged.

Static GitHub comparison only; I did not run tests, fuzzing or allocation measurements.

Merged via the queue into main with commit 5583782 Nov 3, 2026
8 of 9 checks passed
@kixelated
kixelated deleted the quest/m2/media-fuzz branch November 3, 2026 05:48
kixelated added a commit to kixelated/mp4-atom that referenced this pull request Nov 3, 2026
When the offset, length and index sizes are all 0, an iloc extent reads no
bytes, so a 28-byte box decoded two items of 65,280 extents each and
allocated about 6 MB. Only an item's first extent may now be zero-size;
a second is OutOfBounds. A lone zero-size extent, naming the whole data
reference, still decodes, even at the end of the box.

Found by moq's media fuzz targets (moq-dev/moq#7313).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant