A Vibecoded Fuzzer Found FFmpeg's 2024 Bug All Over Again
TL;DR
On August 27 a developer going by daedalus filed FFmpeg issue 24290: a 21-byte file that makes vpk_read_packet divide by zero and take down any process linked against libavformat. The finder was fuzzer-tool, an MIT-licensed Python fuzzer whose repository is 71 days old, carries 1,181 commits, ships 157,846 lines of Python, and contains an AGENTS.md with 30 numbered rules written for a coding agent. It took 495,211 executions and 10 hours 43 minutes to reach the crash. The twist is the interesting part: OSS-Fuzz found this exact defect and it was discussed on ffmpeg-devel in November 2024. Nobody merged a fix. This time a maintainer had PR 24297 open 12 hours and 14 minutes after the report.
The bug is three lines of arithmetic
The Sony PS2 VPK demuxer in FFmpeg handles the final audio block specially. At libavformat/vpk.c:89 it does this:
unsigned size = vpk->last_block_size / par->ch_layout.nb_channels;
unsigned skip = (par->block_align - vpk->last_block_size)
/ par->ch_layout.nb_channels;
If nb_channels is zero, the CPU raises SIGFPE and the process is gone. No heap grooming, no ROP chain, no memory corruption. Just an integer divide against a value nobody guarded.
The reason it can be zero is the part worth reading twice. vpk_read_header does validate the channel count and rejects anything at or below zero. Then avformat_find_stream_info tries to open the adpcm_psx decoder, that open fails, and the failure path resets the codec parameters, which blanks the channel layout that the header code just approved. The demuxer's own packet reader then divides by the blank.
Think of a bouncer checking IDs at the door and stamping every hand. A second employee, whose job is to confirm what kind of party this is, gives up halfway and wipes all the stamps off on the way out. The bartender downstream still splits the tab by the number of stamped hands, and there are now zero of them.
Severity is honest in the report: medium. It is a reliable denial of service against anything calling avformat_open_input plus av_read_frame on untrusted data, and nothing more. The finder said so explicitly rather than dressing it up as a code execution primitive, which already puts the write-up ahead of a large fraction of the security mail that lands on maintainers.
OSS-Fuzz got here first, in 2024
Eleven hours after the report, FFmpeg maintainer Jun Zhao replied with a single line pointing at an ffmpeg-devel thread from November 2, 2024. In it, Kacper Michajlow describes an OSS-Fuzz finding with the same root cause, in the same demuxer, traced through the same codec_parameters_reset path, and proposes the same two candidate fixes: guard the demuxer, or stop avformat_find_stream_info from overwriting parameters it could not establish.
Nearly 22 months later, the bug was still live in FFmpeg 7.1.3.
PR 24297 is 32 added lines and 3 deleted across 3 files. It stops libavformat from clobbering a valid container channel layout when decoder init fails, adds the channel check in the VPK last-block path, and lands a FATE test built on the 21-byte sample. That is both options from the 2024 thread, implemented together. At the time of writing it is open with zero of one required approvals, so if you run FFmpeg against untrusted media, this is still live.
What the thing that found it actually is
fuzzer-tool is not a wrapper around AFL++. It is a coverage-guided fuzzer written from scratch in Python with a C shim, and its feature list reads like somebody worked through two decades of fuzzing literature with a checklist.
- Scheduling: more than 40 strategies, including Thompson sampling with Brier calibration, MOpt particle swarm, EXP3, GP-UCB with an RBF kernel, replicator dynamics, and an Elo rating layer that arbitrates between them while the losers keep running in shadow mode.
- Information-theoretic scoring: mutual information between byte position and coverage, Renyi entropy weighting for rare edges, transfer entropy for directional byte to edge causality, and Shapley values for operator credit assignment.
- Checksum learning: Berlekamp-Massey recovery of unknown linear checksum polynomials, so targets with proprietary CRCs stop being opaque walls.
- Grammar mutators for roughly 20 formats, including ISO-BMFF box nesting, H.264 and H.265 NAL units, MPEG-TS, ADTS, Ogg, FLV, ASF, and raw x86-64 and ARM instruction streams.
- Directed fuzzing via the exact AFLGo power schedule with a pure-Python DWARF path, plus optional Z3 constraint solving over cmplog pairs.
Note the second bar. 72,298 of the 157,846 Python lines are tests, spread across 285 test files. Whatever you think of agent-written code, the ratio is not the one people usually complain about, and the repository's AGENTS.md requires a falsification test and an adversarial test for every new function. That same file also contains rule 8, "Do not nuke the repo," and rule 22, which grants permission to run tests the user says are hanging only if you have solved the halting problem. Somebody has been through a few bad afternoons.
The caveat the author put in his own README
This is the line that makes the project credible:
This is the most complex fuzzer from an information-theory standpoint, and also the slowest raw-throughput. The tradeoff is speed for edge-discovery novelty. For production fuzzing at scale, AFL family fuzzers remain the best choice.
That is the correct read. Fuzzing is overwhelmingly an executions-per-second game, and a Python fuzzer loses that game badly no matter how clever the bandit is. Ten hours and 495,211 executions is a rounding error next to what an AFL++ node does overnight. The trophy case is two entries: this FFmpeg SIGFPE and an assertion crash in the author's own fgrep. A separate findings doc records a second FFmpeg crash, a 46-byte PGS subtitle input that reaches a live av_assert0(0) and is rated high severity.
Why this report landed and Google's did not
In November 2025 FFmpeg publicly called Google's Big Sleep submissions "CVE slop" and said that companies with highly paid engineers were pushing work onto volunteers, adding that if Google wanted to reduce risk it should write and submit patches. The complaint was never that machines find bugs. It was that a machine-generated finding with a clock attached and no patch is a bill, not a contribution.
Compare the artifact here. Issue 24290 arrives with the faulting line, a full trigger chain through probe, header and packet read, a hex dump of all 21 bytes with the trigger offset annotated, a GDB backtrace, an exploitability table that argues the severity down rather than up, a suggested guard matching the demuxer's existing validation style, and a ready-to-paste regression test. A maintainer read it, connected it to a two-year-old thread, and wrote the patch overnight.
The reusable lesson for anyone pointing an agent at somebody else's codebase: the model can generate the finding, but the thing that gets it merged is the packaging around it. Volume without packaging is how you get told to go away.
Key Takeaways
- A 21-byte VPK file still raises SIGFPE in FFmpeg 7.1.3 at
libavformat/vpk.c:89. PR 24297 is open, not merged, so anything parsing untrusted media is still exposed. - OSS-Fuzz reported the same root cause on ffmpeg-devel in November 2024 with two proposed fixes. It sat for nearly 22 months. Bug discovery was not the constraint.
- fuzzer-tool is 157,846 lines of Python and 1,181 commits in 71 days, MIT licensed, with 72,298 of those lines being tests. It is worth reading as a catalogue of fuzzing scheduling techniques even if you never run it.
- The author's own README says AFL family fuzzers remain the right call for production scale. Believe the caveat, and treat this as an exploration tool rather than a throughput tool.
- The difference between an accepted AI-assisted bug report and "CVE slop" is root cause, a minimal repro, an honest severity, a suggested fix, and a regression test. All five, or expect silence.
Sources: FFmpeg issue 24290, FFmpeg PR 24297, ffmpeg-devel, November 2024, daedalus/fuzzer on GitHub, Hacker News discussion, It's FOSS on FFmpeg and Google Big Sleep