| name | fuzzing-rust-expert |
| description | Use this skill to fuzz open source Rust software projects. |
Fuzzing Rust expert
This skill provides the agent with the knowledge and tools to write, build, and
validate fuzz targets for Rust projects integrated into OSS-Fuzz. Rust fuzzing
uses cargo-fuzz with the libfuzzer_sys crate, which drives libFuzzer.
Fundamental Concepts
OSS-Fuzz base image
Rust projects must use the Rust base builder image:
FROM gcr.io/oss-fuzz-base/base-builder-rust
Set language: rust in project.yaml.
Harness structure
Rust fuzz targets live in fuzz/fuzz_targets/<name>.rs within the crate being
fuzzed. The minimal harness using raw bytes:
#![no_main]
use libfuzzer_sys::fuzz_target;
fuzz_target!(|data: &[u8]| {
let _ = my_crate::parse(data);
});
For structured fuzzing using the arbitrary crate (preferred when the target
expects typed input):
#![no_main]
use libfuzzer_sys::fuzz_target;
use arbitrary::Arbitrary;
#[derive(Debug, Arbitrary)]
struct MyInput {
header: u8,
payload: Vec<u8>,
flags: u32,
}
fuzz_target!(|input: MyInput| {
let _ = my_crate::process(input.header, &input.payload, input.flags);
});
The Cargo.toml for the fuzz directory must declare dependencies:
[package]
name = "my-crate-fuzz"
version = "0.0.0"
edition = "2021"
publish = false
[dependencies]
libfuzzer-sys = "0.4"
arbitrary = { version = "1", features = ["derive"] }
[dependencies.my-crate]
path = ".."
[[bin]]
name = "fuzz_target_name"
path = "fuzz_targets/fuzz_target_name.rs"
test = false
doc = false
Building in OSS-Fuzz
build.sh uses cargo fuzz build and then copies binaries to $OUT:
cd $SRC/<crate-dir>
cargo fuzz build -O
FUZZ_TARGET_OUTPUT_DIR=$SRC/<crate-dir>/target/x86_64-unknown-linux-gnu/release
for f in fuzz/fuzz_targets/*.rs; do
name=$(basename "${f%.*}")
cp "$FUZZ_TARGET_OUTPUT_DIR/$name" "$OUT/"
done
If the project requires a nightly toolchain, set it in the Dockerfile:
ENV RUSTUP_TOOLCHAIN=nightly
Or pin to a specific nightly for reproducibility:
ENV RUSTUP_TOOLCHAIN=nightly-2025-07-03
Seed corpus and dictionaries
- Place seed files in
fuzz/corpus/<target_name>/ within the repo; they are
automatically picked up by cargo-fuzz and can be zipped for OSS-Fuzz.
- To ship a corpus with OSS-Fuzz copy a zip to
$OUT/<target_name>_seed_corpus.zip.
- Dictionaries go to
$OUT/<target_name>.dict.
- For targets that parse a structured format, generating seeds with a script
beats hand-picking a few files — random mutation rarely passes the parser's
early checks (note: cargo-fuzz's
arbitrary is the better route when the
target takes typed data rather than a byte format). See the structured seed
generation reference.
Characteristics of good Rust fuzzing harnesses
- Targets attack surface: parsers, deserializers, protocol implementations,
unsafe blocks, and any API that processes untrusted bytes.
- Uses structured fuzzing (
arbitrary) when the target expects typed data
rather than raw bytes — this dramatically improves coverage.
- Handles expected errors:
Result and Option should be let-bound and
ignored (let _ = ...). Only actual panics are findings.
- Avoids panicking on every input: do not use
.unwrap() or .expect()
on fallible operations driven by fuzz input — these create false positives.
- Avoids non-determinism: no
rand, no system time, no thread spawning
inside the fuzz callback.
- Is fast: avoid I/O, heavy allocations, or expensive setup inside the
callback; do those outside the
fuzz_target! macro if needed with
LazyLock / OnceLock.
- Considers
unsafe code: if the crate has unsafe blocks, write
harnesses that exercise those paths — this is where memory bugs can still
occur in Rust.
- Builds in release mode (
-O): fuzzing in debug mode is much slower.
What Rust fuzzing finds
Rust's ownership model prevents most memory-corruption bugs, but fuzzing still
finds:
- Panics:
unwrap/expect on None/Err, index out of bounds,
arithmetic overflow in debug mode, explicit panic! calls.
unsafe bugs: use-after-free, out-of-bounds reads/writes, undefined
behaviour in unsafe blocks — these are real memory bugs.
- Logic errors: incorrect parsing, data corruption, wrong output.
- Hangs: infinite loops triggered by crafted input.
- Stack overflows: deep recursion on adversarial input.
Operational guidelines