Skip to content

Commit 393082d

Browse files
authored
Merge pull request #97 from dacut/corpus
Enable keep/reject inputs from the corpus
2 parents 64ad66a + 1a0499e commit 393082d

File tree

1 file changed

+89
-16
lines changed

1 file changed

+89
-16
lines changed

src/lib.rs

Lines changed: 89 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,41 @@
1414
pub use arbitrary;
1515
use 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+
1748
extern "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" {
2758
pub 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

Comments
 (0)