diff --git a/CHANGELOG.md b/CHANGELOG.md index 83c8894..913abfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/lib.rs b/src/lib.rs index 1cd12ee..03593e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 @@ -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.