1 #![no_main] 2 3 use libfuzzer_sys::arbitrary::{Arbitrary, Unstructured}; 4 use libfuzzer_sys::{fuzz_mutator, fuzz_target, fuzzer_mutate}; 5 use mutatis::Session; 6 use postcard::{from_bytes, to_slice}; 7 use rand::{Rng, SeedableRng}; 8 use wasmtime_fuzzing::generators::ExceptionOps; 9 use wasmtime_fuzzing::oracles::exception_ops; 10 11 fuzz_target!(|data: &[u8]| { 12 let Ok((seed, ops)) = postcard::from_bytes::<(u64, ExceptionOps)>(data) else { 13 return; 14 }; 15 16 let mut buf = [0u8; 1024]; 17 let mut rng = rand::rngs::StdRng::seed_from_u64(seed); 18 rng.fill(&mut buf); 19 20 let u = Unstructured::new(&buf); 21 let Ok(config) = wasmtime_fuzzing::generators::Config::arbitrary_take_rest(u) else { 22 return; 23 }; 24 25 let _ = exception_ops(config, ops); 26 }); 27 28 fuzz_mutator!(|data: &mut [u8], size: usize, max_size: usize, seed: u32| { 29 let _ = env_logger::try_init(); 30 31 // With probability of about 1/8, use default mutator 32 if seed & 7 == 0 { 33 return fuzzer_mutate(data, size, max_size); 34 } 35 36 // Try to decode using postcard; fallback to default input on failure 37 let mut tuple: (u64, ExceptionOps) = from_bytes(&data[..size]).ok().unwrap_or_default(); 38 39 let mut session = Session::new().seed(seed.into()).shrink(max_size < size); 40 41 if session.mutate(&mut tuple).is_ok() { 42 loop { 43 if let Ok(encoded) = to_slice(&tuple, data) { 44 return encoded.len(); 45 } 46 47 // Attempt to shrink scenarios if encoding fails (e.g., buffer too small) 48 if tuple.1.pop() { 49 continue; 50 } 51 52 break; 53 } 54 } 55 56 // Fallback to default libfuzzer mutator 57 fuzzer_mutate(data, size, max_size) 58 }); 59