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 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+
1744extern "C" {
1845 // We do not actually cross the FFI bound here.
1946 #[ allow( improper_ctypes) ]
20- fn rust_fuzzer_test_input ( input : & [ u8 ] ) ;
47+ fn rust_fuzzer_test_input ( input : & [ u8 ] ) -> i32 ;
2148
2249 fn LLVMFuzzerMutate ( data : * mut u8 , size : usize , max_size : usize ) -> usize ;
2350}
@@ -27,14 +54,17 @@ extern "C" {
2754pub fn test_input_wrap ( data : * const u8 , size : usize ) -> i32 {
2855 let test_input = :: std:: panic:: catch_unwind ( || unsafe {
2956 let data_slice = :: std:: slice:: from_raw_parts ( data, size) ;
30- rust_fuzzer_test_input ( data_slice) ;
57+ rust_fuzzer_test_input ( data_slice)
3158 } ) ;
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+ }
3667 }
37- 0
3868}
3969
4070#[ doc( hidden) ]
@@ -86,6 +116,30 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize
86116/// # mod my_crate { pub fn parse(_: &[u8]) -> Result<(), ()> { unimplemented!() } }
87117/// ```
88118///
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+ ///
89143/// ## Arbitrary Input Types
90144///
91145/// The input is a `&[u8]` slice by default, but you can take arbitrary input
@@ -139,7 +193,7 @@ macro_rules! fuzz_target {
139193 const _: ( ) = {
140194 /// Auto-generated function
141195 #[ 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 {
143197 // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
144198 // formatting of the input to that file. This is only intended for
145199 // `cargo fuzz`'s use!
@@ -154,7 +208,8 @@ macro_rules! fuzz_target {
154208 return ;
155209 }
156210
157- run( bytes)
211+ run( bytes) ;
212+ 0
158213 }
159214
160215 // Split out the actual fuzzer into a separate function which is
@@ -181,10 +236,14 @@ macro_rules! fuzz_target {
181236 } ;
182237
183238 ( |$data: ident: $dty: ty| $body: block) => {
239+ $crate:: fuzz_target!( |$data: $dty| -> ( ) $body) ;
240+ } ;
241+
242+ ( |$data: ident: $dty: ty| -> $rty: ty $body: block) => {
184243 const _: ( ) = {
185244 /// Auto-generated function
186245 #[ 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 {
188247 use $crate:: arbitrary:: { Arbitrary , Unstructured } ;
189248
190249 // Early exit if we don't have enough bytes for the `Arbitrary`
@@ -194,7 +253,7 @@ macro_rules! fuzz_target {
194253 // get to longer inputs that actually lead to interesting executions
195254 // quicker.
196255 if bytes. len( ) < <$dty as Arbitrary >:: size_hint( 0 ) . 0 {
197- return ;
256+ return - 1 ;
198257 }
199258
200259 let mut u = Unstructured :: new( bytes) ;
@@ -214,20 +273,21 @@ macro_rules! fuzz_target {
214273 Err ( err) => writeln!( & mut file, "Arbitrary Error: {}" , err) ,
215274 } )
216275 . expect( "failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file" ) ;
217- return ;
276+ return - 1 ;
218277 }
219278
220279 let data = match data {
221280 Ok ( d) => d,
222- Err ( _) => return ,
281+ Err ( _) => return - 1 ,
223282 } ;
224283
225- run( data)
284+ let result: i32 = :: libfuzzer_sys:: Corpus :: from( run( data) ) . into( ) ;
285+ result
226286 }
227287
228288 // See above for why this is split to a separate function.
229289 #[ inline( never) ]
230- fn run( $data: $dty) {
290+ fn run( $data: $dty) -> $rty {
231291 $body
232292 }
233293 } ;
0 commit comments