Lion Cipher Lab
Set up continuous fuzzing with a regression gate (libFuzzer + ASan in CI)
- AppSec / DevSecOps
- Advanced
- ~90 min
- Free · no account required
Why it matters
A one-shot fuzzing run is a party trick: you point a fuzzer at a target, it finds a crash, everyone claps, and then the corpus is deleted and the same class of bug reappears three commits later because nothing is watching. Continuous fuzzing is the actual engineering practice — it is how Chrome, OpenSSL, curl, and thousands of OSS-Fuzz projects stay fuzzed, and it rests on two ideas this lab makes physical. First, the CORPUS IS AN ASSET: every interesting input the fuzzer discovers is saved and replayed, so coverage compounds over weeks instead of restarting from zero each run, and the deep…
What you'll learn
- writing a libFuzzer harness (`LLVMFuzzerTestOneInput`) around a C parser and building it with clang `-fsanitize=fuzzer,address`
- why AddressSanitizer — not the fuzzer — is what turns a silent heap overflow into a reproducible crash, and what a fuzzer built WITHOUT ASan actually misses
- running a libFuzzer target to find a crash, then MINIMIZING the crashing input with `-minimize_crash=1 -runs=…` down to the smallest bytes that still reproduce
- building a version-controlled SEED CORPUS and a separate `crashes/` regression set, and understanding why the corpus is a compounding asset rather than throwaway output
- writing a REGRESSION GATE script that replays the whole corpus + known crashes in one run and FAILS CI (non-zero exit) if any input still crashes
- time-boxing a CI fuzzing session with `-max_total_time=` so the job neither runs forever nor exits instantly and gates nothing
- reading a coverage signal from libFuzzer's `-print_final_stats` (cov/ft/exec-per-sec) and knowing when a plateau means byte-mutation can't reach a structured format
- wiring the gate into GitHub Actions (or ClusterFuzzLite via Docker) so every push replays regressions and runs a short fresh-fuzz pass
- crash DEDUP by minimized input + sanitizer stack, so the same bug isn't reported forever and drowns out new findings
- the engineering judgment: libFuzzer vs AFL++ vs OSS-Fuzz/ClusterFuzzLite, and why continuous fuzzing beats a one-shot run (regressions creep back; corpora compound)
Steps in this lab
- The angle: continuous, not one-shot — corpus as an asset, crash as a test
- Set up the project and confirm clang has libFuzzer + ASan
- Write a tiny parser with a PLANTED off-by-one heap overflow
- Write the libFuzzer harness and build with -fsanitize=fuzzer,address
- Seed the corpus, then run the fuzzer to FIND the crash
- PROVE the ASan dependency: the same fuzzer, built without ASan, finds nothing
- MINIMIZE the crash, commit it as a regression test, and build the gate
- Fix the bug, watch the gate go green, and read the coverage number
- Wire it into CI: a GitHub Actions gate (and the ClusterFuzzLite option)
- The judgment: libFuzzer vs AFL++ vs OSS-Fuzz/ClusterFuzzLite, and the named failure modes