Writing harnesses #
In the following, we will go over Rust specific tips to optimize the results from your harnesses. For general advice, refer to Writing harnesses.
Structure-Aware fuzzing with the arbitrary crate #
The
arbitrary crate simplifies writing fuzzing harnesses. By deriving a macro, Rust structs can be targeted for fuzzing. For example, the following code requires constructing a Name
struct that owns a String
. We derived the Arbitrary macro to facilitate the construction of such a Name
.
use arbitrary::{Arbitrary};
#[derive(Debug, Arbitrary)]
pub struct Name {
data: String
}
impl Name {
pub fn check_buf(&self) {
let data = self.data.as_bytes();
if data.len() > 0 && data[0] == b'a' {
if data.len() > 1 && data[1] == b'b' {
if data.len() > 2 && data[2] == b'c' {
process::abort();
}
}
}
}
}
your_project
that uses the arbitrary crateWith the arbitrary crate, we can easily write a fuzzing harness for this test, similar to the harness in Write a Fuzz test.
#![no_main]
use libfuzzer_sys::fuzz_target;
use arbitrary::{Arbitrary, Unstructured};
fn harness(data: &[u8]) {
// Wrap it in an `Unstructured`.
let mut unstructured = Unstructured::new(data);
// Generate an `Name` and run our checks.
if let Ok(name) = your_project::Name::arbitrary(&mut unstructured) {
name.check_buf();
}
}
fuzz_target!(|data: &[u8]| {
harness(data);
});
The cargo-fuzz tool actually supports the arbitrary crate, so we can simplify this.
#![no_main]
use libfuzzer_sys::fuzz_target;
fn harness(data: &your_project::Name) {
data.check_buf();
}
fuzz_target!(|data: your_project::Name| {
harness(&data);
});
Both of the above examples require the arbitrary crate to be a dependency of your library crate. In the first example, you also need to add the dependency to the Cargo.toml
in the fuzz/
directory. The second example does not require this because the arbitrary dependency of the libfuzzer_sys
dependency is used. Here is the dependency declaration you need:
[dependencies]
arbitrary = { version = "1", features = ["derive"] }
As usual, the fuzz test can be started using the following command:
cargo +nightly fuzz run fuzz_target_1
The arbitrary crate essentially offers a way to deserialize byte arrays to Rust structs. However, it is limited in that it does not offer the reverse function: serializing Rust structs to byte arrays. This becomes a problem when trying to prepare a corpus of seeds. It is not possible to purposefully construct byte-arrays that construct a specific Rust struct.
Therefore, the arbitrary crate is useful only when starting from an empty corpus. This is not an issue when using cargo-fuzz because it uses libFuzzer internally, and libFuzzer supports starting from an empty corpus. However, other fuzzers like AFL++ require a seed input.