Skip to content

Commit 64ad66a

Browse files
authored
Merge pull request #95 from alexcrichton/add-inline-never-frame
Force at least one unique stack frame per fuzzer
2 parents 74ac60c + 63b9226 commit 64ad66a

File tree

1 file changed

+79
-52
lines changed

1 file changed

+79
-52
lines changed

src/lib.rs

Lines changed: 79 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -136,74 +136,101 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize
136136
#[macro_export]
137137
macro_rules! fuzz_target {
138138
(|$bytes:ident| $body:block) => {
139-
/// Auto-generated function
140-
#[no_mangle]
141-
pub extern "C" fn rust_fuzzer_test_input($bytes: &[u8]) {
142-
// When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
143-
// formatting of the input to that file. This is only intended for
144-
// `cargo fuzz`'s use!
139+
const _: () = {
140+
/// Auto-generated function
141+
#[no_mangle]
142+
pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) {
143+
// When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
144+
// formatting of the input to that file. This is only intended for
145+
// `cargo fuzz`'s use!
145146

146-
// `RUST_LIBFUZZER_DEBUG_PATH` is set in initialization.
147-
if let Some(path) = $crate::RUST_LIBFUZZER_DEBUG_PATH.get() {
148-
use std::io::Write;
149-
let mut file = std::fs::File::create(path)
150-
.expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file");
151-
writeln!(&mut file, "{:?}", $bytes)
152-
.expect("failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file");
153-
return;
147+
// `RUST_LIBFUZZER_DEBUG_PATH` is set in initialization.
148+
if let Some(path) = $crate::RUST_LIBFUZZER_DEBUG_PATH.get() {
149+
use std::io::Write;
150+
let mut file = std::fs::File::create(path)
151+
.expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file");
152+
writeln!(&mut file, "{:?}", bytes)
153+
.expect("failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file");
154+
return;
155+
}
156+
157+
run(bytes)
154158
}
155159

156-
$body
157-
}
160+
// Split out the actual fuzzer into a separate function which is
161+
// tagged as never being inlined. This ensures that if the fuzzer
162+
// panics there's at least one stack frame which is named uniquely
163+
// according to this specific fuzzer that this is embedded within.
164+
//
165+
// Systems like oss-fuzz try to deduplicate crashes and without this
166+
// panics in separate fuzzers can accidentally appear the same
167+
// because each fuzzer will have a function called
168+
// `rust_fuzzer_test_input`. By using a normal Rust function here
169+
// it's named something like `the_fuzzer_name::_::run` which should
170+
// ideally help prevent oss-fuzz from deduplicate fuzz bugs across
171+
// distinct targets accidentally.
172+
#[inline(never)]
173+
fn run($bytes: &[u8]) {
174+
$body
175+
}
176+
};
158177
};
159178

160179
(|$data:ident: &[u8]| $body:block) => {
161180
$crate::fuzz_target!(|$data| $body);
162181
};
163182

164183
(|$data:ident: $dty: ty| $body:block) => {
165-
/// Auto-generated function
166-
#[no_mangle]
167-
pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) {
168-
use $crate::arbitrary::{Arbitrary, Unstructured};
184+
const _: () = {
185+
/// Auto-generated function
186+
#[no_mangle]
187+
pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) {
188+
use $crate::arbitrary::{Arbitrary, Unstructured};
169189

170-
// Early exit if we don't have enough bytes for the `Arbitrary`
171-
// implementation. This helps the fuzzer avoid exploring all the
172-
// different not-enough-input-bytes paths inside the `Arbitrary`
173-
// implementation. Additionally, it exits faster, letting the fuzzer
174-
// get to longer inputs that actually lead to interesting executions
175-
// quicker.
176-
if bytes.len() < <$dty as Arbitrary>::size_hint(0).0 {
177-
return;
178-
}
190+
// Early exit if we don't have enough bytes for the `Arbitrary`
191+
// implementation. This helps the fuzzer avoid exploring all the
192+
// different not-enough-input-bytes paths inside the `Arbitrary`
193+
// implementation. Additionally, it exits faster, letting the fuzzer
194+
// get to longer inputs that actually lead to interesting executions
195+
// quicker.
196+
if bytes.len() < <$dty as Arbitrary>::size_hint(0).0 {
197+
return;
198+
}
179199

180-
let mut u = Unstructured::new(bytes);
181-
let data = <$dty as Arbitrary>::arbitrary_take_rest(u);
200+
let mut u = Unstructured::new(bytes);
201+
let data = <$dty as Arbitrary>::arbitrary_take_rest(u);
182202

183-
// When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
184-
// formatting of the input to that file. This is only intended for
185-
// `cargo fuzz`'s use!
203+
// When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
204+
// formatting of the input to that file. This is only intended for
205+
// `cargo fuzz`'s use!
186206

187-
// `RUST_LIBFUZZER_DEBUG_PATH` is set in initialization.
188-
if let Some(path) = $crate::RUST_LIBFUZZER_DEBUG_PATH.get() {
189-
use std::io::Write;
190-
let mut file = std::fs::File::create(path)
191-
.expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file");
192-
(match data {
193-
Ok(data) => writeln!(&mut file, "{:#?}", data),
194-
Err(err) => writeln!(&mut file, "Arbitrary Error: {}", err),
195-
})
196-
.expect("failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file");
197-
return;
198-
}
207+
// `RUST_LIBFUZZER_DEBUG_PATH` is set in initialization.
208+
if let Some(path) = $crate::RUST_LIBFUZZER_DEBUG_PATH.get() {
209+
use std::io::Write;
210+
let mut file = std::fs::File::create(path)
211+
.expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file");
212+
(match data {
213+
Ok(data) => writeln!(&mut file, "{:#?}", data),
214+
Err(err) => writeln!(&mut file, "Arbitrary Error: {}", err),
215+
})
216+
.expect("failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file");
217+
return;
218+
}
199219

200-
let $data = match data {
201-
Ok(d) => d,
202-
Err(_) => return,
203-
};
220+
let data = match data {
221+
Ok(d) => d,
222+
Err(_) => return,
223+
};
204224

205-
$body
206-
}
225+
run(data)
226+
}
227+
228+
// See above for why this is split to a separate function.
229+
#[inline(never)]
230+
fn run($data: $dty) {
231+
$body
232+
}
233+
};
207234
};
208235
}
209236

0 commit comments

Comments
 (0)