Testland
Browse all skills & agents

cargo-fuzz-rust

Author and run cargo-fuzz - Rust fuzzing via libFuzzer with cargo integration. Covers `cargo install cargo-fuzz`, `cargo fuzz init` + `cargo fuzz add {target}` for harness scaffolding, the `fuzz_target!` macro for entry-point declaration, the `Arbitrary` trait for structured input mutation, and `cargo fuzz run` invocation. Requires Rust nightly. Use for fuzz testing Rust libraries - cargo-fuzz wraps libFuzzer with native Rust ergonomics.

Install with skills.sh (any agent)

npx skills add testland/qa --skill cargo-fuzz-rust
View source

cargo-fuzz-rust

Overview

cargo-fuzz (per github.com/rust-fuzz/cargo-fuzz (opens in new window)) requires Rust nightly because libFuzzer integration depends on unstable compiler features.

For sanitiser pairing: cargo-fuzz auto-enables ASan by default (per the cargo-fuzz README). See sanitiser-integration-reference for ASan + UBSan composition. For corpus discipline see corpus-management-reference.

When to use

  • Fuzz testing a Rust crate (parser, decoder, format converter).
  • Targets that benefit from structured input mutation via the Arbitrary trait.
  • CI smoke fuzz alongside cargo test.

For raw libFuzzer in C/C++ with Rust FFI see libfuzzer-cpp.

Authoring

Install

Per the cargo-fuzz README:

# Rust nightly is required
rustup install nightly

# Install cargo-fuzz
cargo install cargo-fuzz

Initialise

In your crate root:

cargo fuzz init

This creates a fuzz/ subdirectory:

fuzz/
  Cargo.toml
  fuzz_targets/
    fuzz_target_1.rs       # generated default target

Add a fuzz target

cargo fuzz add parse_query

Creates fuzz/fuzz_targets/parse_query.rs:

#![no_main]
use libfuzzer_sys::fuzz_target;
use my_crate::parser;

fuzz_target!(|data: &[u8]| {
    let _ = parser::parse_query(data);
});

Per the cargo-fuzz docs, fuzz_target! is the macro that wires up the libFuzzer entry point (LLVMFuzzerTestOneInput under the hood). The closure body is what runs per input.

Structured input via Arbitrary

Raw byte slices work for binary formats; for structured inputs use the arbitrary crate:

#![no_main]
use libfuzzer_sys::fuzz_target;
use arbitrary::Arbitrary;

#[derive(Debug, Arbitrary)]
struct Request {
    host: String,
    port: u16,
    body: Vec<u8>,
}

fuzz_target!(|req: Request| {
    let _ = handle_request(&req.host, req.port, &req.body);
});

Add arbitrary = { version = "1", features = ["derive"] } to fuzz/Cargo.toml.

Running

Basic run

# Nightly toolchain required
cargo +nightly fuzz run parse_query

This builds the target with libFuzzer instrumentation + ASan and runs indefinitely.

Common options

OptionEffect
--releaseRelease-mode build (faster, less debug info)
--debug-assertionsKeep debug assertions in release mode
--sanitizer=<name>address (default), leak, memory, thread, none
--jobs=NParallel workers
--no-default-featuresDisable default cargo-fuzz features
-- <libFuzzer-flag>Pass through to libFuzzer (e.g., -max_total_time=300)
cargo +nightly fuzz run parse_query -- -max_total_time=300

Sanitiser variants

UBSan (via --sanitizer=none + custom RUSTFLAGS) and MSan variants: see references/crash-reports-and-ci.md.

Reproducing a crash

cargo +nightly fuzz run parse_query \
  fuzz/artifacts/parse_query/crash-<sha1>

Or:

cargo +nightly fuzz fmt parse_query \
  fuzz/artifacts/parse_query/crash-<sha1>
# Prints the crash input in a Rust-readable format

Crash artefacts location

Per cargo-fuzz convention:

fuzz/
  corpus/
    parse_query/            # evolved corpus
  artifacts/
    parse_query/
      crash-<sha1>          # crash artefacts
      leak-<sha1>
      timeout-<sha1>

Parsing results

Sanitiser report format is identical to libFuzzer / ASan - see sanitiser-integration-reference "Reading a sanitiser report", and references/crash-reports-and-ci.md for the ASan report anatomy (bug class, access, stack, allocation site).

cargo fuzz fmt decodes binary inputs into a Rust-readable form (useful when using Arbitrary - recovers the struct).

CI integration

Smoke-fuzz every target for 5 min on each PR (nightly toolchain, cached corpus, uploaded artifacts): see references/crash-reports-and-ci.md.

Anti-patterns

