Commit graph

1 commit

Author SHA1 Message Date
trvon
43c2e4d825
feat(fuzz): S3 — libFuzzer dictionary mining
For every Z3 model fold proved SAT, the concrete byte values it
picked for integer parameters often *are* the magic constants the
path guards on. Promoting them to a libFuzzer dictionary lets the
mutator splice those bytes into nearby inputs that would otherwise
fail the same guard.

Per libFuzzer's docs (https://llvm.org/docs/LibFuzzer.html#dictionaries),
dictionaries are the single largest mutation-quality lever for
formats with magic bytes, fixed headers, or length-prefixed records.

* dict_miner.{hpp,cpp} — mineDictionary(witnesses) -> dict-format
  string. One entry per unique non-trivial (Integer|Bool) byte
  sequence. Filters trivial values (0/1/all-FF) since S2 boundary
  derivatives + libFuzzer's mutator already cover those. Encodes
  printable ASCII verbatim, \xNN for non-printable.
* fuzz_prep_runner — emits one .dict per function under
  <prep>/dictionaries/<safe>.dict.
* FuzzRunOptions.dict_path + runner.cpp — when set, appends
  -dict=<path> to the libFuzzer argv at launch.
* fuzz_run_runner — auto-detects <prep>/dictionaries/<safe>.dict
  per function and sets ropts.dict_path. No new CLI flag.
* tests/unit/test_dict_miner.cpp — 10 cases / 12 assertions
  covering dedup, trivial filtering, ASCII passthrough, escape
  correctness for \" and \\, pointer skip, header counts.

Smoke: leaky(int*, int) with `flag == 0xAB` guard. dict contains
"\xab\x00\x00\x00"; libFuzzer crashes in 0.7s (vs 1s without
dict — gain is modest on a 1-byte guard but the architecture is
in place for the multi-byte magic-value formats where libFuzzer's
own docs cite 5-50x speedups).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 16:19:11 -04:00