1- extern "C" {
2- #![ allow( improper_ctypes) ] // we do not actually cross the FFI bound here
1+ use std:: os:: raw:: c_char;
2+ use std:: os:: raw:: c_int;
3+ use std:: ffi:: CString ;
34
4- fn rust_fuzzer_test_input ( input : & [ u8 ] ) ;
5+ extern "C" {
6+ // This is the mangled name of the C++ function starting the fuzzer
7+ fn _ZN6fuzzer12FuzzerDriverEPiPPPcPFiPKhmE ( argc : * mut c_int , argv : * mut * mut * mut c_char , callback : extern fn ( * const u8 , usize ) -> c_int ) ;
58}
69
7- #[ export_name="LLVMFuzzerTestOneInput" ]
8- pub fn test_input_wrap ( data : * const u8 , size : usize ) -> i32 {
9- :: std:: panic:: catch_unwind ( || unsafe {
10+ static mut STATIC_CLOSURE : Option < Box < FnMut ( & [ u8 ] ) > > = None ;
11+
12+ // #[no_mangle]
13+ // pub extern "C" fn LLVMFuzzerInitialize(_argc: *const isize, _argv: *const *const *const u8) -> isize {
14+ // 0
15+ // }
16+
17+ #[ no_mangle]
18+ pub extern "C" fn LLVMFuzzerTestOneInput ( data : * const u8 , size : usize ) -> c_int {
19+ unsafe {
1020 let data_slice = :: std:: slice:: from_raw_parts ( data, size) ;
11- rust_fuzzer_test_input ( data_slice) ;
12- } )
13- . err ( ) . map ( |_|
14- // hopefully the custom panic hook will be called before and abort the
15- // process before the stack frames are unwinded.
16- :: std:: process:: abort ( )
17- ) ;
21+ if let Some ( ref mut closure) = STATIC_CLOSURE {
22+ // We still catch unwinding panics just in case the fuzzed code modifies
23+ // the panic hook.
24+ // If so, the fuzzer will be unable to tell different bugs appart and you will
25+ // only be able to find one bug at a time before fixing it to then find a new one.
26+ let did_panic = std:: panic:: catch_unwind ( std:: panic:: AssertUnwindSafe ( || {
27+ closure ( data_slice) ;
28+ } ) ) . is_err ( ) ;
29+
30+ if did_panic {
31+ // hopefully the custom panic hook will be called before and abort the
32+ // process before the stack frames are unwinded.
33+ std:: process:: abort ( ) ;
34+ }
35+ }
36+ }
1837 0
1938}
2039
21- #[ export_name="LLVMFuzzerInitialize" ]
22- pub fn initialize ( _argc : * const isize , _argv : * const * const * const u8 ) -> isize {
40+ pub fn fuzz < F : std:: marker:: Sync + ' static > ( closure : F ) where F : Fn ( & [ u8 ] ) + std:: panic:: RefUnwindSafe {
41+ // Converts env::args() to C format
42+ let args = std:: env:: args ( )
43+ . map ( |arg| CString :: new ( arg) . unwrap ( ) ) // convert args to null terminated C strings
44+ . collect :: < Vec < _ > > ( ) ;
45+ let c_args = args. iter ( )
46+ . map ( |arg| arg. as_ptr ( ) )
47+ . chain ( std:: iter:: once ( std:: ptr:: null ( ) ) ) // C standard expects the array of args to be null terminated
48+ . collect :: < Vec < * const c_char > > ( ) ;
49+
50+ let mut argc = c_args. len ( ) as c_int - 1 ;
51+ let mut argv = c_args. as_ptr ( ) as * mut * mut c_char ;
52+
2353 // Registers a panic hook that aborts the process before unwinding.
2454 // It is useful to abort before unwinding so that the fuzzer will then be
2555 // able to analyse the process stack frames to tell different bugs appart.
@@ -28,38 +58,41 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize
2858 // impossible to build code using compiler plugins with this flag.
2959 // We will be able to remove this code when
3060 // https://github.com/rust-lang/cargo/issues/5423 is fixed.
31- :: std:: panic:: set_hook ( Box :: new ( |_| {
32- :: std:: process:: abort ( ) ;
61+ std:: panic:: set_hook ( Box :: new ( |_| {
62+ std:: process:: abort ( ) ;
3363 } ) ) ;
34- 0
64+
65+ unsafe {
66+ // save closure at static location
67+ STATIC_CLOSURE = Some ( Box :: new ( closure) ) ;
68+
69+ // call C++ mangled method `fuzzer::FuzzerDriver()`
70+ _ZN6fuzzer12FuzzerDriverEPiPPPcPFiPKhmE ( & mut argc, & mut argv, LLVMFuzzerTestOneInput ) ;
71+ }
3572}
3673
3774#[ macro_export]
38- macro_rules! fuzz_target {
39- ( |$bytes: ident| $body: block) => {
40- #[ no_mangle]
41- pub extern fn rust_fuzzer_test_input( $bytes: & [ u8 ] ) {
42- $body
43- }
75+ macro_rules! fuzz {
76+ ( |$buf: ident| $body: block) => {
77+ libfuzzer_sys:: fuzz( |$buf| $body) ;
4478 } ;
45- ( |$data : ident: & [ u8 ] | $body: block) => {
46- fuzz_target! ( |$data | $body) ;
79+ ( |$buf : ident: & [ u8 ] | $body: block) => {
80+ libfuzzer_sys :: fuzz ( |$buf | $body) ;
4781 } ;
48- ( |$data: ident: $dty: ty| $body: block) => {
49- extern crate arbitrary;
50-
51- #[ no_mangle]
52- pub extern fn rust_fuzzer_test_input( bytes: & [ u8 ] ) {
53- use arbitrary:: { Arbitrary , RingBuffer } ;
54-
55- let $data: $dty = if let Ok ( d) = RingBuffer :: new( bytes, bytes. len( ) ) . and_then( |mut b|{
56- Arbitrary :: arbitrary( & mut b) . map_err( |_| "" )
57- } ) {
58- d
59- } else {
60- return
82+ ( |$buf: ident: $dty: ty| $body: block) => {
83+ libfuzzer_sys:: fuzz( |$buf| {
84+ let $buf: $dty = {
85+ use arbitrary:: { Arbitrary , RingBuffer } ;
86+ if let Ok ( d) = RingBuffer :: new( $buf, $buf. len( ) ) . and_then( |mut b|{
87+ Arbitrary :: arbitrary( & mut b) . map_err( |_| "" )
88+ } ) {
89+ d
90+ } else {
91+ return
92+ }
6193 } ;
94+
6295 $body
63- }
96+ } ) ;
6497 } ;
6598}
0 commit comments