Anti-patternWhy it failsFix
Using stable toolchaincargo-fuzz needs nightlyrustup install nightly; use cargo +nightly fuzz
Raw &[u8] for structured inputMutation hits format errors more than logicUse Arbitrary + a custom struct
Empty seed corpusFuzzer wanders; slow path discoveryDrop a few representative inputs in fuzz/corpus/<target>/
Ignoring --releaseDebug builds slow iterationUse --release for long campaigns
No cargo fuzz fmt on crashHard-to-read crash inputsAlways cargo fuzz fmt before filing a bug
Committing fuzz/artifacts/ to repoRepo bloat.gitignore artifacts; persist via CI cache
Mixing fuzz targets in one fileCargo treats each fuzz_targets/*.rs as one binaryOne file per target

Limitations

  • Nightly-only. Stable Rust doesn't support the required unstable features. Pin a known-good nightly date to avoid drift.
  • Build time. First build is slow (rebuilds dependencies with sanitiser instrumentation).
  • Arbitrary derive is shallow. Custom types need manual impl Arbitrary for non-trivial mutation strategies.
  • Cross-target corpus is per-target. fuzz/corpus/<target>/ - no sharing across targets.
  • macOS / Windows partial support. Linux is the primary platform; some features (MSan) Linux-only.

References

cargo-fuzz: sanitiser variants, crash reports, and CI

View source (opens in new window)

cargo-fuzz: sanitiser variants, crash reports, and CI

Deep reference for cargo-fuzz-rust. The core install / init / add / run / reproduce workflow lives in the skill spine; this file holds the advanced sanitiser variants, the ASan report anatomy, and the CI job.

Sanitiser variants

# UBSan via none sanitiser + custom RUSTFLAGS
RUSTFLAGS="-Cpasses=sancov-module -Cllvm-args=-sanitizer-coverage-level=4 -Zsanitizer=undefined" \
  cargo +nightly fuzz run --sanitizer=none parse_query

# MSan
cargo +nightly fuzz run --sanitizer=memory parse_query

Reading a sanitiser report

Report format is identical to libFuzzer / ASan (per clang.llvm.org/docs/AddressSanitizer.html (opens in new window)):

==1234==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f...
READ of size 4 at 0x7f... thread T0
    #0 0x4015a3 in process_input src/parser.rs:42:5
    #1 0x4012f0 in rust_fuzzer_test_input parse_query.rs:8:5
0x7f... is located 0 bytes to the right of 16-byte region [0x7f..., 0x7f...)
allocated by thread T0 here:
    #0 0x40e7c0 in __interceptor_malloc
  • Bug class: heap-buffer-overflow, stack-use-after-return, use-after-free, double-free, memory-leak.
  • Access: READ or WRITE, plus size.
  • Stack: top frame is the crash site; frames below are the call chain down to the fuzz target entry point.
  • Allocation site: where the corrupted memory was allocated.
  • Freed site: on use-after-free, where it was freed.

CI integration

jobs:
  fuzz:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@nightly
      - run: cargo install cargo-fuzz
      - uses: actions/cache@v4
        with:
          path: |
            fuzz/corpus
            ~/.cargo/registry
            target
          key: fuzz-${{ github.sha }}
          restore-keys: fuzz-
      - name: Smoke fuzz (5 min per target)
        run: |
          for target in $(cargo fuzz list); do
            timeout 300 cargo +nightly fuzz run $target -- -max_total_time=300 || true
          done
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: fuzz-artifacts
          path: fuzz/artifacts/

Related skills

afl-plus-plus

Author and run AFL++ - out-of-process coverage-guided fuzzer (a community fork of Google's original AFL with improved mutations and instrumentation). Covers afl-cc / afl-clang-fast instrumented build, afl-fuzz invocation, parallel master/slave (-M / -S), dictionary support (-x), QEMU mode (-Q) for binaries without source, output structure (queue / crashes / hangs), crash minimisation (afl-tmin), corpus minimisation (afl-cmin), crash filename triage, and CI integration. Use for fuzzing standalone binaries (file processors, command-line tools) where libFuzzer's in-process model doesn't fit; for cross-fuzzer corpus strategy see corpus-management-reference.

atheris-python-fuzzing

Author and run Atheris - Google's Python coverage-guided fuzzer built on libFuzzer. Covers pip installation, atheris.Setup + atheris.Fuzz invocation, TestOneInput(data: bytes) target signature, FuzzedDataProvider for structured input, instrument_imports() / instrument_func decorators for coverage instrumentation, and libFuzzer-passthrough flags (-atheris_runs, -max_total_time, -dict). Use for fuzzing Python libraries - also supports CPython native-extension fuzzing.

corpus-management-reference

Pure-reference catalog of fuzz-corpus management practices. Defines what a corpus is (seed corpus + evolved corpus saved by the fuzzer), corpus directory layout per libFuzzer / AFL++ / Go native / cargo-fuzz / OSS-Fuzz, the canonical crash-artefact naming (crash-{sha1} / leak-{sha1} / timeout-{sha1}), seed corpus construction strategies (sample-from-prod, sample-from-test-fixtures, from-spec-keywords), corpus minimisation, dictionary files, and the OSS-Fuzz integration corpus sync. Use as the corpus-discipline reference when building a fuzz target or maintaining a long-running fuzz campaign.

crash-triage-reference

Pure-reference catalog for manually triaging individual fuzzer crash artifacts - reading ASan, UBSan, and MSan output; classifying findings as LIKELY-EXPLOITABLE, MEDIUM, or BENIGN; deduplicating by stack-hash; and minimizing reproducers with -minimize_crash. Use when you need to understand what a specific crash means, build exploitability intuition, or manually work a small set of findings. For automated bulk triage across a full artifact directory, run automated findings triage instead.

fuzz-tool-selector

Routes a fuzz-target authoring task to the right fuzzer for the detected language and build type. Decision tree: C/C++ → libfuzzer-cpp + afl-plus-plus; Rust → cargo-fuzz-rust (or libfuzzer-cpp via FFI); Go → go-native-fuzzing; Python → atheris-python-fuzzing; JVM → jazzer-jvm-fuzzing; closed-source binary → afl-plus-plus in QEMU mode; mature open-source project → ossfuzz-integration. Use when a project needs coverage-guided fuzzing and no fuzzer has been chosen for its language or toolchain yet.

go-native-fuzzing

Author and run Go's native fuzzing (Go 1.18+) - coverage-guided fuzzing built into the standard testing package via FuzzXxx functions. Covers f.Add seed-corpus declaration, f.Fuzz callback signature with typed parameters, testdata/fuzz/{FuzzXxx}/ directory layout for seeds + regression cases, the -fuzz flag for `go test`, and CI integration via short smoke runs. Use for fuzz testing Go libraries - Go's native approach integrates seamlessly with standard `go test` rather than requiring a separate toolchain like AFL++.

jazzer-jvm-fuzzing

Author and run Jazzer - Code Intelligence's JVM coverage-guided fuzzer built on libFuzzer. Covers Maven / Gradle / standalone JAR installation, the @FuzzTest annotation (JUnit 5 integration), typed parameter mutation (String, primitives, byte[]), built-in JVM sanitisers (SSRF / path traversal / OS command injection / deserialization gadget / ReDoS), and the JAZZER_FUZZ=1 env var to switch between regression and fuzzing modes. Use for fuzz testing Java / Kotlin libraries - particularly effective against parsing, deserialization, and HTTP-handling code.

libfuzzer-cpp

Author and run LLVM libFuzzer for C/C++ - in-process coverage-guided fuzzing. Covers harness authoring (LLVMFuzzerTestOneInput entry point), build with -fsanitize=fuzzer,address,undefined, runtime flags (-max_total_time, -runs, -dict, -fork, -workers), corpus + crash-artefact handling, and CI integration. Use for libraries / parsers / decoders in C/C++ where in-process fuzzing of a function is the right scope. Compose with ASan + UBSan from sanitiser-integration-reference and corpus discipline from corpus-management-reference.

ossfuzz-integration

Author and submit a project to Google OSS-Fuzz - the open-source continuous fuzzing service that runs libFuzzer / AFL++ / Honggfuzz campaigns on Google infrastructure 24x7. Covers the project.yaml + Dockerfile + build.sh contract, the $OUT/$WORK conventions, supported languages + sanitisers, seed-corpus + dictionary submission, the OSS-Fuzz Build Status dashboard, and the disclosure SLA (issues filed in Monorail with 90-day deadline). Use to offload long-running fuzz campaigns to dedicated infrastructure rather than self-hosting.

sanitiser-integration-reference

Pure-reference catalog of compiler sanitisers used with fuzz testing - AddressSanitizer (ASan), UndefinedBehaviorSanitizer (UBSan), MemorySanitizer (MSan), ThreadSanitizer (TSan), and LeakSanitizer (LSan). Explains what each detects, compatibility (can ASan + UBSan combine? - yes; ASan + MSan? - no), build flags, runtime options (ASAN_OPTIONS / UBSAN_OPTIONS env vars), and the typical ~2x slowdown per ASan. Use to pick the right sanitiser per fuzz target, configure the build, and interpret crash reports.