/src/wasm-tools/fuzz/src/no_traps.rs
Line | Count | Source |
1 | | use arbitrary::{Result, Unstructured}; |
2 | | use wasmparser::WasmFeatures; |
3 | | #[cfg(feature = "wasmtime")] |
4 | | use wasmtime::*; |
5 | | |
6 | | #[cfg(feature = "wasmtime")] |
7 | | #[path = "../../crates/fuzz-stats/src/lib.rs"] |
8 | | pub mod fuzz_stats; |
9 | | |
10 | | // Define a fuzz target that accepts arbitrary |
11 | | // `Module`s as input. |
12 | 1.59k | pub fn run(u: &mut Unstructured<'_>) -> Result<()> { |
13 | | // Use data to generate a random wasm module |
14 | 1.59k | let (wasm_bytes, config) = crate::generate_valid_module(u, |config, _| { |
15 | 1.59k | config.disallow_traps = true; |
16 | 1.59k | config.threads_enabled = false; |
17 | 1.59k | config.exceptions_enabled = false; |
18 | 1.59k | config.gc_enabled = false; |
19 | 1.59k | config.max_memory32_bytes = config.max_memory32_bytes.min(1 << 18); |
20 | 1.59k | config.max_memory64_bytes = config.max_memory64_bytes.min(1 << 18); |
21 | | |
22 | 1.59k | Ok(()) |
23 | 1.59k | })?; |
24 | 1.58k | validate_module(&wasm_bytes); |
25 | | |
26 | | // Not implemented in wasmtime at this time. |
27 | 1.58k | if config.compact_imports_enabled { |
28 | 493 | return Ok(()); |
29 | 1.09k | } |
30 | | |
31 | | #[cfg(feature = "wasmtime")] |
32 | | { |
33 | | // Configure the engine, module, and store |
34 | | let mut eng_conf = wasmtime::Config::new(); |
35 | | eng_conf.wasm_wide_arithmetic(true); |
36 | | eng_conf.wasm_custom_page_sizes(true); |
37 | | eng_conf.consume_fuel(true); |
38 | | let engine = Engine::new(&eng_conf).unwrap(); |
39 | | let module = match Module::from_binary(&engine, &wasm_bytes) { |
40 | | Ok(m) => m, |
41 | | // Waiting on wasmtime to get published to fix this. |
42 | | Err(e) if format!("{e:?}").contains("unsupported init expr in element section") => { |
43 | | return Ok(()); |
44 | | } |
45 | | Err(e) => panic!("failed to compile module {e:?}"), |
46 | | }; |
47 | | |
48 | | // Call all exported functions |
49 | | for export in module.exports() { |
50 | | match export.ty() { |
51 | | ExternType::Func(func_ty) => { |
52 | | let mut store = Store::new( |
53 | | &engine, |
54 | | fuzz_stats::limits::StoreLimits { |
55 | | remaining_memory: 1 << 30, |
56 | | oom: false, |
57 | | }, |
58 | | ); |
59 | | store.limiter(|s| s as &mut dyn ResourceLimiter); |
60 | | store.set_fuel(1_000).unwrap(); |
61 | | |
62 | | // Instantiate the module |
63 | | let inst_result = fuzz_stats::dummy::dummy_imports(&mut store, &module) |
64 | | .and_then(|imports| Instance::new(&mut store, &module, &imports)); |
65 | | let instance = match inst_result { |
66 | | Ok(r) => r, |
67 | | Err(err) => return Ok(check_err(err)), |
68 | | }; |
69 | | |
70 | | let args = fuzz_stats::dummy::dummy_values(func_ty.params()); |
71 | | let mut results = fuzz_stats::dummy::dummy_values(func_ty.results()); |
72 | | let func = instance.get_func(&mut store, export.name()).unwrap(); |
73 | | match func.call(&mut store, &args, &mut results) { |
74 | | Ok(_) => {} |
75 | | Err(err) => check_err(err), |
76 | | } |
77 | | } |
78 | | _ => continue, |
79 | | } |
80 | | } |
81 | | |
82 | | fn check_err(err: wasmtime::Error) { |
83 | | // Allow stack overflow since this generally can't be protected |
84 | | // against as it's an implementation detail of cranelift we could |
85 | | // expose regardless of the limits placed on the function. |
86 | | if let Some(wasmtime::Trap::StackOverflow) = err.downcast_ref::<wasmtime::Trap>() { |
87 | | return; |
88 | | } |
89 | | |
90 | | // Allow out of fuel on module instantiation |
91 | | if let Some(wasmtime::Trap::OutOfFuel) = err.downcast_ref::<wasmtime::Trap>() { |
92 | | return; |
93 | | } |
94 | | |
95 | | let s = err.to_string(); |
96 | | // Allow "nominal" traps such as running out of fuel and the |
97 | | // module trying to allocate more resources than we'd like to |
98 | | // allow it (e.g. lots of memories or lots of tables). |
99 | | if s.contains("all fuel consumed") || s.contains("Insufficient resources") { |
100 | | return; |
101 | | } |
102 | | |
103 | | // Otherwise though this is a bug. |
104 | | panic!("generated wasm trapped in non-trapping mode: {}", err) |
105 | | } |
106 | | } |
107 | 1.09k | Ok(()) |
108 | 1.59k | } |
109 | | |
110 | 1.58k | fn validate_module(wasm_bytes: &Vec<u8>) { |
111 | | // Validate the module or component and assert that it passes validation. |
112 | 1.58k | let mut validator = wasmparser::Validator::new_with_features(WasmFeatures::all()); |
113 | 1.58k | if let Err(e) = validator.validate_all(wasm_bytes) { |
114 | 0 | panic!("Invalid module: {e}"); |
115 | 1.58k | } |
116 | 1.58k | } |
117 | | |
118 | | #[test] |
119 | | fn smoke() { |
120 | | super::test::test_n_times(100, run); |
121 | | } |