13
13
14
14
pub use arbitrary;
15
15
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
+
16
22
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 ) ;
20
25
}
21
26
27
+ static mut STATIC_CLOSURE : * const ( ) = ptr:: null ( ) ;
28
+
22
29
#[ 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 {
26
32
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
+ }
33
47
}
34
48
0
35
49
}
36
50
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
+
40
67
// Registers a panic hook that aborts the process before unwinding.
41
68
// It is useful to abort before unwinding so that the fuzzer will then be
42
69
// able to analyse the process stack frames to tell different bugs appart.
43
- //
70
+ //
44
71
// HACK / FIXME: it would be better to use `-C panic=abort` but it's currently
45
72
// impossible to build code using compiler plugins with this flag.
46
73
// We will be able to remove this code when
47
74
// 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| {
50
77
default_hook ( panic_info) ;
51
- :: std:: process:: abort ( ) ;
78
+ std:: process:: abort ( ) ;
52
79
} ) ) ;
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
+ }
54
89
}
55
90
56
91
/// Define a fuzz target.
@@ -61,9 +96,7 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize
61
96
/// might fail and return an `Err`, but it shouldn't ever panic or segfault.
62
97
///
63
98
/// ```no_run
64
- /// #![no_main]
65
- ///
66
- /// use libfuzzer_sys::fuzz_target;
99
+ /// use libfuzzer_sys::fuzz;
67
100
///
68
101
/// // Note: `|input|` is short for `|input: &[u8]|`.
69
102
/// fuzz_target!(|input| {
@@ -83,65 +116,45 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize
83
116
/// following:
84
117
///
85
118
/// ```no_run
86
- /// #![no_main]
87
- ///
88
- /// use libfuzzer_sys::{arbitrary::{Arbitrary, Unstructured}, fuzz_target};
119
+ /// use libfuzzer_sys::{arbitrary, fuzz};
89
120
///
90
- /// #[derive(Debug)]
121
+ /// #[derive(Debug, arbitrary::Arbitrary )]
91
122
/// pub struct Rgb {
92
123
/// r: u8,
93
124
/// g: u8,
94
125
/// b: u8,
95
126
/// }
96
127
///
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
- ///
111
128
/// // Write a fuzz target that works with RGB colors instead of raw bytes.
112
- /// fuzz_target !(|color: Rgb| {
129
+ /// fuzz !(|color: Rgb| {
113
130
/// my_crate::convert_color(color);
114
131
/// });
115
132
/// # mod my_crate { fn convert_color(_: super::Rgb) {} }
116
133
#[ 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) ;
123
137
} ;
124
-
125
- ( |$data: ident: & [ u8 ] | $body: block) => {
126
- fuzz_target!( |$data| $body) ;
138
+ ( |$buf: ident: & [ u8 ] | $body: block) => {
139
+ $crate:: fuzz( |$buf| $body) ;
127
140
} ;
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
+ } ;
128
149
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
142
155
} ;
143
156
144
157
$body
145
- }
158
+ } ) ;
146
159
} ;
147
160
}
0 commit comments