/src/wasmi/fuzz/fuzz_targets/execute.rs
Line | Count | Source |
1 | | #![no_main] |
2 | | |
3 | | use arbitrary::{Arbitrary, Unstructured}; |
4 | | use libfuzzer_sys::fuzz_target; |
5 | | use wasmi::{Config, Engine, Export, Linker, Module, Store, StoreLimitsBuilder, Val, ValType}; |
6 | | use wasmi_fuzz::{ |
7 | | FuzzModule, |
8 | | FuzzSmithConfig, |
9 | | FuzzVal, |
10 | | FuzzValType, |
11 | | FuzzWasmiConfig, |
12 | | config::ValidationMode, |
13 | | }; |
14 | | |
15 | | #[derive(Debug)] |
16 | | pub struct FuzzInput<'a> { |
17 | | config: FuzzWasmiConfig, |
18 | | module: FuzzModule, |
19 | | u: Unstructured<'a>, |
20 | | } |
21 | | |
22 | | impl<'a> Arbitrary<'a> for FuzzInput<'a> { |
23 | 17.3k | fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> { |
24 | 17.3k | let config = FuzzWasmiConfig::arbitrary(u)?; |
25 | 17.3k | let mut fuzz_config = FuzzSmithConfig::arbitrary(u)?; |
26 | 17.3k | fuzz_config.allow_execution(); |
27 | 17.3k | fuzz_config.export_everything(); |
28 | 17.3k | let module = FuzzModule::new(fuzz_config, u)?; |
29 | 17.2k | Ok(Self { |
30 | 17.2k | config, |
31 | 17.2k | module, |
32 | 17.2k | u: Unstructured::new(&[]), |
33 | 17.2k | }) |
34 | 17.3k | } |
35 | | |
36 | 17.3k | fn arbitrary_take_rest(mut u: Unstructured<'a>) -> arbitrary::Result<Self> { |
37 | 17.3k | Self::arbitrary(&mut u).map(|mut input| { |
38 | 17.2k | input.u = u; |
39 | 17.2k | input |
40 | 17.2k | }) |
41 | 17.3k | } |
42 | | } |
43 | | |
44 | | fuzz_target!(|input: FuzzInput| { |
45 | | let FuzzInput { |
46 | | config, |
47 | | module, |
48 | | mut u, |
49 | | } = input; |
50 | | let wasm_bytes = module.wasm().into_bytes(); |
51 | | let wasm = &wasm_bytes[..]; |
52 | | |
53 | | let engine_config = { |
54 | | let mut config = Config::from(config); |
55 | | // We use Wasmi's built-in fuel metering since it is way faster |
56 | | // than `wasm_smith`'s fuel metering and thus allows the fuzzer |
57 | | // to expand its test coverage faster. |
58 | | config.consume_fuel(true); |
59 | | config |
60 | | }; |
61 | | let engine = Engine::new(&engine_config); |
62 | | let linker = Linker::new(&engine); |
63 | | let limiter = StoreLimitsBuilder::new() |
64 | | .memory_size(1000 * 0x10000) |
65 | | .build(); |
66 | | let mut store = Store::new(&engine, limiter); |
67 | | store.limiter(|lim| lim); |
68 | | let Ok(_) = store.set_fuel(1000) else { |
69 | | return; |
70 | | }; |
71 | | if matches!(config.validation_mode, ValidationMode::Unchecked) { |
72 | | // We validate the Wasm module before handing it over to Wasmi |
73 | | // despite `wasm_smith` stating to only produce valid Wasm. |
74 | | // Translating an invalid Wasm module is undefined behavior. |
75 | | if Module::validate(&engine, wasm).is_err() { |
76 | | return; |
77 | | } |
78 | | } |
79 | | let status = match config.validation_mode { |
80 | | ValidationMode::Checked => Module::new(&engine, wasm), |
81 | | ValidationMode::Unchecked => { |
82 | | // Safety: we have just checked Wasm validity above. |
83 | | unsafe { Module::new_unchecked(&engine, wasm) } |
84 | | } |
85 | | }; |
86 | | let module = status.unwrap(); |
87 | | let Ok(instance) = linker.instantiate_and_start(&mut store, &module) else { |
88 | | return; |
89 | | }; |
90 | | |
91 | | let mut params = Vec::new(); |
92 | | let mut results = Vec::new(); |
93 | | |
94 | | let funcs = instance |
95 | | .exports(&store) |
96 | | .filter_map(Export::into_func) |
97 | | .collect::<Vec<_>>(); |
98 | | for func in funcs { |
99 | | let func_ty = func.ty(&store); |
100 | | fill_values(&mut params, func_ty.params(), &mut u); |
101 | | fill_values(&mut results, func_ty.results(), &mut u); |
102 | | _ = func.call(&mut store, ¶ms, &mut results); |
103 | | } |
104 | | }); |
105 | | |
106 | | /// Fill [`Val`]s of type `src` into `dst` using `u` for initialization. |
107 | | /// |
108 | | /// Clears `dst` before the operation. |
109 | 484k | fn fill_values(dst: &mut Vec<Val>, src: &[ValType], u: &mut Unstructured) { |
110 | 484k | dst.clear(); |
111 | 484k | dst.extend( |
112 | 484k | src.iter() |
113 | 484k | .copied() |
114 | 484k | .map(FuzzValType::from) |
115 | 1.99M | .map(|ty| FuzzVal::with_type(ty, u)) |
116 | 484k | .map(Val::from), |
117 | | ); |
118 | 484k | } |