1313
1414pub 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+
1622extern "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