Skip to content

Commit 31a75ba

Browse files
committed
Add support for mut in the fuzz_target! macro
1 parent 9848925 commit 31a75ba

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Released YYYY-MM-DD.
66

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

1011
### Changed
1112

src/lib.rs

+104
Original file line numberDiff line numberDiff line change
@@ -245,14 +245,64 @@ macro_rules! fuzz_target {
245245
};
246246
};
247247

248+
(|mut $bytes:ident| $body:expr) => {
249+
const _: () = {
250+
/// Auto-generated function
251+
#[no_mangle]
252+
pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) -> i32 {
253+
// When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
254+
// formatting of the input to that file. This is only intended for
255+
// `cargo fuzz`'s use!
256+
257+
// `RUST_LIBFUZZER_DEBUG_PATH` is set in initialization.
258+
if let Some(path) = $crate::RUST_LIBFUZZER_DEBUG_PATH.get() {
259+
use std::io::Write;
260+
let mut file = std::fs::File::create(path)
261+
.expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file");
262+
writeln!(&mut file, "{:?}", bytes)
263+
.expect("failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file");
264+
return 0;
265+
}
266+
267+
__libfuzzer_sys_run(bytes);
268+
0
269+
}
270+
271+
// Split out the actual fuzzer into a separate function which is
272+
// tagged as never being inlined. This ensures that if the fuzzer
273+
// panics there's at least one stack frame which is named uniquely
274+
// according to this specific fuzzer that this is embedded within.
275+
//
276+
// Systems like oss-fuzz try to deduplicate crashes and without this
277+
// panics in separate fuzzers can accidentally appear the same
278+
// because each fuzzer will have a function called
279+
// `rust_fuzzer_test_input`. By using a normal Rust function here
280+
// it's named something like `the_fuzzer_name::_::__libfuzzer_sys_run` which should
281+
// ideally help prevent oss-fuzz from deduplicate fuzz bugs across
282+
// distinct targets accidentally.
283+
#[inline(never)]
284+
fn __libfuzzer_sys_run(mut $bytes: &[u8]) {
285+
$body
286+
}
287+
};
288+
};
289+
248290
(|$data:ident: &[u8]| $body:expr) => {
249291
$crate::fuzz_target!(|$data| $body);
250292
};
251293

294+
(|mut $data:ident: &[u8]| $body:expr) => {
295+
$crate::fuzz_target!(|mut $data| $body);
296+
};
297+
252298
(|$data:ident: $dty:ty| $body:expr) => {
253299
$crate::fuzz_target!(|$data: $dty| -> () { $body });
254300
};
255301

302+
(|mut $data:ident: $dty:ty| $body:expr) => {
303+
$crate::fuzz_target!(|mut $data: $dty| -> () { $body });
304+
};
305+
256306
(|$data:ident: $dty:ty| -> $rty:ty $body:block) => {
257307
const _: () = {
258308
/// Auto-generated function
@@ -306,6 +356,60 @@ macro_rules! fuzz_target {
306356
}
307357
};
308358
};
359+
360+
(|mut $data:ident: $dty:ty| -> $rty:ty $body:block) => {
361+
const _: () = {
362+
/// Auto-generated function
363+
#[no_mangle]
364+
pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) -> i32 {
365+
use $crate::arbitrary::{Arbitrary, Unstructured};
366+
367+
// Early exit if we don't have enough bytes for the `Arbitrary`
368+
// implementation. This helps the fuzzer avoid exploring all the
369+
// different not-enough-input-bytes paths inside the `Arbitrary`
370+
// implementation. Additionally, it exits faster, letting the fuzzer
371+
// get to longer inputs that actually lead to interesting executions
372+
// quicker.
373+
if bytes.len() < <$dty as Arbitrary>::size_hint(0).0 {
374+
return -1;
375+
}
376+
377+
let mut u = Unstructured::new(bytes);
378+
let data = <$dty as Arbitrary>::arbitrary_take_rest(u);
379+
380+
// When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
381+
// formatting of the input to that file. This is only intended for
382+
// `cargo fuzz`'s use!
383+
384+
// `RUST_LIBFUZZER_DEBUG_PATH` is set in initialization.
385+
if let Some(path) = $crate::RUST_LIBFUZZER_DEBUG_PATH.get() {
386+
use std::io::Write;
387+
let mut file = std::fs::File::create(path)
388+
.expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file");
389+
(match data {
390+
Ok(data) => writeln!(&mut file, "{:#?}", data),
391+
Err(err) => writeln!(&mut file, "Arbitrary Error: {}", err),
392+
})
393+
.expect("failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file");
394+
return -1;
395+
}
396+
397+
let data = match data {
398+
Ok(d) => d,
399+
Err(_) => return -1,
400+
};
401+
402+
let result = ::libfuzzer_sys::Corpus::from(__libfuzzer_sys_run(data));
403+
result.to_libfuzzer_code()
404+
}
405+
406+
// See above for why this is split to a separate function.
407+
#[inline(never)]
408+
fn __libfuzzer_sys_run(mut $data: $dty) -> $rty {
409+
$body
410+
}
411+
};
412+
};
309413
}
310414

311415
/// Define a custom mutator.

0 commit comments

Comments
 (0)