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