Skip to content

Commit

Permalink
Add support for mut in the fuzz_target! macro
Browse files Browse the repository at this point in the history
  • Loading branch information
ventaquil committed Mar 19, 2024
1 parent 9848925 commit 31a75ba
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Released YYYY-MM-DD.

* Bindings to `LLVMFuzzerCustomCrossOver` through the `fuzz_crossover` macro.
* `example_crossover` using both `fuzz_mutator` and `fuzz_crossover` (adapted from @rigtorp)
* Support for `mut` in the `fuzz_target!` macro.

### Changed

Expand Down
104 changes: 104 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,14 +245,64 @@ macro_rules! fuzz_target {
};
};

(|mut $bytes:ident| $body:expr) => {
const _: () = {
/// Auto-generated function
#[no_mangle]
pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) -> i32 {
// When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
// formatting of the input to that file. This is only intended for
// `cargo fuzz`'s use!

// `RUST_LIBFUZZER_DEBUG_PATH` is set in initialization.
if let Some(path) = $crate::RUST_LIBFUZZER_DEBUG_PATH.get() {
use std::io::Write;
let mut file = std::fs::File::create(path)
.expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file");
writeln!(&mut file, "{:?}", bytes)
.expect("failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file");
return 0;
}

__libfuzzer_sys_run(bytes);
0
}

// Split out the actual fuzzer into a separate function which is
// tagged as never being inlined. This ensures that if the fuzzer
// panics there's at least one stack frame which is named uniquely
// according to this specific fuzzer that this is embedded within.
//
// Systems like oss-fuzz try to deduplicate crashes and without this
// panics in separate fuzzers can accidentally appear the same
// because each fuzzer will have a function called
// `rust_fuzzer_test_input`. By using a normal Rust function here
// it's named something like `the_fuzzer_name::_::__libfuzzer_sys_run` which should
// ideally help prevent oss-fuzz from deduplicate fuzz bugs across
// distinct targets accidentally.
#[inline(never)]
fn __libfuzzer_sys_run(mut $bytes: &[u8]) {
$body
}
};
};

(|$data:ident: &[u8]| $body:expr) => {
$crate::fuzz_target!(|$data| $body);
};

(|mut $data:ident: &[u8]| $body:expr) => {
$crate::fuzz_target!(|mut $data| $body);
};

(|$data:ident: $dty:ty| $body:expr) => {
$crate::fuzz_target!(|$data: $dty| -> () { $body });
};

(|mut $data:ident: $dty:ty| $body:expr) => {
$crate::fuzz_target!(|mut $data: $dty| -> () { $body });
};

(|$data:ident: $dty:ty| -> $rty:ty $body:block) => {
const _: () = {
/// Auto-generated function
Expand Down Expand Up @@ -306,6 +356,60 @@ macro_rules! fuzz_target {
}
};
};

(|mut $data:ident: $dty:ty| -> $rty:ty $body:block) => {
const _: () = {
/// Auto-generated function
#[no_mangle]
pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) -> i32 {
use $crate::arbitrary::{Arbitrary, Unstructured};

// Early exit if we don't have enough bytes for the `Arbitrary`
// implementation. This helps the fuzzer avoid exploring all the
// different not-enough-input-bytes paths inside the `Arbitrary`
// implementation. Additionally, it exits faster, letting the fuzzer
// get to longer inputs that actually lead to interesting executions
// quicker.
if bytes.len() < <$dty as Arbitrary>::size_hint(0).0 {
return -1;
}

let mut u = Unstructured::new(bytes);
let data = <$dty as Arbitrary>::arbitrary_take_rest(u);

// When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
// formatting of the input to that file. This is only intended for
// `cargo fuzz`'s use!

// `RUST_LIBFUZZER_DEBUG_PATH` is set in initialization.
if let Some(path) = $crate::RUST_LIBFUZZER_DEBUG_PATH.get() {
use std::io::Write;
let mut file = std::fs::File::create(path)
.expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file");
(match data {
Ok(data) => writeln!(&mut file, "{:#?}", data),
Err(err) => writeln!(&mut file, "Arbitrary Error: {}", err),
})
.expect("failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file");
return -1;
}

let data = match data {
Ok(d) => d,
Err(_) => return -1,
};

let result = ::libfuzzer_sys::Corpus::from(__libfuzzer_sys_run(data));
result.to_libfuzzer_code()
}

// See above for why this is split to a separate function.
#[inline(never)]
fn __libfuzzer_sys_run(mut $data: $dty) -> $rty {
$body
}
};
};
}

/// Define a custom mutator.
Expand Down

0 comments on commit 31a75ba

Please sign in to comment.