Skip to content

cargo-fuzz - Fuzzing Rust with libFuzzer Cheatsheet

cargo-fuzz - Fuzzing Rust with libFuzzer Cheatsheet

cargo-fuzz is the standard way to fuzz Rust code. It wires up libFuzzer with a Cargo-native workflow: write a fuzz target as an ordinary Rust function taking arbitrary bytes, run cargo fuzz run, and coverage-guided fuzzing hunts for panics, arithmetic overflows, and — in unsafe code — genuine memory errors. Safe Rust prevents memory corruption, but it does not prevent panics, infinite loops, or logic bugs, and any unsafe block or C dependency reintroduces the full class of memory issues.

Installation

StepCommand
Installcargo install cargo-fuzz
Nightly requiredrustup install nightly (libFuzzer needs nightly)
Initializecargo fuzz init (in your crate)
Verifycargo fuzz --version

Creating a Fuzz Target

cargo fuzz init                 # creates fuzz/ directory
cargo fuzz add parse_input      # creates a new target
// fuzz/fuzz_targets/parse_input.rs
#![no_main]
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
    if let Ok(s) = std::str::from_utf8(data) {
        let _ = my_crate::parse(s);   // panics here become findings
    }
});

Running

CommandDescription
cargo fuzz run parse_inputFuzz the target
cargo fuzz run parse_input -- -max_total_time=300Run for 5 minutes
cargo fuzz run parse_input -j 88 parallel jobs
cargo fuzz listList targets
cargo fuzz buildBuild without running

Structured Input with Arbitrary

Raw bytes waste time on inputs your parser rejects immediately. The arbitrary crate builds typed values instead:

use arbitrary::Arbitrary;

#[derive(Arbitrary, Debug)]
struct Config { name: String, retries: u8, enabled: bool }

fuzz_target!(|cfg: Config| {
    let _ = my_crate::apply_config(cfg);
});
BenefitWhy
Valid-shaped inputsReaches deeper logic
Typed targetsFuzz APIs, not just parsers
Debug outputReadable reproducers

Sanitizers

SanitizerFlag
AddressSanitizer (default)-s address
LeakSanitizerincluded with ASan
MemorySanitizer-s memory
ThreadSanitizer-s thread
None (faster)-s none
# Fuzz unsafe FFI code with ASan
cargo fuzz run ffi_target -s address

Corpus and Coverage

CommandPurpose
fuzz/corpus/<target>/Where interesting inputs accumulate
cargo fuzz cmin <target>Minimize the corpus
cargo fuzz tmin <target> <input>Minimize a crashing input
cargo fuzz coverage <target>Generate coverage data
Seed corpusAdd valid examples to bootstrap

Seeding the corpus with real, valid inputs is the single most effective way to make fuzzing productive quickly.

Reproducing Crashes

# Crashes land in fuzz/artifacts/<target>/
cargo fuzz run parse_input fuzz/artifacts/parse_input/crash-abc123

# Shrink it to a minimal case
cargo fuzz tmin parse_input fuzz/artifacts/parse_input/crash-abc123

Convert the minimized input into a regular #[test] so the bug stays fixed.

What to Fuzz in Rust

Good targetWhy
Parsers / deserializersUntrusted input, complex state
unsafe blocksMemory safety actually at risk
FFI boundariesC code behind the interface
Protocol/state machinesOrdering bugs
Compression / encodingClassic bug territory
Less usefulWhy
Pure safe arithmeticPanics are usually obvious
Thin wrappersLittle logic to explore

cargo-fuzz vs Other Fuzzers

Aspectcargo-fuzzAFL++ (afl.rs)LibAFLhonggfuzz-rs
EnginelibFuzzerAFL++Customhonggfuzz
SetupEasiest for RustModerateBuild your ownEasy
Structured inputarbitraryarbitraryCustom typesarbitrary
Best forStandard Rust fuzzingPersistent/binary modesUnusual targetsHardware feedback

For non-Rust targets see AFL++ or honggfuzz; for custom fuzzers see LibAFL.

Resources