1414pub use arbitrary;
1515use once_cell:: sync:: OnceCell ;
1616
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+
1748extern "C" {
1849 // We do not actually cross the FFI bound here.
1950 #[ allow( improper_ctypes) ]
20- fn rust_fuzzer_test_input ( input : & [ u8 ] ) ;
51+ fn rust_fuzzer_test_input ( input : & [ u8 ] ) -> i32 ;
2152
2253 fn LLVMFuzzerMutate ( data : * mut u8 , size : usize , max_size : usize ) -> usize ;
2354}
@@ -27,14 +58,17 @@ extern "C" {
2758pub fn test_input_wrap ( data : * const u8 , size : usize ) -> i32 {
2859 let test_input = :: std:: panic:: catch_unwind ( || unsafe {
2960 let data_slice = :: std:: slice:: from_raw_parts ( data, size) ;
30- rust_fuzzer_test_input ( data_slice) ;
61+ rust_fuzzer_test_input ( data_slice)
3162 } ) ;
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+ }
3671 }
37- 0
3872}
3973
4074#[ doc( hidden) ]
@@ -86,6 +120,39 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize
86120/// # mod my_crate { pub fn parse(_: &[u8]) -> Result<(), ()> { unimplemented!() } }
87121/// ```
88122///
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+ ///
89156/// ## Arbitrary Input Types
90157///
91158/// The input is a `&[u8]` slice by default, but you can take arbitrary input
@@ -139,7 +206,7 @@ macro_rules! fuzz_target {
139206 const _: ( ) = {
140207 /// Auto-generated function
141208 #[ 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 {
143210 // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
144211 // formatting of the input to that file. This is only intended for
145212 // `cargo fuzz`'s use!
@@ -151,10 +218,11 @@ macro_rules! fuzz_target {
151218 . expect( "failed to create `RUST_LIBFUZZER_DEBUG_PATH` file" ) ;
152219 writeln!( & mut file, "{:?}" , bytes)
153220 . expect( "failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file" ) ;
154- return ;
221+ return 0 ;
155222 }
156223
157- run( bytes)
224+ run( bytes) ;
225+ 0
158226 }
159227
160228 // Split out the actual fuzzer into a separate function which is
@@ -181,10 +249,14 @@ macro_rules! fuzz_target {
181249 } ;
182250
183251 ( |$data: ident: $dty: ty| $body: block) => {
252+ $crate:: fuzz_target!( |$data: $dty| -> ( ) $body) ;
253+ } ;
254+
255+ ( |$data: ident: $dty: ty| -> $rty: ty $body: block) => {
184256 const _: ( ) = {
185257 /// Auto-generated function
186258 #[ 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 {
188260 use $crate:: arbitrary:: { Arbitrary , Unstructured } ;
189261
190262 // Early exit if we don't have enough bytes for the `Arbitrary`
@@ -194,7 +266,7 @@ macro_rules! fuzz_target {
194266 // get to longer inputs that actually lead to interesting executions
195267 // quicker.
196268 if bytes. len( ) < <$dty as Arbitrary >:: size_hint( 0 ) . 0 {
197- return ;
269+ return - 1 ;
198270 }
199271
200272 let mut u = Unstructured :: new( bytes) ;
@@ -214,20 +286,21 @@ macro_rules! fuzz_target {
214286 Err ( err) => writeln!( & mut file, "Arbitrary Error: {}" , err) ,
215287 } )
216288 . expect( "failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file" ) ;
217- return ;
289+ return - 1 ;
218290 }
219291
220292 let data = match data {
221293 Ok ( d) => d,
222- Err ( _) => return ,
294+ Err ( _) => return - 1 ,
223295 } ;
224296
225- run( data)
297+ let result = :: libfuzzer_sys:: Corpus :: from( run( data) ) ;
298+ result. to_libfuzzer_code( )
226299 }
227300
228301 // See above for why this is split to a separate function.
229302 #[ inline( never) ]
230- fn run( $data: $dty) {
303+ fn run( $data: $dty) -> $rty {
231304 $body
232305 }
233306 } ;
0 commit comments