/src/wasmtime/crates/fuzzing/src/generators/stacks.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! Generate a Wasm program that keeps track of its current stack frames. |
2 | | //! |
3 | | //! We can then compare the stack trace we observe in Wasmtime to what the Wasm |
4 | | //! program believes its stack should be. Any discrepancies between the two |
5 | | //! points to a bug in either this test case generator or Wasmtime's stack |
6 | | //! walker. |
7 | | |
8 | | use std::mem; |
9 | | |
10 | | use arbitrary::{Arbitrary, Result, Unstructured}; |
11 | | use wasm_encoder::{Instruction, ValType}; |
12 | | |
13 | | const MAX_FUNCS: u32 = 20; |
14 | | const MAX_OPS: usize = 1_000; |
15 | | const MAX_PARAMS: usize = 10; |
16 | | |
17 | | /// Generate a Wasm module that keeps track of its current call stack, to |
18 | | /// compare to the host. |
19 | | #[derive(Debug)] |
20 | | pub struct Stacks { |
21 | | funcs: Vec<Function>, |
22 | | inputs: Vec<u8>, |
23 | | } |
24 | | |
25 | | #[derive(Debug, Default)] |
26 | | struct Function { |
27 | | ops: Vec<Op>, |
28 | | params: usize, |
29 | | results: usize, |
30 | | } |
31 | | |
32 | | #[derive(Debug, Clone, Copy)] |
33 | | enum Op { |
34 | | CheckStackInHost, |
35 | | Call(u32), |
36 | | CallThroughHost(u32), |
37 | | ReturnCall(u32), |
38 | | } |
39 | | |
40 | | impl<'a> Arbitrary<'a> for Stacks { |
41 | 4.92k | fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> { |
42 | 4.92k | let funcs = Self::arbitrary_funcs(u)?; |
43 | 4.92k | let n = u.len().min(200); |
44 | 4.92k | let inputs = u.bytes(n)?.to_vec(); |
45 | 4.92k | Ok(Stacks { funcs, inputs }) |
46 | 4.92k | } |
47 | | } |
48 | | |
49 | | impl Stacks { |
50 | 4.92k | fn arbitrary_funcs(u: &mut Unstructured) -> Result<Vec<Function>> { |
51 | | // Generate a list of functions first with a number of parameters and |
52 | | // results. Bodies are generated afterwards. |
53 | 4.92k | let nfuncs = u.int_in_range(1..=MAX_FUNCS)?; |
54 | 4.92k | let mut funcs = (0..nfuncs) |
55 | 60.6k | .map(|_| { |
56 | 60.6k | Ok(Function { |
57 | 60.6k | ops: Vec::new(), // generated later |
58 | 60.6k | params: u.int_in_range(0..=MAX_PARAMS)?, |
59 | 60.6k | results: u.int_in_range(0..=MAX_PARAMS)?, |
60 | | }) |
61 | 60.6k | }) |
62 | 4.92k | .collect::<Result<Vec<_>>>()?; |
63 | 4.92k | let mut funcs_by_result = vec![Vec::new(); MAX_PARAMS + 1]; |
64 | 60.6k | for (i, func) in funcs.iter().enumerate() { |
65 | 60.6k | funcs_by_result[func.results].push(i as u32); |
66 | 60.6k | } |
67 | | |
68 | | // Fill in each function body with various instructions/operations now |
69 | | // that the set of functions is known. |
70 | 60.6k | for f in funcs.iter_mut() { |
71 | 60.6k | let funcs_with_same_results = &funcs_by_result[f.results]; |
72 | 60.6k | for _ in 0..u.arbitrary_len::<usize>()?.min(MAX_OPS) { |
73 | 2.29M | let op = match u.int_in_range(0..=3)? { |
74 | 336k | 0 => Op::CheckStackInHost, |
75 | 1.35M | 1 => Op::Call(u.int_in_range(0..=nfuncs - 1)?), |
76 | 592k | 2 => Op::CallThroughHost(u.int_in_range(0..=nfuncs - 1)?), |
77 | | // This only works if the target function has the same |
78 | | // number of results, so choose from a different set here. |
79 | 12.4k | 3 => Op::ReturnCall(*u.choose(funcs_with_same_results)?), |
80 | 0 | _ => unreachable!(), |
81 | | }; |
82 | 2.29M | f.ops.push(op); |
83 | 2.29M | // once a `return_call` has been generated there's no need to |
84 | 2.29M | // generate any more instructions, so fall through to below. |
85 | 2.29M | if let Some(Op::ReturnCall(_)) = f.ops.last() { |
86 | 12.4k | break; |
87 | 2.28M | } |
88 | | } |
89 | | } |
90 | | |
91 | 4.92k | Ok(funcs) |
92 | 4.92k | } |
93 | | |
94 | | /// Get the input values to run the Wasm module with. |
95 | 4.92k | pub fn inputs(&self) -> &[u8] { |
96 | 4.92k | &self.inputs |
97 | 4.92k | } |
98 | | |
99 | | /// Get this test case's Wasm module. |
100 | | /// |
101 | | /// The Wasm module has the following imports: |
102 | | /// |
103 | | /// * `host.check_stack: [] -> []`: The host can check the Wasm's |
104 | | /// understanding of its own stack against the host's understanding of the |
105 | | /// Wasm stack to find discrepancy bugs. |
106 | | /// |
107 | | /// * `host.call_func: [funcref] -> []`: The host should call the given |
108 | | /// `funcref`, creating a call stack with multiple sequences of contiguous |
109 | | /// Wasm frames on the stack like `[..., wasm, host, wasm]`. |
110 | | /// |
111 | | /// The Wasm module has the following exports: |
112 | | /// |
113 | | /// * `run: [i32] -> []`: This function should be called with each of the |
114 | | /// input values to run this generated test case. |
115 | | /// |
116 | | /// * `get_stack: [] -> [i32 i32]`: Get the pointer and length of the `u32` |
117 | | /// array of this Wasm's understanding of its stack. This is useful for |
118 | | /// checking whether the host's view of the stack at a trap matches the |
119 | | /// Wasm program's understanding. |
120 | 4.92k | pub fn wasm(&self) -> Vec<u8> { |
121 | 4.92k | let mut module = wasm_encoder::Module::new(); |
122 | 4.92k | |
123 | 4.92k | let mut types = wasm_encoder::TypeSection::new(); |
124 | 4.92k | |
125 | 4.92k | let run_type = types.len(); |
126 | 4.92k | types |
127 | 4.92k | .ty() |
128 | 4.92k | .function(vec![wasm_encoder::ValType::I32], vec![]); |
129 | 4.92k | |
130 | 4.92k | let get_stack_type = types.len(); |
131 | 4.92k | types.ty().function( |
132 | 4.92k | vec![], |
133 | 4.92k | vec![wasm_encoder::ValType::I32, wasm_encoder::ValType::I32], |
134 | 4.92k | ); |
135 | 4.92k | |
136 | 4.92k | let call_func_type = types.len(); |
137 | 4.92k | types |
138 | 4.92k | .ty() |
139 | 4.92k | .function(vec![wasm_encoder::ValType::FUNCREF], vec![]); |
140 | 4.92k | |
141 | 4.92k | let check_stack_type = types.len(); |
142 | 4.92k | types.ty().function(vec![], vec![]); |
143 | 4.92k | |
144 | 4.92k | let func_types_start = types.len(); |
145 | 60.6k | for func in self.funcs.iter() { |
146 | 60.6k | types.ty().function( |
147 | 60.6k | vec![ValType::I32; func.params], |
148 | 60.6k | vec![ValType::I32; func.results], |
149 | 60.6k | ); |
150 | 60.6k | } |
151 | | |
152 | 4.92k | section(&mut module, types); |
153 | 4.92k | |
154 | 4.92k | let mut imports = wasm_encoder::ImportSection::new(); |
155 | 4.92k | let check_stack_func = 0; |
156 | 4.92k | imports.import( |
157 | 4.92k | "host", |
158 | 4.92k | "check_stack", |
159 | 4.92k | wasm_encoder::EntityType::Function(check_stack_type), |
160 | 4.92k | ); |
161 | 4.92k | let call_func_func = 1; |
162 | 4.92k | imports.import( |
163 | 4.92k | "host", |
164 | 4.92k | "call_func", |
165 | 4.92k | wasm_encoder::EntityType::Function(call_func_type), |
166 | 4.92k | ); |
167 | 4.92k | let num_imported_funcs = 2; |
168 | 4.92k | section(&mut module, imports); |
169 | 4.92k | |
170 | 4.92k | let mut funcs = wasm_encoder::FunctionSection::new(); |
171 | 60.6k | for (i, _) in self.funcs.iter().enumerate() { |
172 | 60.6k | funcs.function(func_types_start + (i as u32)); |
173 | 60.6k | } |
174 | 4.92k | let run_func = funcs.len() + num_imported_funcs; |
175 | 4.92k | funcs.function(run_type); |
176 | 4.92k | let get_stack_func = funcs.len() + num_imported_funcs; |
177 | 4.92k | funcs.function(get_stack_type); |
178 | 4.92k | section(&mut module, funcs); |
179 | 4.92k | |
180 | 4.92k | let mut mems = wasm_encoder::MemorySection::new(); |
181 | 4.92k | let memory = mems.len(); |
182 | 4.92k | mems.memory(wasm_encoder::MemoryType { |
183 | 4.92k | minimum: 1, |
184 | 4.92k | maximum: Some(1), |
185 | 4.92k | memory64: false, |
186 | 4.92k | shared: false, |
187 | 4.92k | page_size_log2: None, |
188 | 4.92k | }); |
189 | 4.92k | section(&mut module, mems); |
190 | 4.92k | |
191 | 4.92k | let mut globals = wasm_encoder::GlobalSection::new(); |
192 | 4.92k | let fuel_global = globals.len(); |
193 | 4.92k | globals.global( |
194 | 4.92k | wasm_encoder::GlobalType { |
195 | 4.92k | val_type: wasm_encoder::ValType::I32, |
196 | 4.92k | mutable: true, |
197 | 4.92k | shared: false, |
198 | 4.92k | }, |
199 | 4.92k | &wasm_encoder::ConstExpr::i32_const(0), |
200 | 4.92k | ); |
201 | 4.92k | let stack_len_global = globals.len(); |
202 | 4.92k | globals.global( |
203 | 4.92k | wasm_encoder::GlobalType { |
204 | 4.92k | val_type: wasm_encoder::ValType::I32, |
205 | 4.92k | mutable: true, |
206 | 4.92k | shared: false, |
207 | 4.92k | }, |
208 | 4.92k | &wasm_encoder::ConstExpr::i32_const(0), |
209 | 4.92k | ); |
210 | 4.92k | section(&mut module, globals); |
211 | 4.92k | |
212 | 4.92k | let mut exports = wasm_encoder::ExportSection::new(); |
213 | 4.92k | exports.export("run", wasm_encoder::ExportKind::Func, run_func); |
214 | 4.92k | exports.export("get_stack", wasm_encoder::ExportKind::Func, get_stack_func); |
215 | 4.92k | exports.export("memory", wasm_encoder::ExportKind::Memory, memory); |
216 | 4.92k | exports.export("fuel", wasm_encoder::ExportKind::Global, fuel_global); |
217 | 4.92k | section(&mut module, exports); |
218 | 4.92k | |
219 | 4.92k | let mut elems = wasm_encoder::ElementSection::new(); |
220 | 4.92k | elems.declared(wasm_encoder::Elements::Functions( |
221 | 4.92k | (0..num_imported_funcs + u32::try_from(self.funcs.len()).unwrap()) |
222 | 4.92k | .collect::<Vec<_>>() |
223 | 4.92k | .into(), |
224 | 4.92k | )); |
225 | 4.92k | section(&mut module, elems); |
226 | 4.92k | |
227 | 131k | let check_fuel = |body: &mut wasm_encoder::Function| { |
228 | 131k | // Trap if we are out of fuel. |
229 | 131k | body.instruction(&Instruction::GlobalGet(fuel_global)) |
230 | 131k | .instruction(&Instruction::I32Eqz) |
231 | 131k | .instruction(&Instruction::If(wasm_encoder::BlockType::Empty)) |
232 | 131k | .instruction(&Instruction::Unreachable) |
233 | 131k | .instruction(&Instruction::End); |
234 | 131k | |
235 | 131k | // Decrement fuel. |
236 | 131k | body.instruction(&Instruction::GlobalGet(fuel_global)) |
237 | 131k | .instruction(&Instruction::I32Const(1)) |
238 | 131k | .instruction(&Instruction::I32Sub) |
239 | 131k | .instruction(&Instruction::GlobalSet(fuel_global)); |
240 | 131k | }; |
241 | | |
242 | 65.5k | let push_func_to_stack = |body: &mut wasm_encoder::Function, func: u32| { |
243 | 65.5k | // Add this function to our internal stack. |
244 | 65.5k | // |
245 | 65.5k | // Note that we know our `stack_len_global` can't go beyond memory |
246 | 65.5k | // bounds because we limit fuel to at most `u8::MAX` and each stack |
247 | 65.5k | // entry is an `i32` and `u8::MAX * size_of(i32)` still fits in one |
248 | 65.5k | // Wasm page. |
249 | 65.5k | body.instruction(&Instruction::GlobalGet(stack_len_global)) |
250 | 65.5k | .instruction(&Instruction::I32Const(func as i32)) |
251 | 65.5k | .instruction(&Instruction::I32Store(wasm_encoder::MemArg { |
252 | 65.5k | offset: 0, |
253 | 65.5k | align: 0, |
254 | 65.5k | memory_index: memory, |
255 | 65.5k | })) |
256 | 65.5k | .instruction(&Instruction::GlobalGet(stack_len_global)) |
257 | 65.5k | .instruction(&Instruction::I32Const(mem::size_of::<i32>() as i32)) |
258 | 65.5k | .instruction(&Instruction::I32Add) |
259 | 65.5k | .instruction(&Instruction::GlobalSet(stack_len_global)); |
260 | 65.5k | }; |
261 | | |
262 | 65.5k | let pop_func_from_stack = |body: &mut wasm_encoder::Function| { |
263 | 65.5k | // Remove this function from our internal stack. |
264 | 65.5k | body.instruction(&Instruction::GlobalGet(stack_len_global)) |
265 | 65.5k | .instruction(&Instruction::I32Const(mem::size_of::<i32>() as i32)) |
266 | 65.5k | .instruction(&Instruction::I32Sub) |
267 | 65.5k | .instruction(&Instruction::GlobalSet(stack_len_global)); |
268 | 65.5k | }; |
269 | | |
270 | 1.37M | let push_params = |body: &mut wasm_encoder::Function, func: u32| { |
271 | 1.37M | let func = &self.funcs[func as usize]; |
272 | 8.05M | for _ in 0..func.params { |
273 | 8.05M | body.instruction(&Instruction::I32Const(0)); |
274 | 8.05M | } |
275 | 1.37M | }; |
276 | 1.36M | let pop_results = |body: &mut wasm_encoder::Function, func: u32| { |
277 | 1.36M | let func = &self.funcs[func as usize]; |
278 | 8.79M | for _ in 0..func.results { |
279 | 8.79M | body.instruction(&Instruction::Drop); |
280 | 8.79M | } |
281 | 1.36M | }; |
282 | 48.1k | let push_results = |body: &mut wasm_encoder::Function, func: u32| { |
283 | 48.1k | let func = &self.funcs[func as usize]; |
284 | 179k | for _ in 0..func.results { |
285 | 179k | body.instruction(&Instruction::I32Const(0)); |
286 | 179k | } |
287 | 48.1k | }; |
288 | | |
289 | 4.92k | let mut code = wasm_encoder::CodeSection::new(); |
290 | 60.6k | for (func_index, func) in self.funcs.iter().enumerate() { |
291 | 60.6k | let mut body = wasm_encoder::Function::new(vec![]); |
292 | 60.6k | |
293 | 60.6k | push_func_to_stack( |
294 | 60.6k | &mut body, |
295 | 60.6k | num_imported_funcs + u32::try_from(func_index).unwrap(), |
296 | 60.6k | ); |
297 | 60.6k | check_fuel(&mut body); |
298 | 60.6k | |
299 | 60.6k | let mut check_fuel_and_pop_at_end = true; |
300 | | |
301 | | // Perform our specified operations. |
302 | 2.35M | for op in &func.ops { |
303 | 2.29M | assert!(check_fuel_and_pop_at_end); |
304 | 2.29M | match op { |
305 | 336k | Op::CheckStackInHost => { |
306 | 336k | body.instruction(&Instruction::Call(check_stack_func)); |
307 | 336k | } |
308 | 1.35M | Op::Call(f) => { |
309 | 1.35M | push_params(&mut body, *f); |
310 | 1.35M | body.instruction(&Instruction::Call(f + num_imported_funcs)); |
311 | 1.35M | pop_results(&mut body, *f); |
312 | 1.35M | } |
313 | 592k | Op::CallThroughHost(f) => { |
314 | 592k | body.instruction(&Instruction::RefFunc(f + num_imported_funcs)) |
315 | 592k | .instruction(&Instruction::Call(call_func_func)); |
316 | 592k | } |
317 | | |
318 | | // For a `return_call` preemptively check fuel to possibly |
319 | | // trap and then pop our function from the in-wasm managed |
320 | | // stack. After that execute the `return_call` itself. |
321 | 12.4k | Op::ReturnCall(f) => { |
322 | 12.4k | push_params(&mut body, *f); |
323 | 12.4k | check_fuel(&mut body); |
324 | 12.4k | pop_func_from_stack(&mut body); |
325 | 12.4k | check_fuel_and_pop_at_end = false; |
326 | 12.4k | body.instruction(&Instruction::ReturnCall(f + num_imported_funcs)); |
327 | 12.4k | } |
328 | | } |
329 | | } |
330 | | |
331 | | // Potentially trap at the end of our function as well, so that we |
332 | | // exercise the scenario where the Wasm-to-host trampoline |
333 | | // initialized `last_wasm_exit_sp` et al when calling out to a host |
334 | | // function, but then we returned back to Wasm and then trapped |
335 | | // while `last_wasm_exit_sp` et al are still initialized from that |
336 | | // previous host call. |
337 | 60.6k | if check_fuel_and_pop_at_end { |
338 | 48.1k | check_fuel(&mut body); |
339 | 48.1k | pop_func_from_stack(&mut body); |
340 | 48.1k | push_results(&mut body, func_index as u32); |
341 | 48.1k | } |
342 | | |
343 | 60.6k | function(&mut code, body); |
344 | | } |
345 | | |
346 | 4.92k | let mut run_body = wasm_encoder::Function::new(vec![]); |
347 | 4.92k | |
348 | 4.92k | // Reset the bump pointer for the internal stack (this allows us to |
349 | 4.92k | // reuse an instance in the oracle, rather than re-instantiate). |
350 | 4.92k | run_body |
351 | 4.92k | .instruction(&Instruction::I32Const(0)) |
352 | 4.92k | .instruction(&Instruction::GlobalSet(stack_len_global)); |
353 | 4.92k | |
354 | 4.92k | // Initialize the fuel global. |
355 | 4.92k | run_body |
356 | 4.92k | .instruction(&Instruction::LocalGet(0)) |
357 | 4.92k | .instruction(&Instruction::GlobalSet(fuel_global)); |
358 | 4.92k | |
359 | 4.92k | push_func_to_stack(&mut run_body, run_func); |
360 | 4.92k | |
361 | 4.92k | // Make sure to check for out-of-fuel in the `run` function as well, so |
362 | 4.92k | // that we also capture stack traces with only one frame, not just `run` |
363 | 4.92k | // followed by the first locally-defined function and then zero or more |
364 | 4.92k | // extra frames. |
365 | 4.92k | check_fuel(&mut run_body); |
366 | 4.92k | |
367 | 4.92k | // Call the first locally defined function. |
368 | 4.92k | push_params(&mut run_body, 0); |
369 | 4.92k | run_body.instruction(&Instruction::Call(num_imported_funcs)); |
370 | 4.92k | pop_results(&mut run_body, 0); |
371 | 4.92k | |
372 | 4.92k | check_fuel(&mut run_body); |
373 | 4.92k | pop_func_from_stack(&mut run_body); |
374 | 4.92k | |
375 | 4.92k | function(&mut code, run_body); |
376 | 4.92k | |
377 | 4.92k | let mut get_stack_body = wasm_encoder::Function::new(vec![]); |
378 | 4.92k | get_stack_body |
379 | 4.92k | .instruction(&Instruction::I32Const(0)) |
380 | 4.92k | .instruction(&Instruction::GlobalGet(stack_len_global)); |
381 | 4.92k | function(&mut code, get_stack_body); |
382 | 4.92k | |
383 | 4.92k | section(&mut module, code); |
384 | 4.92k | |
385 | 4.92k | return module.finish(); |
386 | | |
387 | | // Helper that defines a section in the module and takes ownership of it |
388 | | // so that it is dropped and its memory reclaimed after adding it to the |
389 | | // module. |
390 | 39.4k | fn section(module: &mut wasm_encoder::Module, section: impl wasm_encoder::Section) { |
391 | 39.4k | module.section(§ion); |
392 | 39.4k | } <wasmtime_fuzzing::generators::stacks::Stacks>::wasm::section::<wasm_encoder::core::code::CodeSection> Line | Count | Source | 390 | 4.92k | fn section(module: &mut wasm_encoder::Module, section: impl wasm_encoder::Section) { | 391 | 4.92k | module.section(§ion); | 392 | 4.92k | } |
<wasmtime_fuzzing::generators::stacks::Stacks>::wasm::section::<wasm_encoder::core::types::TypeSection> Line | Count | Source | 390 | 4.92k | fn section(module: &mut wasm_encoder::Module, section: impl wasm_encoder::Section) { | 391 | 4.92k | module.section(§ion); | 392 | 4.92k | } |
<wasmtime_fuzzing::generators::stacks::Stacks>::wasm::section::<wasm_encoder::core::exports::ExportSection> Line | Count | Source | 390 | 4.92k | fn section(module: &mut wasm_encoder::Module, section: impl wasm_encoder::Section) { | 391 | 4.92k | module.section(§ion); | 392 | 4.92k | } |
<wasmtime_fuzzing::generators::stacks::Stacks>::wasm::section::<wasm_encoder::core::globals::GlobalSection> Line | Count | Source | 390 | 4.92k | fn section(module: &mut wasm_encoder::Module, section: impl wasm_encoder::Section) { | 391 | 4.92k | module.section(§ion); | 392 | 4.92k | } |
<wasmtime_fuzzing::generators::stacks::Stacks>::wasm::section::<wasm_encoder::core::imports::ImportSection> Line | Count | Source | 390 | 4.92k | fn section(module: &mut wasm_encoder::Module, section: impl wasm_encoder::Section) { | 391 | 4.92k | module.section(§ion); | 392 | 4.92k | } |
<wasmtime_fuzzing::generators::stacks::Stacks>::wasm::section::<wasm_encoder::core::elements::ElementSection> Line | Count | Source | 390 | 4.92k | fn section(module: &mut wasm_encoder::Module, section: impl wasm_encoder::Section) { | 391 | 4.92k | module.section(§ion); | 392 | 4.92k | } |
<wasmtime_fuzzing::generators::stacks::Stacks>::wasm::section::<wasm_encoder::core::memories::MemorySection> Line | Count | Source | 390 | 4.92k | fn section(module: &mut wasm_encoder::Module, section: impl wasm_encoder::Section) { | 391 | 4.92k | module.section(§ion); | 392 | 4.92k | } |
<wasmtime_fuzzing::generators::stacks::Stacks>::wasm::section::<wasm_encoder::core::functions::FunctionSection> Line | Count | Source | 390 | 4.92k | fn section(module: &mut wasm_encoder::Module, section: impl wasm_encoder::Section) { | 391 | 4.92k | module.section(§ion); | 392 | 4.92k | } |
|
393 | | |
394 | | // Helper that defines a function body in the code section and takes |
395 | | // ownership of it so that it is dropped and its memory reclaimed after |
396 | | // adding it to the module. |
397 | 70.5k | fn function(code: &mut wasm_encoder::CodeSection, mut func: wasm_encoder::Function) { |
398 | 70.5k | func.instruction(&Instruction::End); |
399 | 70.5k | code.function(&func); |
400 | 70.5k | } |
401 | 4.92k | } |
402 | | } |
403 | | |
404 | | #[cfg(test)] |
405 | | mod tests { |
406 | | use super::*; |
407 | | use rand::prelude::*; |
408 | | use wasmparser::Validator; |
409 | | |
410 | | #[test] |
411 | | fn stacks_generates_valid_wasm_modules() { |
412 | | let mut rng = SmallRng::seed_from_u64(0); |
413 | | let mut buf = vec![0; 2048]; |
414 | | for _ in 0..1024 { |
415 | | rng.fill_bytes(&mut buf); |
416 | | let u = Unstructured::new(&buf); |
417 | | if let Ok(stacks) = Stacks::arbitrary_take_rest(u) { |
418 | | let wasm = stacks.wasm(); |
419 | | validate(&wasm); |
420 | | } |
421 | | } |
422 | | } |
423 | | |
424 | | fn validate(wasm: &[u8]) { |
425 | | let mut validator = Validator::new(); |
426 | | let err = match validator.validate_all(wasm) { |
427 | | Ok(_) => return, |
428 | | Err(e) => e, |
429 | | }; |
430 | | drop(std::fs::write("test.wasm", wasm)); |
431 | | if let Ok(text) = wasmprinter::print_bytes(wasm) { |
432 | | drop(std::fs::write("test.wat", &text)); |
433 | | } |
434 | | panic!("wasm failed to validate: {err}"); |
435 | | } |
436 | | } |