Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Fuzzing

Neumann uses cargo-fuzz (libFuzzer-based) for coverage-guided fuzzing.

Setup

# Install cargo-fuzz (requires nightly)
cargo install cargo-fuzz

# List available targets
cd fuzz && cargo +nightly fuzz list

Running Fuzz Targets

# Run a specific target for 60 seconds
cargo +nightly fuzz run parser_parse -- -max_total_time=60

# Run without sanitizer (2x faster for safe Rust)
cargo +nightly fuzz run parser_parse --sanitizer none

# Reproduce a crash
cargo +nightly fuzz run parser_parse artifacts/parser_parse/crash-xxx

Available Targets

TargetModuleWhat it tests
parser_parseneumann_parserStatement parsing
parser_parse_allneumann_parserMulti-statement parsing
parser_parse_exprneumann_parserExpression parsing
parser_tokenizeneumann_parserLexer/tokenization
compress_idstensor_compressVarint ID compression
compress_rletensor_compressRLE encode/decode
compress_snapshottensor_compressSnapshot serialization
vault_ciphertensor_vaultAES-256-GCM roundtrip
checkpoint_statetensor_checkpointCheckpoint bincode
storage_sparse_vectortensor_storeSparse vector roundtrip
slab_entity_indextensor_storeEntityIndex operations
consistent_hashtensor_storeConsistent hash partitioner
tcp_framingtensor_chainTCP wire protocol codec
membershiptensor_chainCluster config serialization

Adding a New Fuzz Target

  1. Create target file in fuzz/fuzz_targets/<name>.rs:
#![allow(unused)]
#![no_main]

fn main() {
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
    // Your fuzzing code here
    if let Ok(input) = std::str::from_utf8(data) {
        let _ = my_crate::parse(input);
    }
});
}
  1. Add entry to fuzz/Cargo.toml:
[[bin]]
name = "my_target"
path = "fuzz_targets/my_target.rs"
test = false
doc = false
bench = false
  1. Add seed corpus files to fuzz/corpus/<name>/:
mkdir -p fuzz/corpus/my_target
echo "valid input 1" > fuzz/corpus/my_target/seed1
echo "valid input 2" > fuzz/corpus/my_target/seed2
  1. Update CI matrix in .github/workflows/fuzz.yml

Structured Fuzzing

For complex input types, use arbitrary:

#![allow(unused)]
fn main() {
use arbitrary::Arbitrary;

#[derive(Arbitrary, Debug)]
struct MyInput {
    field1: u32,
    field2: String,
}

fuzz_target!(|input: MyInput| {
    let _ = my_crate::process(&input);
});
}

Investigating Crashes

# View crash input
xxd artifacts/my_target/crash-xxx

# Minimize crash
cargo +nightly fuzz tmin my_target artifacts/my_target/crash-xxx

# Debug
cargo +nightly fuzz run my_target artifacts/my_target/crash-xxx -- -verbosity=2

CI Integration

Fuzz tests run in CI for 60 seconds per target. See .github/workflows/fuzz.yml.

Best Practices

  1. Add corpus seeds: Real-world inputs help fuzzer find paths
  2. Use structured fuzzing: For complex inputs
  3. Run locally: Before pushing changes to fuzzed code
  4. Minimize crashes: Smaller inputs are easier to debug
  5. Keep targets focused: One functionality per target