Automated fuzzing tool for Rust libraries and CLI tools. Finds panics, overflows, OOM, timeouts, ASAN errors, and other crashes.
Two fuzzing modes:
- Custom — mutates valid files with
create_broken_files, runs a wrapper binary, detects crashes from output patterns - Cargo-fuzz — uses libfuzzer via
cargo fuzz run, restarts in a loop until timeout
Both modes share crash archives, reports, and ignore lists. Results are grouped by error signature (type + source file + line).
src/ auto_fuzzer binary (CLI orchestrator)
crates/<name>/ wrapper binaries that exercise a library's API
src/main.rs uses fuzz_utils::run(check_file)
fuzz_settings_ci.toml fuzzing config (search/ignore patterns, extensions, etc.)
crates/fuzz_utils/ shared lib: file walking boilerplate + structured ByteInput
fuzz/fuzz_targets/ cargo-fuzz (libfuzzer) targets
fuzz/Cargo.toml cargo-fuzz workspace with optional features per target
configs/external/ fuzz configs for tools without a local crate (biome, ruff, swc...)
.github/workflows/ CI (single fuzz.yml with mode: custom or cargo-fuzz in matrix)
cargo install --path .
cargo install create_broken_files minimizer # external toolsjust list-crates # show all 27 crates
just check-crates # verify all compile (crates + fuzz targets)
just upgrade-check # update deps + verify
# Prepare + fuzz a project
just prepare symphonia "AA_MUSIC_VALID_FILES.7z"
just fuzz symphonia 3600
# Structured fuzzing (no file corpus needed)
just prepare pdf_writer --generate
just fuzz pdf_writer 3600
# Full pipeline: prepare → fuzz → verify (ASAN) → reports
just pipeline zune "AA_IMAGE_VALID_FILES.7z" 3600
# Verify results, list reports
just verify symphonia
just reportsauto_fuzzer fuzz --mode custom --timeout 3600
auto_fuzzer fuzz --mode custom --config crates/symphonia/fuzz_settings_ci.toml --timeout 7200auto_fuzzer fuzz --mode cargo-fuzz --target image --corpus /opt/INPUT_FILES \
--features "image_f" --timeout 3600Runs the external minimizer on crash files, keeping originals.
auto_fuzzer minimize
auto_fuzzer minimize --dir /opt/BROKEN_FILES_DIREach crash generates a folder like zune__assertion_eq__src_image.rs_482/397_bytes_12345/ containing:
to_report.txt— full crash report with reproducer codeto_report_metadata.toml— structured metadata (type, signature, source file, line)crash_output.txt— raw stdout+stderrcompressed.zip— crash fileissue_title.txt— ready-made title with backticksissue_body.md— ready-made bodycreate_issue.sh— one-click: creates GitHub issue, opens browser to attach zip
auto_fuzzer report list --dir /tmp/tmp_folder/data
bash /tmp/tmp_folder/data/zune__assertion_eq__src_image.rs_482/397_bytes_12345/create_issue.shThe repo is auto-detected from crates/<name>/Cargo.toml git dependency. For library crates, the report includes a simplified reproducer with check_file code. For structured fuzzing crates (pdf_writer), the reproducer has all input.xxx() calls replaced with hardcoded values.
Two mechanisms work together:
Filters crashes during fuzzing — matched patterns are never saved. Always add a comment with the issue URL:
ignored_item_1 = "stack-overflow" # https://github.com/boa-dev/boa/issues/1402
ignored_item_2 = "memory allocation of" # https://github.com/boa-dev/boa/issues/5367
ignored_item_3 = "src/bits.rs" # https://github.com/dnglab/dnglab/issues/571Managed via CLI, independent of fuzzing:
auto_fuzzer ignore add lofty "src/wavpack/properties.rs" "https://github.com/Serial-ATA/lofty-rs/issues/620"
auto_fuzzer ignore remove lofty "src/wavpack/properties.rs"# Show all ignored patterns (both ignore_list.toml and all fuzz configs)
auto_fuzzer ignore list
auto_fuzzer ignore list --project hayro
# Check if issues have been closed (read-only)
auto_fuzzer ignore verify
# Check and remove entries for closed issues
auto_fuzzer ignore cleanverify and clean check both ignore_list.toml and every crates/*/fuzz_settings_ci.toml. Entries without a GitHub URL are left untouched.
Crashes are grouped by signature: {error_type}::{source_file}. Folders include the line number for disambiguation.
| Crash | Folder | Issue title |
|---|---|---|
overflow in src/wavpack/properties.rs:123 |
symphonia__overflow_s__src_wavpack_properties.rs_123/ |
Panic attempt to subtract with overflow in src/wavpack/properties.rs |
assertion_eq in src/image.rs:482 |
zune__assertion_eq__src_image.rs_482/ |
Panic assertion \left == right` failedinsrc/image.rs` |
| timeout (no source) | boa__timeout__unknown/ |
Timeout when processing file |
| OOM (no source) | boa__memory_failure__unknown/ |
Memory allocation failure when processing file |
| ASAN heap-use-after-free | foo__heap_use_after_free__src_buf.rs_10/ |
Heap use after free heap-use-after-free in src/buf.rs |
For libraries that generate output (not parse input), like pdf-writer:
use fuzz_utils::ByteInput;
fn main() {
fuzz_utils::run(|path| {
let mut input = ByteInput::from_file(path).unwrap();
let count = input.u8_range("page_count", 1, 10).unwrap();
let width = input.f32_range("width", 0.0, 1000.0).unwrap();
// ... call library API with these values
});
}- Input file = random bytes, consumed sequentially
- Each call logs the decision:
page_count=3,width=595.2 - Same file = same decisions = deterministic
- Minimizer shrinks the file = fewer API calls
- On crash:
.paramsfile +.reproducer.rswith hardcoded values
Single workflow .github/workflows/fuzz.yml with matrix entries for both modes. Default timeout is DEFAULT_TIMEOUT (env), overridable per-job with custom_timeout.
Flow:
- Build binary (normal + ASAN), upload to Nightly release
- Download/generate test corpus
- Fuzz for N seconds
- Verify new crashes (ASAN for custom, minimizer for cargo-fuzz)
- Download previous crashes (post-fuzz = small race window)
- Regression check (re-run old crashes, remove fixed ones)
- Merge + dedup (MD5), upload with retry (race condition safe)
- Print crash details in logs (exit code per file)
- Generate reports, upload artifacts
Crashes persist across runs in crashes_<NAME>.7z on the Nightly release. Both custom and cargo-fuzz modes write to the same archive (suffix _CF is stripped).
Ctrl+C once = finish current iteration, save results. Again = force quit. Works in both modes.
MIT