Implementation of a libfuzzer custom mutator for a single-module-fuzzer. This mutator will take the seed specified in `data` and attempt to mutate it with the provided `mutate` function. The `mutate` function may not receive the `data` as-specified, but instead may receive only the seed that was used to generate `data`.
(
data: &mut [u8],
mut size: usize,
max_size: usize,
gen_module: fn(&mut T, &mut Unstructured<'_>) -> Result<(Vec<u8>, KnownValid)>,
mutate: fn(&mut [u8], usize, usize) -> usize,
)
| 147 | /// receive the `data` as-specified, but instead may receive only the seed |
| 148 | /// that was used to generate `data`. |
| 149 | pub fn mutate<T>( |
| 150 | data: &mut [u8], |
| 151 | mut size: usize, |
| 152 | max_size: usize, |
| 153 | gen_module: fn(&mut T, &mut Unstructured<'_>) -> Result<(Vec<u8>, KnownValid)>, |
| 154 | mutate: fn(&mut [u8], usize, usize) -> usize, |
| 155 | ) -> usize |
| 156 | where |
| 157 | T: for<'a> Arbitrary<'a>, |
| 158 | { |
| 159 | // If `data` is a valid wasm module with the fuzz seed at the end, then |
| 160 | // discard the wasm module portion and instead shuffle the seed into the |
| 161 | // beginning of the `data` slice. This is the "de-envelope" part of the |
| 162 | // seed management here. |
| 163 | // |
| 164 | // After this the `data` array should contain the raw contents used to |
| 165 | // produce the module and is ripe for mutation/minimization/etc. |
| 166 | if let Ok(input) = extract_fuzz_input(&data[..size]) { |
| 167 | let start = input.fuzz_data.as_ptr() as usize - data.as_ptr() as usize; |
| 168 | size = input.fuzz_data.len(); |
| 169 | data.copy_within(start..start + input.fuzz_data.len(), 0); |
| 170 | } |
| 171 | |
| 172 | // Delegate to the provided mutation function for standard mutations to |
| 173 | // apply. |
| 174 | let new_size = mutate(data, size, max_size); |
| 175 | |
| 176 | // Next the goal of this function is to produce a test case which is an |
| 177 | // actual wasm module. To that end this will run module generation over the |
| 178 | // input provided. If this is all successful then the custom section |
| 179 | // representing the seed is appended to the module, making it a sort of |
| 180 | // self-referential module. |
| 181 | // |
| 182 | // After all this it's copied into `data` if the it fits. If the module |
| 183 | // doesn't fit then the seed is left un-perturbed since there's not much |
| 184 | // that we can do about that. |
| 185 | let mut u = Unstructured::new(&data[..new_size]); |
| 186 | match u |
| 187 | .arbitrary() |
| 188 | .and_then(|mut config| gen_module(&mut config, &mut u)) |
| 189 | { |
| 190 | Ok((module, _known_valid)) => { |
| 191 | let module = encode_module(&module, &data[..new_size]); |
| 192 | |
| 193 | if module.len() < max_size { |
| 194 | log::debug!( |
| 195 | "successfully generated mutated module with \ |
| 196 | appended input section" |
| 197 | ); |
| 198 | data[..module.len()].copy_from_slice(&module); |
| 199 | return module.len(); |
| 200 | } else { |
| 201 | log::debug!("mutated module doesn't fit in original slice"); |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | // If our new seed can't generate a new module then that's something |
| 206 | // for the fuzzer to figure out later when it "officially" executes |