Skip to content

Commit 785aa9c

Browse files
PaulGrandperrinManishearth
authored andcommitted
Change API to be the same as Honggfuzz-rs and eventually AFL.rs
closes rust-fuzz/cargo-fuzz#119 closes rust-fuzz/cargo-fuzz#101
1 parent 0c45075 commit 785aa9c

File tree

4 files changed

+107
-80
lines changed

4 files changed

+107
-80
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ os:
88
- linux
99
env:
1010
- ARCH=x86_64
11+
- ASAN_OPTIONS=detect_odr_violation=0
1112
notifications:
1213
email: false
1314
script:

example/src/main.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
#![no_main]
1+
use libfuzzer_sys::fuzz;
22

3-
use libfuzzer_sys::fuzz_target;
4-
5-
fuzz_target!(|data: &[u8]| {
6-
if data == b"banana!" {
7-
panic!("success!");
8-
}
9-
});
3+
fn main() {
4+
fuzz!(|data: &[u8]| {
5+
if data == b"banana!" {
6+
panic!("success!");
7+
}
8+
});
9+
}

example_arbitrary/src/main.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,22 @@
1-
#![no_main]
1+
use libfuzzer_sys::fuzz;
22

3-
use libfuzzer_sys::fuzz_target;
3+
fn main() {
4+
// Here you can parse `std::env::args and
5+
// setup / initialize your project
46

5-
fuzz_target!(|data: u16| {
6-
if data == 0xba7 { // ba[nana]
7-
panic!("success!");
8-
}
9-
});
7+
// The fuzz macro gives an arbitrary object (see `arbitrary crate`)
8+
// to a closure-like block of code.
9+
// For performance, it is recommended that you use the native type
10+
// `&[u8]` when possible.
11+
// Here, this slice will contain a "random" quantity of "random" data.
12+
fuzz!(|data: &[u8]| {
13+
if data.len() != 6 {return}
14+
if data[0] != b'q' {return}
15+
if data[1] != b'w' {return}
16+
if data[2] != b'e' {return}
17+
if data[3] != b'r' {return}
18+
if data[4] != b't' {return}
19+
if data[5] != b'y' {return}
20+
panic!("BOOM")
21+
});
22+
}

src/lib.rs

Lines changed: 78 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -13,44 +13,79 @@
1313

1414
pub use arbitrary;
1515

16+
17+
use std::os::raw::c_char;
18+
use std::os::raw::c_int;
19+
use std::ffi::CString;
20+
use std::{panic, ptr};
21+
1622
extern "C" {
17-
// We do not actually cross the FFI bound here.
18-
#[allow(improper_ctypes)]
19-
fn rust_fuzzer_test_input(input: &[u8]);
23+
// This is the mangled name of the C++ function starting the fuzzer
24+
fn _ZN6fuzzer12FuzzerDriverEPiPPPcPFiPKhmE(argc: *mut c_int, argv: *mut *mut *mut c_char, callback: extern fn(*const u8, usize) -> c_int );
2025
}
2126

27+
static mut STATIC_CLOSURE: *const () = ptr::null();
28+
2229
#[doc(hidden)]
23-
#[export_name = "LLVMFuzzerTestOneInput"]
24-
pub fn test_input_wrap(data: *const u8, size: usize) -> i32 {
25-
let test_input = ::std::panic::catch_unwind(|| unsafe {
30+
pub extern "C" fn test_one_input<F>(data: *const u8, size: usize) -> c_int where F: Fn(&[u8]) + panic::RefUnwindSafe {
31+
unsafe {
2632
let data_slice = ::std::slice::from_raw_parts(data, size);
27-
rust_fuzzer_test_input(data_slice);
28-
});
29-
if test_input.err().is_some() {
30-
// hopefully the custom panic hook will be called before and abort the
31-
// process before the stack frames are unwinded.
32-
::std::process::abort();
33+
let closure = STATIC_CLOSURE as *const F;
34+
// We still catch unwinding panics just in case the fuzzed code modifies
35+
// the panic hook.
36+
// If so, the fuzzer will be unable to tell different bugs appart and you will
37+
// only be able to find one bug at a time before fixing it to then find a new one.
38+
let did_panic = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
39+
(&*closure)(data_slice);
40+
})).is_err();
41+
42+
if did_panic {
43+
// hopefully the custom panic hook will be called before and abort the
44+
// process before the stack frames are unwinded.
45+
std::process::abort();
46+
}
3347
}
3448
0
3549
}
3650

