Skip to content

Fuzz the parser, the tokeniser and the filter chain from outside - #17

Merged
tannevaled merged 1 commit into
mainfrom
feat/fuzz-the-parser
Aug 27, 2026
Merged

Fuzz the parser, the tokeniser and the filter chain from outside#17
tannevaled merged 1 commit into
mainfrom
feat/fuzz-the-parser

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Three of the four entry points a caller actually uses had no fuzz target:
ParseObject, Operations and Decode. This adds them, in the external test
package so they exercise the API as a caller sees it, together with two things
that make a fuzz run worth more than its wall clock.

A TIME BUDGET, BECAUSE TIME IS A RETURN CODE IN DISGUISE

Each target refuses an input that takes longer than a couple of seconds. The
219-byte file that took 21.2 seconds to open panicked at nothing and allocated
nothing — no memory limit and no panic check could have caught it, and only a
clock did. A fuzz target that watches only for crashes would have run past it
three million times without a word.

SEEDS FROM SOMEBODY ELSE'S SCARS

PDF_SEEDS points the corpus at a directory of real files. Pointed at mozilla's
pdf.js test suite — 1 437 files, every one of which is there because it broke a
reader once — FuzzOpenWithinABudget runs 3 077 612 executions and finds 417
interesting inputs without a failure. Unset, the committed seeds still run: the
corpus makes the search better, it is not what makes the target work.

WHY THE RENAME

recover_test.go already has a FuzzOpen in the in-package test package. Two fuzz
targets of the same name in the two packages compile perfectly well and cannot
both be selected: go test -fuzz FuzzOpen refuses the ambiguity outright, so
neither could be run at all. The public-API one is now
FuzzOpenWithinABudget, which also says what it adds.

This is the useful half of #13, whose fix for the inline-image map order landed
independently as #14; the code there and here agreed to the line, having been
written from the same measurement.

100% statement coverage, go vet clean.

Three of the four entry points a caller actually uses had no fuzz target:
ParseObject, Operations and Decode. This adds them, in the external test
package so they exercise the API as a caller sees it, together with two things
that make a fuzz run worth more than its wall clock.

A TIME BUDGET, BECAUSE TIME IS A RETURN CODE IN DISGUISE

Each target refuses an input that takes longer than a couple of seconds. The
219-byte file that took 21.2 seconds to open panicked at nothing and allocated
nothing — no memory limit and no panic check could have caught it, and only a
clock did. A fuzz target that watches only for crashes would have run past it
three million times without a word.

SEEDS FROM SOMEBODY ELSE'S SCARS

PDF_SEEDS points the corpus at a directory of real files. Pointed at mozilla's
pdf.js test suite — 1 437 files, every one of which is there because it broke a
reader once — FuzzOpenWithinABudget runs 3 077 612 executions and finds 417
interesting inputs without a failure. Unset, the committed seeds still run: the
corpus makes the search better, it is not what makes the target work.

WHY THE RENAME

recover_test.go already has a FuzzOpen in the in-package test package. Two fuzz
targets of the same name in the two packages compile perfectly well and cannot
both be selected: `go test -fuzz FuzzOpen` refuses the ambiguity outright, so
neither could be run at all. The public-API one is now
FuzzOpenWithinABudget, which also says what it adds.

This is the useful half of #13, whose fix for the inline-image map order landed
independently as #14; the code there and here agreed to the line, having been
written from the same measurement.

100% statement coverage, go vet clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 0a338e1 into main Aug 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant