• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 once_cell::sync::OnceCell;
16 
17 extern "C" {
18     // We do not actually cross the FFI bound here.
19     #[allow(improper_ctypes)]
rust_fuzzer_test_input(input: &[u8])20     fn rust_fuzzer_test_input(input: &[u8]);
21 
LLVMFuzzerMutate(data: *mut u8, size: usize, max_size: usize) -> usize22     fn LLVMFuzzerMutate(data: *mut u8, size: usize, max_size: usize) -> usize;
23 }
24 
25 #[doc(hidden)]
26 #[export_name = "LLVMFuzzerTestOneInput"]
test_input_wrap(data: *const u8, size: usize) -> i3227 pub fn test_input_wrap(data: *const u8, size: usize) -> i32 {
28     let test_input = ::std::panic::catch_unwind(|| unsafe {
29         let data_slice = ::std::slice::from_raw_parts(data, size);
30         rust_fuzzer_test_input(data_slice);
31     });
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();
36     }
37     0
38 }
39 
40 #[doc(hidden)]
41 pub static RUST_LIBFUZZER_DEBUG_PATH: OnceCell<String> = OnceCell::new();
42 
43 #[doc(hidden)]
44 #[export_name = "LLVMFuzzerInitialize"]
initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize45 pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize {
46     // Registers a panic hook that aborts the process before unwinding.
47     // It is useful to abort before unwinding so that the fuzzer will then be
48     // able to analyse the process stack frames to tell different bugs appart.
49     //
50     // HACK / FIXME: it would be better to use `-C panic=abort` but it's currently
51     // impossible to build code using compiler plugins with this flag.
52     // We will be able to remove this code when
53     // https://github.com/rust-lang/cargo/issues/5423 is fixed.
54     let default_hook = ::std::panic::take_hook();
55     ::std::panic::set_hook(Box::new(move |panic_info| {
56         default_hook(panic_info);
57         ::std::process::abort();
58     }));
59 
60     // Initialize the `RUST_LIBFUZZER_DEBUG_PATH` cell with the path so it can be
61     // reused with little overhead.
62     if let Ok(path) = std::env::var("RUST_LIBFUZZER_DEBUG_PATH") {
63         RUST_LIBFUZZER_DEBUG_PATH
64             .set(path)
65             .expect("Since this is initialize it is only called once so can never fail");
66     }
67     0
68 }
69 
70 /// Define a fuzz target.
71 ///
72 /// ## Example
73 ///
74 /// This example takes a `&[u8]` slice and attempts to parse it. The parsing
75 /// might fail and return an `Err`, but it shouldn't ever panic or segfault.
76 ///
77 /// ```no_run
78 /// #![no_main]
79 ///
80 /// use libfuzzer_sys::fuzz_target;
81 ///
82 /// // Note: `|input|` is short for `|input: &[u8]|`.
83 /// fuzz_target!(|input| {
84 ///     let _result: Result<_, _> = my_crate::parse(input);
85 /// });
86 /// # mod my_crate { pub fn parse(_: &[u8]) -> Result<(), ()> { unimplemented!() } }
87 /// ```
88 ///
89 /// ## Arbitrary Input Types
90 ///
91 /// The input is a `&[u8]` slice by default, but you can take arbitrary input
92 /// types, as long as the type implements [the `arbitrary` crate's `Arbitrary`
93 /// trait](https://docs.rs/arbitrary/*/arbitrary/trait.Arbitrary.html) (which is
94 /// also re-exported as `libfuzzer_sys::arbitrary::Arbitrary` for convenience).
95 ///
96 /// For example, if you wanted to take an arbitrary RGB color, you could do the
97 /// following:
98 ///
99 /// ```no_run
100 /// #![no_main]
101 /// # mod foo {
102 ///
103 /// use libfuzzer_sys::{arbitrary::{Arbitrary, Error, Unstructured}, fuzz_target};
104 ///
105 /// #[derive(Debug)]
106 /// pub struct Rgb {
107 ///     r: u8,
108 ///     g: u8,
109 ///     b: u8,
110 /// }
111 ///
112 /// impl<'a> Arbitrary<'a> for Rgb {
113 ///     fn arbitrary(raw: &mut Unstructured<'a>) -> Result<Self, Error> {
114 ///         let mut buf = [0; 3];
115 ///         raw.fill_buffer(&mut buf)?;
116 ///         let r = buf[0];
117 ///         let g = buf[1];
118 ///         let b = buf[2];
119 ///         Ok(Rgb { r, g, b })
120 ///     }
121 /// }
122 ///
123 /// // Write a fuzz target that works with RGB colors instead of raw bytes.
124 /// fuzz_target!(|color: Rgb| {
125 ///     my_crate::convert_color(color);
126 /// });
127 /// # mod my_crate {
128 /// #     use super::Rgb;
129 /// #     pub fn convert_color(_: Rgb) {}
130 /// # }
131 /// # }
132 /// ```
133 ///
134 /// You can also enable the `arbitrary` crate's custom derive via this crate's
135 /// `"arbitrary-derive"` cargo feature.
136 #[macro_export]
137 macro_rules! fuzz_target {
138     (|$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!
145 
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;
154             }
155 
156             $body
157         }
158     };
159 
160     (|$data:ident: &[u8]| $body:block) => {
161         fuzz_target!(|$data| $body);
162     };
163 
164     (|$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};
169 
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             }
179 
180             let mut u = Unstructured::new(bytes);
181             let data = <$dty as Arbitrary>::arbitrary_take_rest(u);
182 
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!
186 
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             }
199 
200             let $data = match data {
201                 Ok(d) => d,
202                 Err(_) => return,
203             };
204 
205             $body
206         }
207     };
208 }
209 
210 /// Define a custom mutator.
211 ///
212 /// This is optional, and libFuzzer will use its own, default mutation strategy
213 /// if this is not provided.
214 ///
215 /// You might consider using a custom mutator when your fuzz target is very
216 /// particular about the shape of its input:
217 ///
218 /// * You want to fuzz "deeper" than just the parser.
219 /// * The input contains checksums that have to match the hash of some subset of
220 ///   the data or else the whole thing is invalid, and therefore mutating any of
221 ///   that subset means you need to recompute the checksums.
222 /// * Small random changes to the input buffer make it invalid.
223 ///
224 /// That is, a custom mutator is useful in similar situations where [a `T:
225 /// Arbitrary` input type](macro.fuzz_target.html#arbitrary-input-types) is
226 /// useful. Note that the two approaches are not mutually exclusive; you can use
227 /// whichever is easier for your problem domain or both!
228 ///
229 /// ## Implementation Contract
230 ///
231 /// The original, unmodified input is given in `data[..size]`.
232 ///
233 /// You must modify the data in place and return the new size.
234 ///
235 /// The new size should not be greater than `max_size`. If this is not the case,
236 /// then the `data` will be truncated to fit within `max_size`. Note that
237 /// `max_size < size` is possible when shrinking test cases.
238 ///
239 /// You must produce the same mutation given the same `seed`. Generally, when
240 /// choosing what kind of mutation to make or where to mutate, you should start
241 /// by creating a random number generator (RNG) that is seeded with the given
242 /// `seed` and then consult the RNG whenever making a decision:
243 ///
244 /// ```no_run
245 /// #![no_main]
246 ///
247 /// use rand::{rngs::StdRng, Rng, SeedableRng};
248 ///
249 /// libfuzzer_sys::fuzz_mutator!(|data: &mut [u8], size: usize, max_size: usize, seed: u32| {
250 ///     let mut rng = StdRng::seed_from_u64(seed as u64);
251 ///
252 /// #   let first_mutation = |_, _, _, _| todo!();
253 /// #   let second_mutation = |_, _, _, _| todo!();
254 /// #   let third_mutation = |_, _, _, _| todo!();
255 /// #   let fourth_mutation = |_, _, _, _| todo!();
256 ///     // Choose which of our four supported kinds of mutations we want to make.
257 ///     match rng.gen_range(0..4) {
258 ///         0 => first_mutation(rng, data, size, max_size),
259 ///         1 => second_mutation(rng, data, size, max_size),
260 ///         2 => third_mutation(rng, data, size, max_size),
261 ///         3 => fourth_mutation(rng, data, size, max_size),
262 ///         _ => unreachable!()
263 ///     }
264 /// });
265 /// ```
266 ///
267 /// ## Example: Compression
268 ///
269 /// Consider a simple fuzz target that takes compressed data as input,
270 /// decompresses it, and then asserts that the decompressed data doesn't begin
271 /// with "boom". It is difficult for `libFuzzer` (or any other fuzzer) to crash
272 /// this fuzz target because nearly all mutations it makes will invalidate the
273 /// compression format. Therefore, we use a custom mutator that decompresses the
274 /// raw input, mutates the decompressed data, and then recompresses it. This
275 /// allows `libFuzzer` to quickly discover crashing inputs.
276 ///
277 /// ```no_run
278 /// #![no_main]
279 ///
280 /// use flate2::{read::GzDecoder, write::GzEncoder, Compression};
281 /// use libfuzzer_sys::{fuzz_mutator, fuzz_target};
282 /// use std::io::{Read, Write};
283 ///
284 /// fuzz_target!(|data: &[u8]| {
285 ///     // Decompress the input data and crash if it starts with "boom".
286 ///     if let Some(data) = decompress(data) {
287 ///         if data.starts_with(b"boom") {
288 ///             panic!();
289 ///         }
290 ///     }
291 /// });
292 ///
293 /// fuzz_mutator!(
294 ///     |data: &mut [u8], size: usize, max_size: usize, _seed: u32| {
295 ///         // Decompress the input data. If that fails, use a dummy value.
296 ///         let mut decompressed = decompress(&data[..size]).unwrap_or_else(|| b"hi".to_vec());
297 ///
298 ///         // Mutate the decompressed data with `libFuzzer`'s default mutator. Make
299 ///         // the `decompressed` vec's extra capacity available for insertion
300 ///         // mutations via `resize`.
301 ///         let len = decompressed.len();
302 ///         let cap = decompressed.capacity();
303 ///         decompressed.resize(cap, 0);
304 ///         let new_decompressed_size = libfuzzer_sys::fuzzer_mutate(&mut decompressed, len, cap);
305 ///
306 ///         // Recompress the mutated data.
307 ///         let compressed = compress(&decompressed[..new_decompressed_size]);
308 ///
309 ///         // Copy the recompressed mutated data into `data` and return the new size.
310 ///         let new_size = std::cmp::min(max_size, compressed.len());
311 ///         data[..new_size].copy_from_slice(&compressed[..new_size]);
312 ///         new_size
313 ///     }
314 /// );
315 ///
316 /// fn decompress(compressed_data: &[u8]) -> Option<Vec<u8>> {
317 ///     let mut decoder = GzDecoder::new(compressed_data);
318 ///     let mut decompressed = Vec::new();
319 ///     if decoder.read_to_end(&mut decompressed).is_ok() {
320 ///         Some(decompressed)
321 ///     } else {
322 ///         None
323 ///     }
324 /// }
325 ///
326 /// fn compress(data: &[u8]) -> Vec<u8> {
327 ///     let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
328 ///     encoder
329 ///         .write_all(data)
330 ///         .expect("writing into a vec is infallible");
331 ///     encoder.finish().expect("writing into a vec is infallible")
332 /// }
333 /// ```
334 ///
335 /// This example is inspired by [a similar example from the official `libFuzzer`
336 /// docs](https://github.com/google/fuzzing/blob/master/docs/structure-aware-fuzzing.md#example-compression).
337 ///
338 /// ## More Example Ideas
339 ///
340 /// * A PNG custom mutator that decodes a PNG, mutates the image, and then
341 /// re-encodes the mutated image as a new PNG.
342 ///
343 /// * A [`serde`](https://serde.rs/) custom mutator that deserializes your
344 ///   structure, mutates it, and then reserializes it.
345 ///
346 /// * A Wasm binary custom mutator that inserts, replaces, and removes a
347 ///   bytecode instruction in a function's body.
348 ///
349 /// * An HTTP request custom mutator that inserts, replaces, and removes a
350 ///   header from an HTTP request.
351 #[macro_export]
352 macro_rules! fuzz_mutator {
353     (
354         |
355         $data:ident : &mut [u8] ,
356         $size:ident : usize ,
357         $max_size:ident : usize ,
358         $seed:ident : u32 $(,)*
359         |
360         $body:block
361     ) => {
362         /// Auto-generated function.
363         #[export_name = "LLVMFuzzerCustomMutator"]
364         pub fn rust_fuzzer_custom_mutator(
365             $data: *mut u8,
366             $size: usize,
367             $max_size: usize,
368             $seed: std::os::raw::c_uint,
369         ) -> usize {
370             // Depending on if we are growing or shrinking the test case, `size`
371             // might be larger or smaller than `max_size`. The `data`'s capacity
372             // is the maximum of the two.
373             let len = std::cmp::max($max_size, $size);
374             let $data: &mut [u8] = unsafe { std::slice::from_raw_parts_mut($data, len) };
375 
376             // `unsigned int` is generally a `u32`, but not on all targets. Do
377             // an infallible (and potentially lossy, but that's okay because it
378             // preserves determinism) conversion.
379             let $seed = $seed as u32;
380 
381             // Truncate the new size if it is larger than the max.
382             let new_size = { $body };
383             std::cmp::min(new_size, $max_size)
384         }
385     };
386 }
387 
388 /// The default `libFuzzer` mutator.
389 ///
390 /// You generally don't have to use this at all unless you're defining a
391 /// custom mutator with [the `fuzz_mutator!` macro][crate::fuzz_mutator].
392 ///
393 /// Mutates `data[..size]` in place such that the mutated data is no larger than
394 /// `max_size` and returns the new size of the mutated data.
395 ///
396 /// To only allow shrinking mutations, make `max_size < size`.
397 ///
398 /// To additionally allow mutations that grow the size of the data, make
399 /// `max_size > size`.
400 ///
401 /// Both `size` and `max_size` must be less than or equal to `data.len()`.
402 ///
403 /// # Example
404 ///
405 /// ```no_run
406 /// // Create some data in a buffer.
407 /// let mut data = vec![0; 128];
408 /// data[..b"hello".len()].copy_from_slice(b"hello");
409 ///
410 /// // Ask `libFuzzer` to mutate the data. By setting `max_size` to our buffer's
411 /// // full length, we are allowing `libFuzzer` to perform mutations that grow
412 /// // the size of the data, such as insertions.
413 /// let size = b"hello".len();
414 /// let max_size = data.len();
415 /// let new_size = libfuzzer_sys::fuzzer_mutate(&mut data, size, max_size);
416 ///
417 /// // Get the mutated data out of the buffer.
418 /// let mutated_data = &data[..new_size];
419 /// ```
fuzzer_mutate(data: &mut [u8], size: usize, max_size: usize) -> usize420 pub fn fuzzer_mutate(data: &mut [u8], size: usize, max_size: usize) -> usize {
421     assert!(size <= data.len());
422     assert!(max_size <= data.len());
423     let new_size = unsafe { LLVMFuzzerMutate(data.as_mut_ptr(), size, max_size) };
424     assert!(new_size <= data.len());
425     new_size
426 }
427