/rust/registry/src/index.crates.io-6f17d22bba15001f/libfuzzer-sys-0.4.9/src/lib.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! Bindings to [libFuzzer](http://llvm.org/docs/LibFuzzer.html): a runtime for |
2 | | //! coverage-guided fuzzing. |
3 | | //! |
4 | | //! See [the `cargo-fuzz` |
5 | | //! guide](https://rust-fuzz.github.io/book/cargo-fuzz.html) for a usage |
6 | | //! tutorial. |
7 | | //! |
8 | | //! The main export of this crate is [the `fuzz_target!` |
9 | | //! macro](./macro.fuzz_target.html), which allows you to define targets for |
10 | | //! libFuzzer to exercise. |
11 | | |
12 | | #![deny(missing_docs, missing_debug_implementations)] |
13 | | |
14 | | pub use arbitrary; |
15 | | use std::sync::OnceLock; |
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 | 0 | fn from(_: ()) -> Self { |
31 | 0 | Self::Keep |
32 | 0 | } Unexecuted instantiation: <libfuzzer_sys::Corpus as core::convert::From<()>>::from Unexecuted instantiation: <libfuzzer_sys::Corpus as core::convert::From<()>>::from |
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 | 0 | pub fn to_libfuzzer_code(self) -> i32 { |
41 | 0 | match self { |
42 | 0 | Corpus::Keep => 0, |
43 | 0 | Corpus::Reject => -1, |
44 | | } |
45 | 0 | } Unexecuted instantiation: <libfuzzer_sys::Corpus>::to_libfuzzer_code Unexecuted instantiation: <libfuzzer_sys::Corpus>::to_libfuzzer_code |
46 | | } |
47 | | |
48 | | extern "C" { |
49 | | // We do not actually cross the FFI bound here. |
50 | | #[allow(improper_ctypes)] |
51 | | fn rust_fuzzer_test_input(input: &[u8]) -> i32; |
52 | | |
53 | | fn LLVMFuzzerMutate(data: *mut u8, size: usize, max_size: usize) -> usize; |
54 | | } |
55 | | |
56 | | /// Do not use; only for LibFuzzer's consumption. |
57 | | #[doc(hidden)] |
58 | | #[export_name = "LLVMFuzzerTestOneInput"] |
59 | 23.2k | pub unsafe fn test_input_wrap(data: *const u8, size: usize) -> i32 { |
60 | 35.3k | let test_input = ::std::panic::catch_unwind(|| { |
61 | 35.3k | let data_slice = ::std::slice::from_raw_parts(data, size); |
62 | 35.3k | rust_fuzzer_test_input(data_slice) |
63 | 35.3k | }); libfuzzer_sys::test_input_wrap::{closure#0} Line | Count | Source | 60 | 23.2k | let test_input = ::std::panic::catch_unwind(|| { | 61 | 23.2k | let data_slice = ::std::slice::from_raw_parts(data, size); | 62 | 23.2k | rust_fuzzer_test_input(data_slice) | 63 | 23.2k | }); |
libfuzzer_sys::test_input_wrap::{closure#0} Line | Count | Source | 60 | 12.1k | let test_input = ::std::panic::catch_unwind(|| { | 61 | 12.1k | let data_slice = ::std::slice::from_raw_parts(data, size); | 62 | 12.1k | rust_fuzzer_test_input(data_slice) | 63 | 12.1k | }); |
|
64 | 23.2k | |
65 | 23.2k | match test_input { |
66 | 23.2k | Ok(i) => i, |
67 | | Err(_) => { |
68 | | // hopefully the custom panic hook will be called before and abort the |
69 | | // process before the stack frames are unwinded. |
70 | 0 | ::std::process::abort(); |
71 | | } |
72 | | } |
73 | 23.2k | } |
74 | | |
75 | | #[doc(hidden)] |
76 | 35.3k | pub fn rust_libfuzzer_debug_path() -> &'static Option<String> { |
77 | | static RUST_LIBFUZZER_DEBUG_PATH: OnceLock<Option<String>> = OnceLock::new(); |
78 | 35.3k | RUST_LIBFUZZER_DEBUG_PATH.get_or_init(|| std::env::var("RUST_LIBFUZZER_DEBUG_PATH").ok()) libfuzzer_sys::rust_libfuzzer_debug_path::{closure#0} Line | Count | Source | 78 | 2 | RUST_LIBFUZZER_DEBUG_PATH.get_or_init(|| std::env::var("RUST_LIBFUZZER_DEBUG_PATH").ok()) |
libfuzzer_sys::rust_libfuzzer_debug_path::{closure#0} Line | Count | Source | 78 | 1 | RUST_LIBFUZZER_DEBUG_PATH.get_or_init(|| std::env::var("RUST_LIBFUZZER_DEBUG_PATH").ok()) |
|
79 | 35.3k | } libfuzzer_sys::rust_libfuzzer_debug_path Line | Count | Source | 76 | 23.2k | pub fn rust_libfuzzer_debug_path() -> &'static Option<String> { | 77 | | static RUST_LIBFUZZER_DEBUG_PATH: OnceLock<Option<String>> = OnceLock::new(); | 78 | 23.2k | RUST_LIBFUZZER_DEBUG_PATH.get_or_init(|| std::env::var("RUST_LIBFUZZER_DEBUG_PATH").ok()) | 79 | 23.2k | } |
libfuzzer_sys::rust_libfuzzer_debug_path Line | Count | Source | 76 | 12.1k | pub fn rust_libfuzzer_debug_path() -> &'static Option<String> { | 77 | | static RUST_LIBFUZZER_DEBUG_PATH: OnceLock<Option<String>> = OnceLock::new(); | 78 | 12.1k | RUST_LIBFUZZER_DEBUG_PATH.get_or_init(|| std::env::var("RUST_LIBFUZZER_DEBUG_PATH").ok()) | 79 | 12.1k | } |
|
80 | | |
81 | | #[doc(hidden)] |
82 | 6 | pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize { |
83 | 6 | // Registers a panic hook that aborts the process before unwinding. |
84 | 6 | // It is useful to abort before unwinding so that the fuzzer will then be |
85 | 6 | // able to analyse the process stack frames to tell different bugs appart. |
86 | 6 | // |
87 | 6 | // HACK / FIXME: it would be better to use `-C panic=abort` but it's currently |
88 | 6 | // impossible to build code using compiler plugins with this flag. |
89 | 6 | // We will be able to remove this code when |
90 | 6 | // https://github.com/rust-lang/cargo/issues/5423 is fixed. |
91 | 6 | let default_hook = std::panic::take_hook(); |
92 | 6 | std::panic::set_hook(Box::new(move |panic_info| { |
93 | 0 | default_hook(panic_info); |
94 | 0 | std::process::abort(); Unexecuted instantiation: libfuzzer_sys::initialize::{closure#0} Unexecuted instantiation: libfuzzer_sys::initialize::{closure#0} |
95 | 6 | })); |
96 | 6 | 0 |
97 | 6 | } libfuzzer_sys::initialize Line | Count | Source | 82 | 4 | pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize { | 83 | 4 | // Registers a panic hook that aborts the process before unwinding. | 84 | 4 | // It is useful to abort before unwinding so that the fuzzer will then be | 85 | 4 | // able to analyse the process stack frames to tell different bugs appart. | 86 | 4 | // | 87 | 4 | // HACK / FIXME: it would be better to use `-C panic=abort` but it's currently | 88 | 4 | // impossible to build code using compiler plugins with this flag. | 89 | 4 | // We will be able to remove this code when | 90 | 4 | // https://github.com/rust-lang/cargo/issues/5423 is fixed. | 91 | 4 | let default_hook = std::panic::take_hook(); | 92 | 4 | std::panic::set_hook(Box::new(move |panic_info| { | 93 | | default_hook(panic_info); | 94 | | std::process::abort(); | 95 | 4 | })); | 96 | 4 | 0 | 97 | 4 | } |
libfuzzer_sys::initialize Line | Count | Source | 82 | 2 | pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize { | 83 | 2 | // Registers a panic hook that aborts the process before unwinding. | 84 | 2 | // It is useful to abort before unwinding so that the fuzzer will then be | 85 | 2 | // able to analyse the process stack frames to tell different bugs appart. | 86 | 2 | // | 87 | 2 | // HACK / FIXME: it would be better to use `-C panic=abort` but it's currently | 88 | 2 | // impossible to build code using compiler plugins with this flag. | 89 | 2 | // We will be able to remove this code when | 90 | 2 | // https://github.com/rust-lang/cargo/issues/5423 is fixed. | 91 | 2 | let default_hook = std::panic::take_hook(); | 92 | 2 | std::panic::set_hook(Box::new(move |panic_info| { | 93 | | default_hook(panic_info); | 94 | | std::process::abort(); | 95 | 2 | })); | 96 | 2 | 0 | 97 | 2 | } |
|
98 | | |
99 | | /// Define a fuzz target. |
100 | | /// |
101 | | /// ## Example |
102 | | /// |
103 | | /// This example takes a `&[u8]` slice and attempts to parse it. The parsing |
104 | | /// might fail and return an `Err`, but it shouldn't ever panic or segfault. |
105 | | /// |
106 | | /// ```no_run |
107 | | /// #![no_main] |
108 | | /// |
109 | | /// use libfuzzer_sys::fuzz_target; |
110 | | /// |
111 | | /// // Note: `|input|` is short for `|input: &[u8]|`. |
112 | | /// fuzz_target!(|input| { |
113 | | /// let _result: Result<_, _> = my_crate::parse(input); |
114 | | /// }); |
115 | | /// # mod my_crate { pub fn parse(_: &[u8]) -> Result<(), ()> { unimplemented!() } } |
116 | | /// ``` |
117 | | /// |
118 | | /// ## Rejecting Inputs |
119 | | /// |
120 | | /// It may be desirable to reject some inputs, i.e. to not add them to the |
121 | | /// corpus. |
122 | | /// |
123 | | /// For example, when fuzzing an API consisting of parsing and other logic, |
124 | | /// one may want to allow only those inputs into the corpus that parse |
125 | | /// successfully. To indicate whether an input should be kept in or rejected |
126 | | /// from the corpus, return either [Corpus::Keep] or [Corpus::Reject] from your |
127 | | /// fuzz target. The default behavior (e.g. if `()` is returned) is to keep the |
128 | | /// input in the corpus. |
129 | | /// |
130 | | /// For example: |
131 | | /// |
132 | | /// ```no_run |
133 | | /// #![no_main] |
134 | | /// |
135 | | /// use libfuzzer_sys::{Corpus, fuzz_target}; |
136 | | /// |
137 | | /// fuzz_target!(|input: String| -> Corpus { |
138 | | /// let parts: Vec<&str> = input.splitn(2, '=').collect(); |
139 | | /// if parts.len() != 2 { |
140 | | /// return Corpus::Reject; |
141 | | /// } |
142 | | /// |
143 | | /// let key = parts[0]; |
144 | | /// let value = parts[1]; |
145 | | /// let _result: Result<_, _> = my_crate::parse(key, value); |
146 | | /// Corpus::Keep |
147 | | /// }); |
148 | | /// # mod my_crate { pub fn parse(_key: &str, _value: &str) -> Result<(), ()> { unimplemented!() } } |
149 | | /// ``` |
150 | | /// |
151 | | /// ## Arbitrary Input Types |
152 | | /// |
153 | | /// The input is a `&[u8]` slice by default, but you can take arbitrary input |
154 | | /// types, as long as the type implements [the `arbitrary` crate's `Arbitrary` |
155 | | /// trait](https://docs.rs/arbitrary/*/arbitrary/trait.Arbitrary.html) (which is |
156 | | /// also re-exported as `libfuzzer_sys::arbitrary::Arbitrary` for convenience). |
157 | | /// |
158 | | /// For example, if you wanted to take an arbitrary RGB color, you could do the |
159 | | /// following: |
160 | | /// |
161 | | /// ```no_run |
162 | | /// #![no_main] |
163 | | /// # mod foo { |
164 | | /// |
165 | | /// use libfuzzer_sys::{arbitrary::{Arbitrary, Error, Unstructured}, fuzz_target}; |
166 | | /// |
167 | | /// #[derive(Debug)] |
168 | | /// pub struct Rgb { |
169 | | /// r: u8, |
170 | | /// g: u8, |
171 | | /// b: u8, |
172 | | /// } |
173 | | /// |
174 | | /// impl<'a> Arbitrary<'a> for Rgb { |
175 | | /// fn arbitrary(raw: &mut Unstructured<'a>) -> Result<Self, Error> { |
176 | | /// let mut buf = [0; 3]; |
177 | | /// raw.fill_buffer(&mut buf)?; |
178 | | /// let r = buf[0]; |
179 | | /// let g = buf[1]; |
180 | | /// let b = buf[2]; |
181 | | /// Ok(Rgb { r, g, b }) |
182 | | /// } |
183 | | /// } |
184 | | /// |
185 | | /// // Write a fuzz target that works with RGB colors instead of raw bytes. |
186 | | /// fuzz_target!(|color: Rgb| { |
187 | | /// my_crate::convert_color(color); |
188 | | /// }); |
189 | | /// # mod my_crate { |
190 | | /// # use super::Rgb; |
191 | | /// # pub fn convert_color(_: Rgb) {} |
192 | | /// # } |
193 | | /// # } |
194 | | /// ``` |
195 | | /// |
196 | | /// You can also enable the `arbitrary` crate's custom derive via this crate's |
197 | | /// `"arbitrary-derive"` cargo feature. |
198 | | /// |
199 | | /// ## Init Code |
200 | | /// |
201 | | /// Init code to the fuzz target by using the `init` keyword. This is called once before the fuzzer starts. |
202 | | /// Supports short |input| or |input: <type>| syntax. |
203 | | /// |
204 | | /// ```no_run |
205 | | /// #![no_main] |
206 | | /// |
207 | | /// use libfuzzer_sys::fuzz_target; |
208 | | /// use std::collections::HashSet; |
209 | | /// use std::sync::OnceLock; |
210 | | /// |
211 | | /// static DICTIONARY: OnceLock<HashSet<String>> = OnceLock::new(); |
212 | | /// |
213 | | /// fuzz_target!( |
214 | | /// init: { |
215 | | /// let read_dictionary = |_| unimplemented!(); |
216 | | /// let dictionary = read_dictionary("/usr/share/dict/words"); |
217 | | /// DICTIONARY.set(dictionary).unwrap(); |
218 | | /// }, |
219 | | /// |input| { |
220 | | /// // Use the initialized `DICTIONARY` here... |
221 | | /// } |
222 | | /// ); |
223 | | /// ``` |
224 | | /// |
225 | | #[macro_export] |
226 | | macro_rules! fuzz_target { |
227 | | (init: $init:expr, |$bytes:ident| $body:expr) => { |
228 | | const _: () = { |
229 | | /// Auto-generated functions |
230 | | /// LLVMFuzzerInitialize is called once before the fuzzer starts. |
231 | | #[no_mangle] |
232 | 2 | pub extern "C" fn LLVMFuzzerInitialize(_argc: *const isize, _argv: *const *const *const u8) -> isize { |
233 | 2 | $crate::initialize(_argc, _argv); |
234 | 2 | |
235 | 2 | // Supplied init code |
236 | 2 | $init; |
237 | 2 | 0 |
238 | 2 | } |
239 | | |
240 | | #[no_mangle] |
241 | 12.7k | pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) -> i32 { |
242 | | // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug |
243 | | // formatting of the input to that file. This is only intended for |
244 | | // `cargo fuzz`'s use! |
245 | | |
246 | | // `RUST_LIBFUZZER_DEBUG_PATH` is set in initialization. |
247 | 12.7k | if let Some(path) = $crate::rust_libfuzzer_debug_path() { |
248 | | use std::io::Write; |
249 | 0 | let mut file = std::fs::File::create(path) |
250 | 0 | .expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file"); |
251 | 0 | writeln!(&mut file, "{:?}", bytes) |
252 | 0 | .expect("failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file"); |
253 | 0 | return 0; |
254 | 12.7k | } |
255 | 12.7k | |
256 | 12.7k | __libfuzzer_sys_run(bytes); |
257 | 12.7k | 0 |
258 | 12.7k | } |
259 | 12.7k | |
260 | 12.7k | // Split out the actual fuzzer into a separate function which is |
261 | 12.7k | // tagged as never being inlined. This ensures that if the fuzzer |
262 | 12.7k | // panics there's at least one stack frame which is named uniquely |
263 | 12.7k | // according to this specific fuzzer that this is embedded within. |
264 | 12.7k | // |
265 | 12.7k | // Systems like oss-fuzz try to deduplicate crashes and without this |
266 | 12.7k | // panics in separate fuzzers can accidentally appear the same |
267 | 12.7k | // because each fuzzer will have a function called |
268 | 12.7k | // `rust_fuzzer_test_input`. By using a normal Rust function here |
269 | 12.7k | // it's named something like `the_fuzzer_name::_::__libfuzzer_sys_run` which should |
270 | 12.7k | // ideally help prevent oss-fuzz from deduplicate fuzz bugs across |
271 | 12.7k | // distinct targets accidentally. |
272 | 12.7k | #[inline(never)] |
273 | 35.3k | fn __libfuzzer_sys_run($bytes: &[u8]) { |
274 | | $body |
275 | 35.3k | } fuzz_url::_::__libfuzzer_sys_run Line | Count | Source | 273 | 12.7k | fn __libfuzzer_sys_run($bytes: &[u8]) { | 274 | | $body | 275 | | } |
parse::_::__libfuzzer_sys_run Line | Count | Source | 273 | 10.5k | fn __libfuzzer_sys_run($bytes: &[u8]) { | 274 | | $body | 275 | | } |
fuzz_document_parse::_::__libfuzzer_sys_run Line | Count | Source | 273 | 12.1k | fn __libfuzzer_sys_run($bytes: &[u8]) { | 274 | | $body | 275 | | } |
|
276 | 35.3k | }; |
277 | 35.3k | }; |
278 | 35.3k | |
279 | 35.3k | (|$bytes:ident| $body:expr) => { |
280 | 35.3k | $crate::fuzz_target!(|$bytes: &[u8]| $body); |
281 | 35.3k | }; |
282 | 35.3k | |
283 | 35.3k | (|$data:ident: &[u8]| $body:expr) => { |
284 | 35.3k | $crate::fuzz_target!(init: (), |$data| $body); |
285 | 35.3k | }; |
286 | 35.3k | |
287 | 35.3k | (|$data:ident: $dty:ty| $body:expr) => { |
288 | 35.3k | $crate::fuzz_target!(init: (), |$data: $dty| -> () { $body }); |
289 | 35.3k | }; |
290 | 35.3k | |
291 | 35.3k | (|$data:ident: $dty:ty| -> $rty:ty $body:block) => { |
292 | 35.3k | $crate::fuzz_target!(init: (), |$data: $dty| -> $rty { $body }); |
293 | 35.3k | }; |
294 | 35.3k | |
295 | 35.3k | (init: $init:expr, |$data:ident: &[u8]| $body:expr) => { |
296 | 35.3k | $crate::fuzz_target!(init: $init, |$data| $body); |
297 | 35.3k | }; |
298 | 35.3k | |
299 | 35.3k | (init: $init:expr, |$bytes:ident| $body:expr) => { |
300 | 35.3k | $crate::fuzz_target!(init: $init, |$bytes: &[u8]| $body); |
301 | 35.3k | }; |
302 | 35.3k | |
303 | 35.3k | (init: $init:expr, |$data:ident: $dty:ty| $body:expr) => { |
304 | 35.3k | $crate::fuzz_target!(init: $init, |$data: $dty| -> () { $body }); |
305 | 35.3k | }; |
306 | 35.3k | |
307 | 35.3k | (init: $init:expr, |$data:ident: $dty:ty| -> $rty:ty $body:block) => { |
308 | 35.3k | const _: () = { |
309 | 35.3k | /// Auto-generated functions |
310 | 35.3k | /// LLVMFuzzerInitialize is called once before the fuzzer starts. |
311 | 35.3k | #[no_mangle] |
312 | 35.3k | pub extern "C" fn LLVMFuzzerInitialize(_argc: *const isize, _argv: *const *const *const u8) -> isize { |
313 | 35.3k | $crate::initialize(_argc, _argv); |
314 | 35.3k | |
315 | 35.3k | // Supplied init code |
316 | 35.3k | $init; |
317 | 35.3k | 0 |
318 | 35.3k | } |
319 | 35.3k | |
320 | 35.3k | #[no_mangle] |
321 | 35.3k | pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) -> i32 { |
322 | 35.3k | use $crate::arbitrary::{Arbitrary, Unstructured}; |
323 | 35.3k | |
324 | 35.3k | // Early exit if we don't have enough bytes for the `Arbitrary` |
325 | 35.3k | // implementation. This helps the fuzzer avoid exploring all the |
326 | 35.3k | // different not-enough-input-bytes paths inside the `Arbitrary` |
327 | 35.3k | // implementation. Additionally, it exits faster, letting the fuzzer |
328 | 35.3k | // get to longer inputs that actually lead to interesting executions |
329 | 35.3k | // quicker. |
330 | 35.3k | if bytes.len() < <$dty as Arbitrary>::size_hint(0).0 { |
331 | 35.3k | return -1; |
332 | 35.3k | } |
333 | 35.3k | |
334 | 35.3k | let mut u = Unstructured::new(bytes); |
335 | 35.3k | let data = <$dty as Arbitrary>::arbitrary_take_rest(u); |
336 | 35.3k | |
337 | 35.3k | // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug |
338 | 35.3k | // formatting of the input to that file. This is only intended for |
339 | 35.3k | // `cargo fuzz`'s use! |
340 | 35.3k | |
341 | 35.3k | // `RUST_LIBFUZZER_DEBUG_PATH` is set in initialization. |
342 | 35.3k | if let Some(path) = $crate::rust_libfuzzer_debug_path() { |
343 | 35.3k | use std::io::Write; |
344 | 35.3k | let mut file = std::fs::File::create(path) |
345 | 35.3k | .expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file"); |
346 | 35.3k | (match data { |
347 | 35.3k | Ok(data) => writeln!(&mut file, "{:#?}", data), |
348 | 35.3k | Err(err) => writeln!(&mut file, "Arbitrary Error: {}", err), |
349 | 35.3k | }) |
350 | 35.3k | .expect("failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file"); |
351 | 35.3k | return -1; |
352 | 35.3k | } |
353 | 35.3k | |
354 | 35.3k | let data = match data { |
355 | 35.3k | Ok(d) => d, |
356 | 35.3k | Err(_) => return -1, |
357 | 35.3k | }; |
358 | 35.3k | |
359 | 35.3k | let result = ::libfuzzer_sys::Corpus::from(__libfuzzer_sys_run(data)); |
360 | 35.3k | result.to_libfuzzer_code() |
361 | 35.3k | } |
362 | 35.3k | // See above for why this is split to a separate function. |
363 | 35.3k | #[inline(never)] |
364 | 35.3k | fn __libfuzzer_sys_run($data: $dty) -> $rty { |
365 | 35.3k | $body |
366 | 35.3k | } |
367 | 35.3k | }; |
368 | 35.3k | }; |
369 | 35.3k | } |
370 | 35.3k | |
371 | 35.3k | /// Define a custom mutator. |
372 | 35.3k | /// |
373 | 35.3k | /// This is optional, and libFuzzer will use its own, default mutation strategy |
374 | 35.3k | /// if this is not provided. |
375 | 35.3k | /// |
376 | 35.3k | /// You might consider using a custom mutator when your fuzz target is very |
377 | 35.3k | /// particular about the shape of its input: |
378 | 35.3k | /// |
379 | 35.3k | /// * You want to fuzz "deeper" than just the parser. |
380 | 35.3k | /// * The input contains checksums that have to match the hash of some subset of |
381 | 35.3k | /// the data or else the whole thing is invalid, and therefore mutating any of |
382 | 35.3k | /// that subset means you need to recompute the checksums. |
383 | 35.3k | /// * Small random changes to the input buffer make it invalid. |
384 | 35.3k | /// |
385 | 35.3k | /// That is, a custom mutator is useful in similar situations where [a `T: |
386 | 35.3k | /// Arbitrary` input type](macro.fuzz_target.html#arbitrary-input-types) is |
387 | 35.3k | /// useful. Note that the two approaches are not mutually exclusive; you can use |
388 | 35.3k | /// whichever is easier for your problem domain or both! |
389 | 35.3k | /// |
390 | 35.3k | /// ## Implementation Contract |
391 | 35.3k | /// |
392 | 35.3k | /// The original, unmodified input is given in `data[..size]`. |
393 | 35.3k | /// |
394 | 35.3k | /// You must modify the data in place and return the new size. |
395 | 35.3k | /// |
396 | 35.3k | /// The new size should not be greater than `max_size`. If this is not the case, |
397 | 35.3k | /// then the `data` will be truncated to fit within `max_size`. Note that |
398 | 35.3k | /// `max_size < size` is possible when shrinking test cases. |
399 | 35.3k | /// |
400 | 35.3k | /// You must produce the same mutation given the same `seed`. Generally, when |
401 | 35.3k | /// choosing what kind of mutation to make or where to mutate, you should start |
402 | 35.3k | /// by creating a random number generator (RNG) that is seeded with the given |
403 | 35.3k | /// `seed` and then consult the RNG whenever making a decision: |
404 | 35.3k | /// |
405 | 35.3k | /// ```no_run |
406 | 35.3k | /// #![no_main] |
407 | 35.3k | /// |
408 | 35.3k | /// use rand::{rngs::StdRng, Rng, SeedableRng}; |
409 | 35.3k | /// |
410 | 35.3k | /// libfuzzer_sys::fuzz_mutator!(|data: &mut [u8], size: usize, max_size: usize, seed: u32| { |
411 | 35.3k | /// let mut rng = StdRng::seed_from_u64(seed as u64); |
412 | 35.3k | /// |
413 | 35.3k | /// # let first_mutation = |_, _, _, _| todo!(); |
414 | 35.3k | /// # let second_mutation = |_, _, _, _| todo!(); |
415 | 35.3k | /// # let third_mutation = |_, _, _, _| todo!(); |
416 | 35.3k | /// # let fourth_mutation = |_, _, _, _| todo!(); |
417 | 35.3k | /// // Choose which of our four supported kinds of mutations we want to make. |
418 | 35.3k | /// match rng.gen_range(0..4) { |
419 | 35.3k | /// 0 => first_mutation(rng, data, size, max_size), |
420 | 35.3k | /// 1 => second_mutation(rng, data, size, max_size), |
421 | 35.3k | /// 2 => third_mutation(rng, data, size, max_size), |
422 | 35.3k | /// 3 => fourth_mutation(rng, data, size, max_size), |
423 | 35.3k | /// _ => unreachable!() |
424 | 35.3k | /// } |
425 | 35.3k | /// }); |
426 | 35.3k | /// ``` |
427 | 35.3k | /// |
428 | 35.3k | /// ## Example: Compression |
429 | 35.3k | /// |
430 | 35.3k | /// Consider a simple fuzz target that takes compressed data as input, |
431 | 35.3k | /// decompresses it, and then asserts that the decompressed data doesn't begin |
432 | 35.3k | /// with "boom". It is difficult for `libFuzzer` (or any other fuzzer) to crash |
433 | 35.3k | /// this fuzz target because nearly all mutations it makes will invalidate the |
434 | 35.3k | /// compression format. Therefore, we use a custom mutator that decompresses the |
435 | 35.3k | /// raw input, mutates the decompressed data, and then recompresses it. This |
436 | 35.3k | /// allows `libFuzzer` to quickly discover crashing inputs. |
437 | 35.3k | /// |
438 | 35.3k | /// ```no_run |
439 | 35.3k | /// #![no_main] |
440 | 35.3k | /// |
441 | 35.3k | /// use flate2::{read::GzDecoder, write::GzEncoder, Compression}; |
442 | 35.3k | /// use libfuzzer_sys::{fuzz_mutator, fuzz_target}; |
443 | 35.3k | /// use std::io::{Read, Write}; |
444 | 35.3k | /// |
445 | 35.3k | /// fuzz_target!(|data: &[u8]| { |
446 | 35.3k | /// // Decompress the input data and crash if it starts with "boom". |
447 | 35.3k | /// if let Some(data) = decompress(data) { |
448 | 35.3k | /// if data.starts_with(b"boom") { |
449 | 35.3k | /// panic!(); |
450 | 35.3k | /// } |
451 | 35.3k | /// } |
452 | 35.3k | /// }); |
453 | 35.3k | /// |
454 | 35.3k | /// fuzz_mutator!( |
455 | 35.3k | /// |data: &mut [u8], size: usize, max_size: usize, _seed: u32| { |
456 | 35.3k | /// // Decompress the input data. If that fails, use a dummy value. |
457 | 35.3k | /// let mut decompressed = decompress(&data[..size]).unwrap_or_else(|| b"hi".to_vec()); |
458 | 35.3k | /// |
459 | 35.3k | /// // Mutate the decompressed data with `libFuzzer`'s default mutator. Make |
460 | 35.3k | /// // the `decompressed` vec's extra capacity available for insertion |
461 | 35.3k | /// // mutations via `resize`. |
462 | 35.3k | /// let len = decompressed.len(); |
463 | 35.3k | /// let cap = decompressed.capacity(); |
464 | 35.3k | /// decompressed.resize(cap, 0); |
465 | 35.3k | /// let new_decompressed_size = libfuzzer_sys::fuzzer_mutate(&mut decompressed, len, cap); |
466 | 35.3k | /// |
467 | 35.3k | /// // Recompress the mutated data. |
468 | 35.3k | /// let compressed = compress(&decompressed[..new_decompressed_size]); |
469 | 35.3k | /// |
470 | 35.3k | /// // Copy the recompressed mutated data into `data` and return the new size. |
471 | 35.3k | /// let new_size = std::cmp::min(max_size, compressed.len()); |
472 | 35.3k | /// data[..new_size].copy_from_slice(&compressed[..new_size]); |
473 | 35.3k | /// new_size |
474 | 35.3k | /// } |
475 | 35.3k | /// ); |
476 | 35.3k | /// |
477 | 35.3k | /// fn decompress(compressed_data: &[u8]) -> Option<Vec<u8>> { |
478 | 35.3k | /// let mut decoder = GzDecoder::new(compressed_data); |
479 | 35.3k | /// let mut decompressed = Vec::new(); |
480 | 35.3k | /// if decoder.read_to_end(&mut decompressed).is_ok() { |
481 | 35.3k | /// Some(decompressed) |
482 | 35.3k | /// } else { |
483 | 35.3k | /// None |
484 | 35.3k | /// } |
485 | 35.3k | /// } |
486 | 35.3k | /// |
487 | 35.3k | /// fn compress(data: &[u8]) -> Vec<u8> { |
488 | 35.3k | /// let mut encoder = GzEncoder::new(Vec::new(), Compression::default()); |
489 | 35.3k | /// encoder |
490 | 35.3k | /// .write_all(data) |
491 | 35.3k | /// .expect("writing into a vec is infallible"); |
492 | 35.3k | /// encoder.finish().expect("writing into a vec is infallible") |
493 | 35.3k | /// } |
494 | 35.3k | /// ``` |
495 | 35.3k | /// |
496 | 35.3k | /// This example is inspired by [a similar example from the official `libFuzzer` |
497 | 35.3k | /// docs](https://github.com/google/fuzzing/blob/master/docs/structure-aware-fuzzing.md#example-compression). |
498 | 35.3k | /// |
499 | 35.3k | /// ## More Example Ideas |
500 | 35.3k | /// |
501 | 35.3k | /// * A PNG custom mutator that decodes a PNG, mutates the image, and then |
502 | 35.3k | /// re-encodes the mutated image as a new PNG. |
503 | 35.3k | /// |
504 | 35.3k | /// * A [`serde`](https://serde.rs/) custom mutator that deserializes your |
505 | 35.3k | /// structure, mutates it, and then reserializes it. |
506 | 35.3k | /// |
507 | 35.3k | /// * A Wasm binary custom mutator that inserts, replaces, and removes a |
508 | 35.3k | /// bytecode instruction in a function's body. |
509 | 35.3k | /// |
510 | 35.3k | /// * An HTTP request custom mutator that inserts, replaces, and removes a |
511 | 35.3k | /// header from an HTTP request. |
512 | 35.3k | #[macro_export] |
513 | 35.3k | macro_rules! fuzz_mutator { |
514 | 35.3k | ( |
515 | 35.3k | | |
516 | 35.3k | $data:ident : &mut [u8] , |
517 | 35.3k | $size:ident : usize , |
518 | 35.3k | $max_size:ident : usize , |
519 | 35.3k | $seed:ident : u32 $(,)* |
520 | 35.3k | | |
521 | 35.3k | $body:block |
522 | 35.3k | ) => { |
523 | 35.3k | /// Auto-generated function. Do not use; only for LibFuzzer's |
524 | 35.3k | /// consumption. |
525 | 35.3k | #[export_name = "LLVMFuzzerCustomMutator"] |
526 | 35.3k | #[doc(hidden)] |
527 | 35.3k | pub unsafe fn rust_fuzzer_custom_mutator( |
528 | 35.3k | $data: *mut u8, |
529 | 35.3k | $size: usize, |
530 | 35.3k | $max_size: usize, |
531 | 35.3k | $seed: std::os::raw::c_uint, |
532 | 35.3k | ) -> usize { |
533 | 35.3k | // Depending on if we are growing or shrinking the test case, `size` |
534 | 35.3k | // might be larger or smaller than `max_size`. The `data`'s capacity |
535 | 35.3k | // is the maximum of the two. |
536 | 35.3k | let len = std::cmp::max($max_size, $size); |
537 | 35.3k | let $data: &mut [u8] = std::slice::from_raw_parts_mut($data, len); |
538 | 35.3k | |
539 | 35.3k | // `unsigned int` is generally a `u32`, but not on all targets. Do |
540 | 35.3k | // an infallible (and potentially lossy, but that's okay because it |
541 | 35.3k | // preserves determinism) conversion. |
542 | 35.3k | let $seed = $seed as u32; |
543 | 35.3k | |
544 | 35.3k | // Define and invoke a new, safe function so that the body doesn't |
545 | 35.3k | // inherit `unsafe`. |
546 | 35.3k | fn custom_mutator( |
547 | 35.3k | $data: &mut [u8], |
548 | 35.3k | $size: usize, |
549 | 35.3k | $max_size: usize, |
550 | 35.3k | $seed: u32, |
551 | 35.3k | ) -> usize { |
552 | 35.3k | $body |
553 | 35.3k | } |
554 | 35.3k | let new_size = custom_mutator($data, $size, $max_size, $seed); |
555 | 35.3k | |
556 | 35.3k | // Truncate the new size if it is larger than the max. |
557 | 35.3k | std::cmp::min(new_size, $max_size) |
558 | 35.3k | } |
559 | 35.3k | }; |
560 | 35.3k | } |
561 | 35.3k | |
562 | 35.3k | /// The default `libFuzzer` mutator. |
563 | 35.3k | /// |
564 | 35.3k | /// You generally don't have to use this at all unless you're defining a |
565 | 35.3k | /// custom mutator with [the `fuzz_mutator!` macro][crate::fuzz_mutator]. |
566 | 35.3k | /// |
567 | 35.3k | /// Mutates `data[..size]` in place such that the mutated data is no larger than |
568 | 35.3k | /// `max_size` and returns the new size of the mutated data. |
569 | 35.3k | /// |
570 | 35.3k | /// To only allow shrinking mutations, make `max_size < size`. |
571 | 35.3k | /// |
572 | 35.3k | /// To additionally allow mutations that grow the size of the data, make |
573 | 35.3k | /// `max_size > size`. |
574 | 35.3k | /// |
575 | 35.3k | /// Both `size` and `max_size` must be less than or equal to `data.len()`. |
576 | 35.3k | /// |
577 | 35.3k | /// # Example |
578 | 35.3k | /// |
579 | 35.3k | /// ```no_run |
580 | 35.3k | /// // Create some data in a buffer. |
581 | 35.3k | /// let mut data = vec![0; 128]; |
582 | 35.3k | /// data[..b"hello".len()].copy_from_slice(b"hello"); |
583 | 35.3k | /// |
584 | 35.3k | /// // Ask `libFuzzer` to mutate the data. By setting `max_size` to our buffer's |
585 | 35.3k | /// // full length, we are allowing `libFuzzer` to perform mutations that grow |
586 | 35.3k | /// // the size of the data, such as insertions. |
587 | 35.3k | /// let size = b"hello".len(); |
588 | 35.3k | /// let max_size = data.len(); |
589 | 35.3k | /// let new_size = libfuzzer_sys::fuzzer_mutate(&mut data, size, max_size); |
590 | 35.3k | /// |
591 | 35.3k | /// // Get the mutated data out of the buffer. |
592 | 35.3k | /// let mutated_data = &data[..new_size]; |
593 | 35.3k | /// ``` |
594 | 35.3k | pub fn fuzzer_mutate(data: &mut [u8], size: usize, max_size: usize) -> usize { |
595 | 0 | assert!(size <= data.len()); |
596 | 0 | assert!(max_size <= data.len()); |
597 | 0 | let new_size = unsafe { LLVMFuzzerMutate(data.as_mut_ptr(), size, max_size) }; |
598 | 0 | assert!(new_size <= data.len()); |
599 | 0 | new_size |
600 | 0 | } Unexecuted instantiation: libfuzzer_sys::fuzzer_mutate Unexecuted instantiation: libfuzzer_sys::fuzzer_mutate |
601 | | |
602 | | /// Define a custom cross-over function to combine test cases. |
603 | | /// |
604 | | /// This is optional, and libFuzzer will use its own, default cross-over strategy |
605 | | /// if this is not provided. (As of the time of writing, this default strategy |
606 | | /// takes alternating byte sequences from the two test cases, to construct the |
607 | | /// new one) (see `FuzzerCrossOver.cpp`) |
608 | | /// |
609 | | /// This could potentially be useful if your input is, for instance, a |
610 | | /// sequence of fixed sized, multi-byte values and the crossover could then |
611 | | /// merge discrete values rather than joining parts of a value. |
612 | | /// |
613 | | /// ## Implementation Contract |
614 | | /// |
615 | | /// The original, read-only inputs are given in the full slices of `data1`, and |
616 | | /// `data2` (as opposed to the, potentially, partial slice of `data` in |
617 | | /// [the `fuzz_mutator!` macro][crate::fuzz_mutator]). |
618 | | /// |
619 | | /// You must place the new input merged from the two existing inputs' data |
620 | | /// into `out` and return the size of the relevant data written to that slice. |
621 | | /// |
622 | | /// The deterministic requirements from [the `fuzz_mutator!` macro][crate::fuzz_mutator] |
623 | | /// apply as well to the `seed` parameter |
624 | | /// |
625 | | /// ## Example: Floating-Point Sum NaN |
626 | | /// |
627 | | /// ```no_run |
628 | | /// #![no_main] |
629 | | /// |
630 | | /// use libfuzzer_sys::{fuzz_crossover, fuzz_mutator, fuzz_target, fuzzer_mutate}; |
631 | | /// use rand::{rngs::StdRng, Rng, SeedableRng}; |
632 | | /// use std::mem::size_of; |
633 | | /// |
634 | | /// fuzz_target!(|data: &[u8]| { |
635 | | /// let (_, floats, _) = unsafe { data.align_to::<f64>() }; |
636 | | /// |
637 | | /// let res = floats |
638 | | /// .iter() |
639 | | /// .fold(0.0, |a, b| if b.is_nan() { a } else { a + b }); |
640 | | /// |
641 | | /// assert!( |
642 | | /// !res.is_nan(), |
643 | | /// "The sum of the following floats resulted in a NaN: {floats:?}" |
644 | | /// ); |
645 | | /// }); |
646 | | /// |
647 | | /// // Inject some ...potentially problematic values to make the example close |
648 | | /// // more quickly. |
649 | | /// fuzz_mutator!(|data: &mut [u8], size: usize, max_size: usize, seed: u32| { |
650 | | /// let mut gen = StdRng::seed_from_u64(seed.into()); |
651 | | /// |
652 | | /// let (_, floats, _) = unsafe { data[..size].align_to_mut::<f64>() }; |
653 | | /// |
654 | | /// let x = gen.gen_range(0..=1000); |
655 | | /// if x == 0 && !floats.is_empty() { |
656 | | /// floats[0] = f64::INFINITY; |
657 | | /// } else if x == 1000 && floats.len() > 1 { |
658 | | /// floats[1] = f64::NEG_INFINITY; |
659 | | /// } else { |
660 | | /// return fuzzer_mutate(data, size, max_size); |
661 | | /// } |
662 | | /// |
663 | | /// size |
664 | | /// }); |
665 | | /// |
666 | | /// fuzz_crossover!(|data1: &[u8], data2: &[u8], out: &mut [u8], _seed: u32| { |
667 | | /// // Decode each source to see how many floats we can pull with proper |
668 | | /// // alignment, and destination as to how many will fit with proper alignment |
669 | | /// // |
670 | | /// // Keep track of the unaligned prefix to `out`, as we will need to remember |
671 | | /// // that those bytes will remain prepended to the actual floats that we |
672 | | /// // write into the out buffer. |
673 | | /// let (out_pref, out_floats, _) = unsafe { out.align_to_mut::<f64>() }; |
674 | | /// let (_, d1_floats, _) = unsafe { data1.align_to::<f64>() }; |
675 | | /// let (_, d2_floats, _) = unsafe { data2.align_to::<f64>() }; |
676 | | /// |
677 | | /// // Put into the destination, floats first from data1 then from data2, ...if |
678 | | /// // possible given the size of `out` |
679 | | /// let mut i: usize = 0; |
680 | | /// for float in d1_floats.iter().chain(d2_floats).take(out_floats.len()) { |
681 | | /// out_floats[i] = *float; |
682 | | /// i += 1; |
683 | | /// } |
684 | | /// |
685 | | /// // Now that we have written the true floats, report back to the fuzzing |
686 | | /// // engine that we left the unaligned `out` prefix bytes at the beginning of |
687 | | /// // `out` and also then the floats that we wrote into the aligned float |
688 | | /// // section. |
689 | | /// out_pref.len() * size_of::<u8>() + i * size_of::<f64>() |
690 | | /// }); |
691 | | /// ``` |
692 | | /// |
693 | | /// This example is a minimized version of [Erik Rigtorp's floating point |
694 | | /// summation fuzzing example][1]. A more detailed version of this experiment |
695 | | /// can be found in the `example_crossover` directory. |
696 | | /// |
697 | | /// [1]: https://rigtorp.se/fuzzing-floating-point-code/ |
698 | | #[macro_export] |
699 | | macro_rules! fuzz_crossover { |
700 | | ( |
701 | | | |
702 | | $data1:ident : &[u8] , |
703 | | $data2:ident : &[u8] , |
704 | | $out:ident : &mut [u8] , |
705 | | $seed:ident : u32 $(,)* |
706 | | | |
707 | | $body:block |
708 | | ) => { |
709 | | /// Auto-generated function. Do not use; only for LibFuzzer's |
710 | | /// consumption. |
711 | | #[export_name = "LLVMFuzzerCustomCrossOver"] |
712 | | #[doc(hidden)] |
713 | | pub unsafe fn rust_fuzzer_custom_crossover( |
714 | | $data1: *const u8, |
715 | | size1: usize, |
716 | | $data2: *const u8, |
717 | | size2: usize, |
718 | | $out: *mut u8, |
719 | | max_out_size: usize, |
720 | | $seed: std::os::raw::c_uint, |
721 | | ) -> usize { |
722 | | let $data1: &[u8] = std::slice::from_raw_parts($data1, size1); |
723 | | let $data2: &[u8] = std::slice::from_raw_parts($data2, size2); |
724 | | let $out: &mut [u8] = std::slice::from_raw_parts_mut($out, max_out_size); |
725 | | |
726 | | // `unsigned int` is generally a `u32`, but not on all targets. Do |
727 | | // an infallible (and potentially lossy, but that's okay because it |
728 | | // preserves determinism) conversion. |
729 | | let $seed = $seed as u32; |
730 | | |
731 | | // Define and invoke a new, safe function so that the body doesn't |
732 | | // inherit `unsafe`. |
733 | | fn custom_crossover( |
734 | | $data1: &[u8], |
735 | | $data2: &[u8], |
736 | | $out: &mut [u8], |
737 | | $seed: u32, |
738 | | ) -> usize { |
739 | | $body |
740 | | } |
741 | | |
742 | | custom_crossover($data1, $data2, $out, $seed) |
743 | | } |
744 | | }; |
745 | | } |