37-
#[doc(hidden)]
38-
#[export_name = "LLVMFuzzerInitialize"]
39-
pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize {
51+
/// Run libfuzzer with a given closure
52+
///
53+
/// This is the undelying API used by the [`fuzz!()`] macro, use that instead where possible.
54+
pub fn fuzz<F>(closure: F) where F: Fn(&[u8]) + std::panic::RefUnwindSafe + Sync + Send {
55+
// Converts env::args() to C format
56+
let args = std::env::args()
57+
.map(|arg| CString::new(arg).unwrap()) // convert args to null terminated C strings
58+
.collect::<Vec<_>>();
59+
let c_args = args.iter()
60+
.map(|arg| arg.as_ptr())
61+
.chain(std::iter::once(std::ptr::null())) // C standard expects the array of args to be null terminated
62+
.collect::<Vec<*const c_char>>();
63+
64+
let mut argc = c_args.len() as c_int - 1;
65+
let mut argv = c_args.as_ptr() as *mut *mut c_char;
66+
4067
// Registers a panic hook that aborts the process before unwinding.
4168
// It is useful to abort before unwinding so that the fuzzer will then be
4269
// able to analyse the process stack frames to tell different bugs appart.
43-
//
70+
//
4471
// HACK / FIXME: it would be better to use `-C panic=abort` but it's currently
4572
// impossible to build code using compiler plugins with this flag.
4673
// We will be able to remove this code when
4774
// https://github.com/rust-lang/cargo/issues/5423 is fixed.
48-
let default_hook = ::std::panic::take_hook();
49-
::std::panic::set_hook(Box::new(move |panic_info| {
75+
let default_hook = panic::take_hook();
76+
panic::set_hook(Box::new(move |panic_info| {
5077
default_hook(panic_info);
51-
::std::process::abort();
78+
std::process::abort();
5279
}));
53-
0
80+
81+
unsafe {
82+
assert!(STATIC_CLOSURE.is_null());
83+
// save closure capture at static location
84+
STATIC_CLOSURE = Box::into_raw(Box::new(closure)) as *const ();
85+
86+
// call C++ mangled method `fuzzer::FuzzerDriver()`
87+
_ZN6fuzzer12FuzzerDriverEPiPPPcPFiPKhmE(&mut argc, &mut argv, test_one_input::<F>);
88+
}
5489
}
5590

5691
/// Define a fuzz target.
@@ -61,9 +96,7 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize
6196
/// might fail and return an `Err`, but it shouldn't ever panic or segfault.
6297
///
6398
/// ```no_run
64-
/// #![no_main]
65-
///
66-
/// use libfuzzer_sys::fuzz_target;
99+
/// use libfuzzer_sys::fuzz;
67100
///
68101
/// // Note: `|input|` is short for `|input: &[u8]|`.
69102
/// fuzz_target!(|input| {
@@ -83,65 +116,45 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize
83116
/// following:
84117
///
85118
/// ```no_run
86-
/// #![no_main]
87-
///
88-
/// use libfuzzer_sys::{arbitrary::{Arbitrary, Unstructured}, fuzz_target};
119+
/// use libfuzzer_sys::{arbitrary, fuzz};
89120
///
90-
/// #[derive(Debug)]
121+
/// #[derive(Debug, arbitrary::Arbitrary)]
91122
/// pub struct Rgb {
92123
/// r: u8,
93124
/// g: u8,
94125
/// b: u8,
95126
/// }
96127
///
97-
/// impl Arbitrary for Rgb {
98-
/// fn arbitrary<U>(raw: &mut U) -> Result<Self, U::Error>
99-
/// where
100-
/// U: Unstructured + ?Sized
101-
/// {
102-
/// let mut buf = [0; 3];
103-
/// raw.fill_buffer(&mut buf)?;
104-
/// let r = buf[0];
105-
/// let g = buf[1];
106-
/// let b = buf[2];
107-
/// Ok(Rgb { r, g, b })
108-
/// }
109-
/// }
110-
///
111128
/// // Write a fuzz target that works with RGB colors instead of raw bytes.
112-
/// fuzz_target!(|color: Rgb| {
129+
/// fuzz!(|color: Rgb| {
113130
/// my_crate::convert_color(color);
114131
/// });
115132
/// # mod my_crate { fn convert_color(_: super::Rgb) {} }
116133
#[macro_export]
117-
macro_rules! fuzz_target {
118-
(|$bytes:ident| $body:block) => {
119-
#[no_mangle]
120-
pub extern "C" fn rust_fuzzer_test_input($bytes: &[u8]) {
121-
$body
122-
}
134+
macro_rules! fuzz {
135+
(|$buf:ident| $body:block) => {
136+
$crate::fuzz(|$buf| $body);
123137
};
124-
125-
(|$data:ident: &[u8]| $body:block) => {
126-
fuzz_target!(|$data| $body);
138+
(|$buf:ident: &[u8]| $body:block) => {
139+
$crate::fuzz(|$buf| $body);
127140
};
141+
(|$buf:ident: $dty: ty| $body:block) => {
142+
$crate::fuzz(|$buf| {
143+
let $buf: $dty = {
144+
use $crate::arbitrary::{Arbitrary, RingBuffer};
145+
let mut buf = match RingBuffer::new($buf, $buf.len()) {
146+
Ok(b) => b,
147+
Err(_) => return,
148+
};
128149

129-
(|$data:ident: $dty: ty| $body:block) => {
130-
#[no_mangle]
131-
pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) {
132-
use libfuzzer_sys::arbitrary::{Arbitrary, RingBuffer};
133-
134-
let mut buf = match RingBuffer::new(bytes, bytes.len()) {
135-
Ok(b) => b,
136-
Err(_) => return,
137-
};
138-
139-
let $data: $dty = match Arbitrary::arbitrary(&mut buf) {
140-
Ok(d) => d,
141-
Err(_) => return,
150+
let d: $dty = match Arbitrary::arbitrary(&mut buf) {
151+
Ok(d) => d,
152+
Err(_) => return,
153+
};
154+
d
142155
};
143156

144157
$body
145-
}
158+
});
146159
};
147160
}

0 commit comments

Comments
 (0)