Skip to content

test: a fuzz target names the promise it broke before it stops - #421

Merged
siracozmen01 merged 1 commit into
mainfrom
test/fuzz-expectation-lines
Nov 8, 2026
Merged

siracozmen01 merged 1 commit into
mainfrom
test/fuzz-expectation-lines

Conversation

@siracozmen01

Copy link
Copy Markdown
Collaborator

Problem

When a fuzz target's promise broke, the harness called abort() with nothing on stderr. A finding's report showed only libFuzzer's "deadly signal" and a stack, so the reader had to map the stack back to a line to learn which promise failed and which status the call returned. The family convention from .github v0.0.55 is that a target prints one "FILE:LINE: expectation failed: ..." line first, and tools/fuzz_target.sh keeps that line in the report.

Change

  • test/fuzz_scene.h defines Require and RequireStatus. When the condition is false they print the file, the line and the promise's own text, plus the returned status for RequireStatus, and then abort. The line never holds the input's bytes.
  • fuzz_journal, fuzz_replay, fuzz_restore_step and fuzz_snapshot drop their private silent Require and use the shared one. Each check on a returned status uses RequireStatus.
  • No library code changes.

Verification

  • tools/check.sh pr on 6738ce7: format, the family checks, the build, the tests, the bindings and clang-tidy, with the fuzz pass.
  • Each of the four targets ran 20 s through tools/fuzz_target.sh with no finding.
  • A mutant that adds Require(size < 0) at the top of fuzz_journal, a promise that always breaks: the report holds test/fuzz_journal.c:20: expectation failed: size < 0 before libFuzzer's deadly signal line.

@siracozmen01 siracozmen01 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.

Review at 6738ce7: ready.

  • Every stop now names its promise. The four fuzz targets' local Require (a bare abort()) is replaced by fuzz_scene.h's macro, which each of them includes. Before it aborts, the macro prints FILE:LINE: expectation failed: <the condition's own text>, and RequireStatus adds the status value. The line carries the source text and an integer, never input bytes.
  • Nothing is left bare. At the head, no abort or __builtin_trap remains in fuzz_journal.c, fuzz_replay.c, fuzz_restore_step.c, fuzz_snapshot.c or fuzz_oracle.h. The only abort is the macro's own, in fuzz_scene.h. fuzz_seeds.c is the seed writer, not a target, and its exit(1) calls already print what failed.
  • No check is weaker. Every converted condition is the same expression as before. The four status checks wrap the same condition and only add the value to the message. The Require calls elsewhere keep their conditions and now print too.
  • The title and body follow the standard. No body line opens a commit header.

@siracozmen01
siracozmen01 merged commit 2eeed0c into main Nov 8, 2026
28 checks passed
@siracozmen01
siracozmen01 deleted the test/fuzz-expectation-lines branch November 8, 2026 11:24
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