Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Force at least one unique stack frame per fuzzer #95

Merged
merged 1 commit into from Sep 6, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
131 changes: 79 additions & 52 deletions src/lib.rs
Expand Up @@ -136,74 +136,101 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize
#[macro_export]
macro_rules! fuzz_target {
(|$bytes:ident| $body:block) => {
/// Auto-generated function
#[no_mangle]
pub extern "C" fn rust_fuzzer_test_input($bytes: &[u8]) {
// 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!
const _: () = {
fitzgen marked this conversation as resolved.
Show resolved Hide resolved
/// Auto-generated function
#[no_mangle]
pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) {
// 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;
// `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;
}

run(bytes)
}

$body
}
// 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::_::run` which should
// ideally help prevent oss-fuzz from deduplicate fuzz bugs across
// distinct targets accidentally.
#[inline(never)]
fn run($bytes: &[u8]) {
$body
}
};
};

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

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

let mut u = Unstructured::new(bytes);
let data = <$dty as Arbitrary>::arbitrary_take_rest(u);
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!
// 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;
}
// `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;
}

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

$body
}
run(data)
}

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

Expand Down