Skip to content

Commit

Permalink
Change API to be the same as Honggfuzz-rs and eventually AFL.rs
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulGrandperrin committed Apr 28, 2018
1 parent 86bcaac commit 21b912f
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 47 deletions.
27 changes: 20 additions & 7 deletions example/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
#![no_main]

#[macro_use]
extern crate libfuzzer_sys;

fuzz_target!(|data: &[u8]| {
if data == b"banana!" {
panic!("success!");
}
});
fn main() {
// Here you can parse `std::env::args and
// setup / initialize your project

// The fuzz macro gives an arbitrary object (see `arbitrary crate`)
// to a closure-like block of code.
// For performance, it is recommended that you use the native type
// `&[u8]` when possible.
// Here, this slice will contain a "random" quantity of "random" data.
fuzz!(|data: &[u8]| {
if data.len() != 6 {return}
if data[0] != b'q' {return}
if data[1] != b'w' {return}
if data[2] != b'e' {return}
if data[3] != b'r' {return}
if data[4] != b't' {return}
if data[5] != b'y' {return}
panic!("BOOM")
});
}
113 changes: 73 additions & 40 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,55 @@
extern "C" {
#![allow(improper_ctypes)] // we do not actually cross the FFI bound here
use std::os::raw::c_char;
use std::os::raw::c_int;
use std::ffi::CString;

fn rust_fuzzer_test_input(input: &[u8]);
extern "C" {
// This is the mangled name of the C++ function starting the fuzzer
fn _ZN6fuzzer12FuzzerDriverEPiPPPcPFiPKhmE(argc: *mut c_int, argv: *mut *mut *mut c_char, callback: extern fn(*const u8, usize) -> c_int );
}

#[export_name="LLVMFuzzerTestOneInput"]
pub fn test_input_wrap(data: *const u8, size: usize) -> i32 {
::std::panic::catch_unwind(|| unsafe {
static mut STATIC_CLOSURE: Option<Box<FnMut(&[u8])>> = None;

// #[no_mangle]
// pub extern "C" fn LLVMFuzzerInitialize(_argc: *const isize, _argv: *const *const *const u8) -> isize {
// 0
// }

#[no_mangle]
pub extern "C" fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> c_int {
unsafe {
let data_slice = ::std::slice::from_raw_parts(data, size);
rust_fuzzer_test_input(data_slice);
})
.err().map(|_|
// hopefully the custom panic hook will be called before and abort the
// process before the stack frames are unwinded.
::std::process::abort()
);
if let Some(ref mut closure) = STATIC_CLOSURE {
// We still catch unwinding panics just in case the fuzzed code modifies
// the panic hook.
// If so, the fuzzer will be unable to tell different bugs appart and you will
// only be able to find one bug at a time before fixing it to then find a new one.
let did_panic = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
closure(data_slice);
})).is_err();

if did_panic {
// hopefully the custom panic hook will be called before and abort the
// process before the stack frames are unwinded.
std::process::abort();
}
}
}
0
}

#[export_name="LLVMFuzzerInitialize"]
pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize {
pub fn fuzz<F:std::marker::Sync + 'static>(closure: F) where F: Fn(&[u8]) + std::panic::RefUnwindSafe {
// Converts env::args() to C format
let args = std::env::args()
.map(|arg| CString::new(arg).unwrap()) // convert args to null terminated C strings
.collect::<Vec<_>>();
let c_args = args.iter()
.map(|arg| arg.as_ptr())
.chain(std::iter::once(std::ptr::null())) // C standard expects the array of args to be null terminated
.collect::<Vec<*const c_char>>();

let mut argc = c_args.len() as c_int - 1;
let mut argv = c_args.as_ptr() as *mut *mut c_char;

// Registers a panic hook that aborts the process before unwinding.
// It is useful to abort before unwinding so that the fuzzer will then be
// able to analyse the process stack frames to tell different bugs appart.
Expand All @@ -28,38 +58,41 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize
// impossible to build code using compiler plugins with this flag.
// We will be able to remove this code when
// https://github.com/rust-lang/cargo/issues/5423 is fixed.
::std::panic::set_hook(Box::new(|_| {
::std::process::abort();
std::panic::set_hook(Box::new(|_| {
std::process::abort();
}));
0

unsafe {
// save closure at static location
STATIC_CLOSURE = Some(Box::new(closure));

// call C++ mangled method `fuzzer::FuzzerDriver()`
_ZN6fuzzer12FuzzerDriverEPiPPPcPFiPKhmE(&mut argc, &mut argv, LLVMFuzzerTestOneInput);
}
}

#[macro_export]
macro_rules! fuzz_target {
(|$bytes:ident| $body:block) => {
#[no_mangle]
pub extern fn rust_fuzzer_test_input($bytes: &[u8]) {
$body
}
macro_rules! fuzz {
(|$buf:ident| $body:block) => {
libfuzzer_sys::fuzz(|$buf| $body);
};
(|$data:ident: &[u8]| $body:block) => {
fuzz_target!(|$data| $body);
(|$buf:ident: &[u8]| $body:block) => {
libfuzzer_sys::fuzz(|$buf| $body);
};
(|$data:ident: $dty: ty| $body:block) => {
extern crate arbitrary;

#[no_mangle]
pub extern fn rust_fuzzer_test_input(bytes: &[u8]) {
use arbitrary::{Arbitrary, RingBuffer};

let $data: $dty = if let Ok(d) = RingBuffer::new(bytes, bytes.len()).and_then(|mut b|{
Arbitrary::arbitrary(&mut b).map_err(|_| "")
}) {
d
} else {
return
(|$buf:ident: $dty: ty| $body:block) => {
libfuzzer_sys::fuzz(|$buf| {
let $buf: $dty = {
use arbitrary::{Arbitrary, RingBuffer};
if let Ok(d) = RingBuffer::new($buf, $buf.len()).and_then(|mut b|{
Arbitrary::arbitrary(&mut b).map_err(|_| "")
}) {
d
} else {
return
}
};

$body
}
});
};
}

0 comments on commit 21b912f

Please sign in to comment.