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