14
14
pub use arbitrary;
15
15
use once_cell:: sync:: OnceCell ;
16
16
17
+ /// Indicates whether the input should be kept in the corpus or rejected. This
18
+ /// should be returned by your fuzz target. If your fuzz target does not return
19
+ /// a value (i.e., returns `()`), then the input will be kept in the corpus.
20
+ #[ derive( Debug ) ]
21
+ pub enum Corpus {
22
+ /// Keep the input in the corpus.
23
+ Keep ,
24
+
25
+ /// Reject the input and do not keep it in the corpus.
26
+ Reject ,
27
+ }
28
+
29
+ impl From < ( ) > for Corpus {
30
+ fn from ( _: ( ) ) -> Self {
31
+ Self :: Keep
32
+ }
33
+ }
34
+
35
+ impl From < Corpus > for i32 {
36
+ fn from ( value : Corpus ) -> i32 {
37
+ match value {
38
+ Corpus :: Keep => 0 ,
39
+ Corpus :: Reject => -1 ,
40
+ }
41
+ }
42
+ }
43
+
17
44
extern "C" {
18
45
// We do not actually cross the FFI bound here.
19
46
#[ allow( improper_ctypes) ]
20
- fn rust_fuzzer_test_input ( input : & [ u8 ] ) ;
47
+ fn rust_fuzzer_test_input ( input : & [ u8 ] ) -> i32 ;
21
48
22
49
fn LLVMFuzzerMutate ( data : * mut u8 , size : usize , max_size : usize ) -> usize ;
23
50
}
@@ -27,14 +54,17 @@ extern "C" {
27
54
pub fn test_input_wrap ( data : * const u8 , size : usize ) -> i32 {
28
55
let test_input = :: std:: panic:: catch_unwind ( || unsafe {
29
56
let data_slice = :: std:: slice:: from_raw_parts ( data, size) ;
30
- rust_fuzzer_test_input ( data_slice) ;
57
+ rust_fuzzer_test_input ( data_slice)
31
58
} ) ;
32
- if test_input. err ( ) . is_some ( ) {
33
- // hopefully the custom panic hook will be called before and abort the
34
- // process before the stack frames are unwinded.
35
- :: std:: process:: abort ( ) ;
59
+
60
+ match test_input {
61
+ Ok ( i) => i,
62
+ Err ( _) => {
63
+ // hopefully the custom panic hook will be called before and abort the
64
+ // process before the stack frames are unwinded.
65
+ :: std:: process:: abort ( ) ;
66
+ }
36
67
}
37
- 0
38
68
}
39
69
40
70
#[ doc( hidden) ]
@@ -86,6 +116,30 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize
86
116
/// # mod my_crate { pub fn parse(_: &[u8]) -> Result<(), ()> { unimplemented!() } }
87
117
/// ```
88
118
///
119
+ /// ## Rejecting Inputs
120
+ ///
121
+ /// To indicate whether an input should be kept in or rejected from the corpus,
122
+ /// return a [Corpus] value from your fuzz target. For example:
123
+ ///
124
+ /// ```no_run
125
+ /// #![no_main]
126
+ ///
127
+ /// use libfuzzer_sys::{Corpus, fuzz_target};
128
+ ///
129
+ /// fuzz_target!(|input: String| -> Corpus {
130
+ /// let parts: Vec<&str> = input.splitn(2, '=').collect();
131
+ /// if parts.len() != 2 {
132
+ /// return Corpus::Reject;
133
+ /// }
134
+ ///
135
+ /// let key = parts[0];
136
+ /// let value = parts[1];
137
+ /// my_crate::parse(key, value);
138
+ /// Corpus::Keep
139
+ /// );
140
+ /// # mod my_crate { pub fn parse(_key: &str, _value: &str) -> Result<(), ()> { unimplemented!() } }
141
+ /// ```
142
+ ///
89
143
/// ## Arbitrary Input Types
90
144
///
91
145
/// The input is a `&[u8]` slice by default, but you can take arbitrary input
@@ -139,7 +193,7 @@ macro_rules! fuzz_target {
139
193
const _: ( ) = {
140
194
/// Auto-generated function
141
195
#[ no_mangle]
142
- pub extern "C" fn rust_fuzzer_test_input( bytes: & [ u8 ] ) {
196
+ pub extern "C" fn rust_fuzzer_test_input( bytes: & [ u8 ] ) -> i32 {
143
197
// When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
144
198
// formatting of the input to that file. This is only intended for
145
199
// `cargo fuzz`'s use!
@@ -154,7 +208,8 @@ macro_rules! fuzz_target {
154
208
return ;
155
209
}
156
210
157
- run( bytes)
211
+ run( bytes) ;
212
+ 0
158
213
}
159
214
160
215
// Split out the actual fuzzer into a separate function which is
@@ -181,10 +236,14 @@ macro_rules! fuzz_target {
181
236
} ;
182
237
183
238
( |$data: ident: $dty: ty| $body: block) => {
239
+ $crate:: fuzz_target!( |$data: $dty| -> ( ) $body) ;
240
+ } ;
241
+
242
+ ( |$data: ident: $dty: ty| -> $rty: ty $body: block) => {
184
243
const _: ( ) = {
185
244
/// Auto-generated function
186
245
#[ no_mangle]
187
- pub extern "C" fn rust_fuzzer_test_input( bytes: & [ u8 ] ) {
246
+ pub extern "C" fn rust_fuzzer_test_input( bytes: & [ u8 ] ) -> i32 {
188
247
use $crate:: arbitrary:: { Arbitrary , Unstructured } ;
189
248
190
249
// Early exit if we don't have enough bytes for the `Arbitrary`
@@ -194,7 +253,7 @@ macro_rules! fuzz_target {
194
253
// get to longer inputs that actually lead to interesting executions
195
254
// quicker.
196
255
if bytes. len( ) < <$dty as Arbitrary >:: size_hint( 0 ) . 0 {
197
- return ;
256
+ return - 1 ;
198
257
}
199
258
200
259
let mut u = Unstructured :: new( bytes) ;
@@ -214,20 +273,21 @@ macro_rules! fuzz_target {
214
273
Err ( err) => writeln!( & mut file, "Arbitrary Error: {}" , err) ,
215
274
} )
216
275
. expect( "failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file" ) ;
217
- return ;
276
+ return - 1 ;
218
277
}
219
278
220
279
let data = match data {
221
280
Ok ( d) => d,
222
- Err ( _) => return ,
281
+ Err ( _) => return - 1 ,
223
282
} ;
224
283
225
- run( data)
284
+ let result: i32 = :: libfuzzer_sys:: Corpus :: from( run( data) ) . into( ) ;
285
+ result
226
286
}
227
287
228
288
// See above for why this is split to a separate function.
229
289
#[ inline( never) ]
230
- fn run( $data: $dty) {
290
+ fn run( $data: $dty) -> $rty {
231
291
$body
232
292
}
233
293
} ;
0 commit comments