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 Corpus {
36
+ #[ doc( hidden) ]
37
+ /// Convert this Corpus result into the [integer codes used by
38
+ /// `libFuzzer`](https://llvm.org/docs/LibFuzzer.html#rejecting-unwanted-inputs).
39
+ /// This is -1 for reject, 0 for keep.
40
+ pub fn to_libfuzzer_code ( self ) -> i32 {
41
+ match self {
42
+ Corpus :: Keep => 0 ,
43
+ Corpus :: Reject => -1 ,
44
+ }
45
+ }
46
+ }
47
+
17
48
extern "C" {
18
49
// We do not actually cross the FFI bound here.
19
50
#[ allow( improper_ctypes) ]
20
- fn rust_fuzzer_test_input ( input : & [ u8 ] ) ;
51
+ fn rust_fuzzer_test_input ( input : & [ u8 ] ) -> i32 ;
21
52
22
53
fn LLVMFuzzerMutate ( data : * mut u8 , size : usize , max_size : usize ) -> usize ;
23
54
}
@@ -27,14 +58,17 @@ extern "C" {
27
58
pub fn test_input_wrap ( data : * const u8 , size : usize ) -> i32 {
28
59
let test_input = :: std:: panic:: catch_unwind ( || unsafe {
29
60
let data_slice = :: std:: slice:: from_raw_parts ( data, size) ;
30
- rust_fuzzer_test_input ( data_slice) ;
61
+ rust_fuzzer_test_input ( data_slice)
31
62
} ) ;
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 ( ) ;
63
+
64
+ match test_input {
65
+ Ok ( i) => i,
66
+ Err ( _) => {
67
+ // hopefully the custom panic hook will be called before and abort the
68
+ // process before the stack frames are unwinded.
69
+ :: std:: process:: abort ( ) ;
70
+ }
36
71
}
37
- 0
38
72
}
39
73
40
74
#[ doc( hidden) ]
@@ -86,6 +120,39 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize
86
120
/// # mod my_crate { pub fn parse(_: &[u8]) -> Result<(), ()> { unimplemented!() } }
87
121
/// ```
88
122
///
123
+ /// ## Rejecting Inputs
124
+ ///
125
+ /// It may be desirable to reject some inputs, i.e. to not add them to the
126
+ /// corpus.
127
+ ///
128
+ /// For example, when fuzzing an API consisting of parsing and other logic,
129
+ /// one may want to allow only those inputs into the corpus that parse
130
+ /// successfully. To indicate whether an input should be kept in or rejected
131
+ /// from the corpus, return either [Corpus::Keep] or [Corpus::Reject] from your
132
+ /// fuzz target. The default behavior (e.g. if `()` is returned) is to keep the
133
+ /// input in the corpus.
134
+ ///
135
+ /// For example:
136
+ ///
137
+ /// ```no_run
138
+ /// #![no_main]
139
+ ///
140
+ /// use libfuzzer_sys::{Corpus, fuzz_target};
141
+ ///
142
+ /// fuzz_target!(|input: String| -> Corpus {
143
+ /// let parts: Vec<&str> = input.splitn(2, '=').collect();
144
+ /// if parts.len() != 2 {
145
+ /// return Corpus::Reject;
146
+ /// }
147
+ ///
148
+ /// let key = parts[0];
149
+ /// let value = parts[1];
150
+ /// let _result: Result<_, _> = my_crate::parse(key, value);
151
+ /// Corpus::Keep
152
+ /// });
153
+ /// # mod my_crate { pub fn parse(_key: &str, _value: &str) -> Result<(), ()> { unimplemented!() } }
154
+ /// ```
155
+ ///
89
156
/// ## Arbitrary Input Types
90
157
///
91
158
/// The input is a `&[u8]` slice by default, but you can take arbitrary input
@@ -139,7 +206,7 @@ macro_rules! fuzz_target {
139
206
const _: ( ) = {
140
207
/// Auto-generated function
141
208
#[ no_mangle]
142
- pub extern "C" fn rust_fuzzer_test_input( bytes: & [ u8 ] ) {
209
+ pub extern "C" fn rust_fuzzer_test_input( bytes: & [ u8 ] ) -> i32 {
143
210
// When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
144
211
// formatting of the input to that file. This is only intended for
145
212
// `cargo fuzz`'s use!
@@ -151,10 +218,11 @@ macro_rules! fuzz_target {
151
218
. expect( "failed to create `RUST_LIBFUZZER_DEBUG_PATH` file" ) ;
152
219
writeln!( & mut file, "{:?}" , bytes)
153
220
. expect( "failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file" ) ;
154
- return ;
221
+ return 0 ;
155
222
}
156
223
157
- run( bytes)
224
+ run( bytes) ;
225
+ 0
158
226
}
159
227
160
228
// Split out the actual fuzzer into a separate function which is
@@ -181,10 +249,14 @@ macro_rules! fuzz_target {
181
249
} ;
182
250
183
251
( |$data: ident: $dty: ty| $body: block) => {
252
+ $crate:: fuzz_target!( |$data: $dty| -> ( ) $body) ;
253
+ } ;
254
+
255
+ ( |$data: ident: $dty: ty| -> $rty: ty $body: block) => {
184
256
const _: ( ) = {
185
257
/// Auto-generated function
186
258
#[ no_mangle]
187
- pub extern "C" fn rust_fuzzer_test_input( bytes: & [ u8 ] ) {
259
+ pub extern "C" fn rust_fuzzer_test_input( bytes: & [ u8 ] ) -> i32 {
188
260
use $crate:: arbitrary:: { Arbitrary , Unstructured } ;
189
261
190
262
// Early exit if we don't have enough bytes for the `Arbitrary`
@@ -194,7 +266,7 @@ macro_rules! fuzz_target {
194
266
// get to longer inputs that actually lead to interesting executions
195
267
// quicker.
196
268
if bytes. len( ) < <$dty as Arbitrary >:: size_hint( 0 ) . 0 {
197
- return ;
269
+ return - 1 ;
198
270
}
199
271
200
272
let mut u = Unstructured :: new( bytes) ;
@@ -214,20 +286,21 @@ macro_rules! fuzz_target {
214
286
Err ( err) => writeln!( & mut file, "Arbitrary Error: {}" , err) ,
215
287
} )
216
288
. expect( "failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file" ) ;
217
- return ;
289
+ return - 1 ;
218
290
}
219
291
220
292
let data = match data {
221
293
Ok ( d) => d,
222
- Err ( _) => return ,
294
+ Err ( _) => return - 1 ,
223
295
} ;
224
296
225
- run( data)
297
+ let result = :: libfuzzer_sys:: Corpus :: from( run( data) ) ;
298
+ result. to_libfuzzer_code( )
226
299
}
227
300
228
301
// See above for why this is split to a separate function.
229
302
#[ inline( never) ]
230
- fn run( $data: $dty) {
303
+ fn run( $data: $dty) -> $rty {
231
304
$body
232
305
}
233
306
} ;
0 commit comments