Rust fuzzing is supported through the libfuzzer-sys
crate, which provides
bindings to LLVM's libFuzzer fuzzing engine. For more information, see the libfuzzer-sys
repository as well as the LLVM libFuzzer project page.
The rust_fuzz
module produces a fuzzer binary which begins fuzzing when it's
run (similar to cc_fuzz
modules). As the fuzzer leverages the libFuzzer
fuzzing engine, it can take a number of arguments to control fuzzing. These are
enumerated in the libFuzzer documentation.
rust_fuzz
modules are an extension of rust_binary
modules, and as such share
the same properties and considerations. Additionally, they implement many of the
same properties and functionality as do the cc_fuzz
modules.
When building rust_fuzz
modules, the --cfg fuzzing
flag is emitted which can
be used to support conditional compilation of library code to improve fuzzing.
Write a basic Rust fuzzer
You can define a fuzz module in an Android.bp
build file with this code:
rust_fuzz {
name: "example_rust_fuzzer",
srcs: ["fuzzer.rs"],
// Config for running the target on fuzzing infrastructure can be set under
// fuzz_config. This shares the same properties as cc_fuzz's fuzz_config.
fuzz_config: {
fuzz_on_haiku_device: true,
fuzz_on_haiku_host: false,
},
// Path to a corpus of sample inputs, optional. See https://llvm.org/docs/LibFuzzer.html#corpus
corpus: ["testdata/*"],
// Path to a dictionary of sample byte sequences, optional. See https://llvm.org/docs/LibFuzzer.html#dictionaries
dictionary: "example_rust_fuzzer.dict",
}
The fuzzer.rs
file contains a simple fuzzer:
fn heap_oob() {
let xs = vec![0, 1, 2, 3];
let val = unsafe { *xs.as_ptr().offset(4) };
println!("Out-of-bounds heap value: {}", val);
}
fuzz_target!(|data: &[u8]| {
let magic_number = 327;
if data.len() == magic_number {
heap_oob();
}
});
Here fuzz_target!(|data: &[u8]| { /* fuzz using data here */ });
defines the
fuzz-target entry-point called by the libFuzzer
engine. The data
argument is
a sequence of bytes provided by the libFuzzer
engine to be manipulated as input
to fuzz the targeted function.
In this example fuzzer, only the length of the data gets checked to determine
whether to call the heap_oob
function, the calling of which results in an
out-of-bounds read. libFuzzer
is a coverage-guided fuzzer, so it quickly converges on the
problematic length as it determines that the first 326 B of data don't
result in new execution paths.
Locate this example, in-tree, at tools/security/fuzzing/example_rust_fuzzer/.
To view a slightly more complex example of another fuzzer (which fuzzes a rustlib
dependency) in-tree, see the legacy_blob_fuzzer.
For guidance on how to write structure-aware Rust fuzzers, see the Rust Fuzz book, the official documentation for the Rust Fuzz project.