1*bad9a354SNick Fitzgerald //! Custom fuzz input mutators.
2*bad9a354SNick Fitzgerald //!
3*bad9a354SNick Fitzgerald //! The functions in this module are intended to be used with [the
4*bad9a354SNick Fitzgerald //! `libfuzzer_sys::fuzz_mutator!` macro][fuzz-mutator].
5*bad9a354SNick Fitzgerald //!
6*bad9a354SNick Fitzgerald //! [fuzz-mutator]: https://docs.rs/libfuzzer-sys/latest/libfuzzer_sys/macro.fuzz_mutator.html
7*bad9a354SNick Fitzgerald
8*bad9a354SNick Fitzgerald use arbitrary::{Arbitrary, Unstructured};
9*bad9a354SNick Fitzgerald use std::sync::Arc;
10*bad9a354SNick Fitzgerald
11*bad9a354SNick Fitzgerald /// Use [`wasm-mutate`][wasm-mutate] to mutate a fuzz input.
12*bad9a354SNick Fitzgerald ///
13*bad9a354SNick Fitzgerald /// [wasm-mutate]: https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasm-mutate
wasm_mutate( data: &mut [u8], size: usize, max_size: usize, seed: u32, libfuzzer_mutate: fn(data: &mut [u8], size: usize, max_size: usize) -> usize, ) -> usize14*bad9a354SNick Fitzgerald pub fn wasm_mutate(
15*bad9a354SNick Fitzgerald data: &mut [u8],
16*bad9a354SNick Fitzgerald size: usize,
17*bad9a354SNick Fitzgerald max_size: usize,
18*bad9a354SNick Fitzgerald seed: u32,
19*bad9a354SNick Fitzgerald libfuzzer_mutate: fn(data: &mut [u8], size: usize, max_size: usize) -> usize,
20*bad9a354SNick Fitzgerald ) -> usize {
21*bad9a354SNick Fitzgerald const MUTATION_FUEL: u64 = 100;
22*bad9a354SNick Fitzgerald const MUTATION_ITERS: usize = 100;
23*bad9a354SNick Fitzgerald
24*bad9a354SNick Fitzgerald let wasm = &data[..size];
25*bad9a354SNick Fitzgerald
26*bad9a354SNick Fitzgerald if wasmparser::validate(wasm).is_ok() {
27*bad9a354SNick Fitzgerald let mut wasm_mutate = wasm_mutate::WasmMutate::default();
28*bad9a354SNick Fitzgerald wasm_mutate
29*bad9a354SNick Fitzgerald .seed(seed.into())
30*bad9a354SNick Fitzgerald .fuel(MUTATION_FUEL)
31*bad9a354SNick Fitzgerald .reduce(max_size < size)
32*bad9a354SNick Fitzgerald .raw_mutate_func(Some(Arc::new(move |data, max_size| {
33*bad9a354SNick Fitzgerald let len = data.len();
34*bad9a354SNick Fitzgerald
35*bad9a354SNick Fitzgerald // The given max could be very large, so clamp it to no more
36*bad9a354SNick Fitzgerald // than `len * 2` in any single, given mutation. This way we
37*bad9a354SNick Fitzgerald // don't over-allocate a bunch of space.
38*bad9a354SNick Fitzgerald let max_size = std::cmp::min(max_size, len * 2);
39*bad9a354SNick Fitzgerald // Also, the max must always be greater than zero (`libfuzzer`
40*bad9a354SNick Fitzgerald // asserts this).
41*bad9a354SNick Fitzgerald let max_size = std::cmp::max(max_size, 1);
42*bad9a354SNick Fitzgerald
43*bad9a354SNick Fitzgerald // Make sure we have capacity in case `libfuzzer` decides to
44*bad9a354SNick Fitzgerald // grow this data.
45*bad9a354SNick Fitzgerald if max_size > len {
46*bad9a354SNick Fitzgerald data.resize(max_size, 0);
47*bad9a354SNick Fitzgerald }
48*bad9a354SNick Fitzgerald
49*bad9a354SNick Fitzgerald // Finally, have `libfuzzer` mutate the data!
50*bad9a354SNick Fitzgerald let new_len = libfuzzer_mutate(data, len, max_size);
51*bad9a354SNick Fitzgerald
52*bad9a354SNick Fitzgerald // Resize the data to the mutated size, releasing any extra
53*bad9a354SNick Fitzgerald // capacity that we don't need anymore.
54*bad9a354SNick Fitzgerald data.resize(new_len, 0);
55*bad9a354SNick Fitzgerald data.shrink_to_fit();
56*bad9a354SNick Fitzgerald
57*bad9a354SNick Fitzgerald Ok(())
58*bad9a354SNick Fitzgerald })));
59*bad9a354SNick Fitzgerald
60*bad9a354SNick Fitzgerald let wasm = wasm.to_vec();
61*bad9a354SNick Fitzgerald let mutations = wasm_mutate.run(&wasm);
62*bad9a354SNick Fitzgerald if let Ok(mutations) = mutations {
63*bad9a354SNick Fitzgerald for mutation in mutations.take(MUTATION_ITERS) {
64*bad9a354SNick Fitzgerald if let Ok(mutated_wasm) = mutation {
65*bad9a354SNick Fitzgerald if mutated_wasm.len() <= max_size {
66*bad9a354SNick Fitzgerald data[..mutated_wasm.len()].copy_from_slice(&mutated_wasm);
67*bad9a354SNick Fitzgerald return mutated_wasm.len();
68*bad9a354SNick Fitzgerald }
69*bad9a354SNick Fitzgerald }
70*bad9a354SNick Fitzgerald }
71*bad9a354SNick Fitzgerald }
72*bad9a354SNick Fitzgerald }
73*bad9a354SNick Fitzgerald
74*bad9a354SNick Fitzgerald // If we can't mutate the input because it isn't valid Wasm or `wasm-mutate`
75*bad9a354SNick Fitzgerald // otherwise fails, try to use `wasm-smith` to generate a new, arbitrary
76*bad9a354SNick Fitzgerald // Wasm module that fits within the max-size limit.
77*bad9a354SNick Fitzgerald let mut u = Unstructured::new(&data[..max_size]);
78*bad9a354SNick Fitzgerald if let Ok(module) = wasm_smith::Module::arbitrary(&mut u) {
79*bad9a354SNick Fitzgerald let wasm = module.to_bytes();
80*bad9a354SNick Fitzgerald if wasm.len() <= max_size {
81*bad9a354SNick Fitzgerald data[..wasm.len()].copy_from_slice(&wasm);
82*bad9a354SNick Fitzgerald return wasm.len();
83*bad9a354SNick Fitzgerald }
84*bad9a354SNick Fitzgerald }
85*bad9a354SNick Fitzgerald
86*bad9a354SNick Fitzgerald // Otherwise, try to return an empty Wasm module:
87*bad9a354SNick Fitzgerald //
88*bad9a354SNick Fitzgerald // ```
89*bad9a354SNick Fitzgerald // (module)
90*bad9a354SNick Fitzgerald // ```
91*bad9a354SNick Fitzgerald static EMPTY_WASM: &[u8] = &[0x00, b'a', b's', b'm', 0x01, 0x00, 0x00, 0x00];
92*bad9a354SNick Fitzgerald if EMPTY_WASM.len() <= max_size {
93*bad9a354SNick Fitzgerald data[..EMPTY_WASM.len()].copy_from_slice(EMPTY_WASM);
94*bad9a354SNick Fitzgerald return EMPTY_WASM.len();
95*bad9a354SNick Fitzgerald }
96*bad9a354SNick Fitzgerald
97*bad9a354SNick Fitzgerald // If the max size is even smaller than an empty Wasm module, then just let
98*bad9a354SNick Fitzgerald // `libfuzzer` mutate the data.
99*bad9a354SNick Fitzgerald libfuzzer_mutate(data, size, max_size)
100*bad9a354SNick Fitzgerald }
101