Overview
The fuzzer drives the full lex -> parse -> VM pipeline against mutated input, looking for panics, arithmetic overflow, and memory faults. It lives in compiler/fuzz-afl/ and is built on cargo-afl (AFL++), which instruments via AFL++‘s LLVM passes and therefore runs on stable Rust, no nightly toolchain required.
The target runs the VM under Limits::sandbox(), so runaway loops and allocations become a VmErr instead of a hang, and any real crash is a genuine bug rather than resource exhaustion. See Limits and errors.
Running it
cd compiler/fuzz-afl
./seeds.sh # generate corpus + dictionary from vm.json (once)
cargo afl build # instrument on stable, no nightly
cargo afl fuzz -i in -o out -x edge.dict target/debug/afl-pipelineUnder WSL, prefix the fuzz command with AFL_SKIP_CPUFREQ=1 AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 to bypass the core-pattern and CPU-governor checks. Crashes and hangs land in out/default/. Reproduce one by piping it back into the target:
./target/debug/afl-pipeline < out/default/crashes/<id>Inputs are generated, not committed
The seed corpus (in/) and the token dictionary (edge.dict) are derived from a single source of truth, tests/cases/vm.json, so they are gitignored and regenerated by seeds.sh:
in/: one file per unique programsrcin the VM test fixtures, giving AFL valid starting points that already exercise most of the language.edge.dict: keywords, operators, and common builtins, so the byte mutator splices real tokens instead of discovering them blindly.
Only three files are tracked: Cargo.toml, src/main.rs, and seeds.sh. The corpus, dictionary, AFL output, and build artifacts are all reproducible.