wasmtime_fuzzing/oracles/
stacks.rs1use crate::generators::Stacks;
2use anyhow::bail;
3use wasmtime::*;
4
5pub fn check_stacks(stacks: Stacks) -> usize {
10 let wasm = stacks.wasm();
11 crate::oracles::log_wasm(&wasm);
12
13 let engine = Engine::default();
14 let module = Module::new(&engine, &wasm).expect("should compile okay");
15
16 let mut linker = Linker::new(&engine);
17 linker
18 .func_wrap(
19 "host",
20 "check_stack",
21 |mut caller: Caller<'_, ()>| -> Result<()> {
22 let fuel = caller
23 .get_export("fuel")
24 .expect("should export `fuel`")
25 .into_global()
26 .expect("`fuel` export should be a global");
27
28 let fuel_left = fuel.get(&mut caller).unwrap_i32();
29 if fuel_left == 0 {
30 bail!(Trap::OutOfFuel);
31 }
32
33 fuel.set(&mut caller, Val::I32(fuel_left - 1)).unwrap();
34 Ok(())
35 },
36 )
37 .unwrap()
38 .func_wrap(
39 "host",
40 "call_func",
41 |mut caller: Caller<'_, ()>, f: Option<Func>| {
42 let f = f.unwrap();
43 let ty = f.ty(&caller);
44 let params = vec![Val::I32(0); ty.params().len()];
45 let mut results = vec![Val::I32(0); ty.results().len()];
46 f.call(&mut caller, ¶ms, &mut results)?;
47 Ok(())
48 },
49 )
50 .unwrap();
51
52 let mut store = Store::new(&engine, ());
53
54 let instance = linker
55 .instantiate(&mut store, &module)
56 .expect("should instantiate okay");
57
58 let run = instance
59 .get_typed_func::<(u32,), ()>(&mut store, "run")
60 .expect("should export `run` function");
61
62 let mut max_stack_depth = 0;
63 for input in stacks.inputs().iter().copied() {
64 log::debug!("input: {}", input);
65 if let Err(trap) = run.call(&mut store, (input.into(),)) {
66 log::debug!("trap: {:?}", trap);
67 let get_stack = instance
68 .get_typed_func::<(), (u32, u32)>(&mut store, "get_stack")
69 .expect("should export `get_stack` function as expected");
70
71 let (ptr, len) = get_stack
72 .call(&mut store, ())
73 .expect("`get_stack` should not trap");
74
75 let memory = instance
76 .get_memory(&mut store, "memory")
77 .expect("should have `memory` export");
78
79 let host_trace = trap.downcast_ref::<WasmBacktrace>().unwrap().frames();
80 let trap = trap.downcast_ref::<Trap>().unwrap();
81 max_stack_depth = max_stack_depth.max(host_trace.len());
82 assert_stack_matches(&mut store, memory, ptr, len, host_trace, *trap);
83 }
84 }
85 max_stack_depth
86}
87
88fn assert_stack_matches(
90 store: &mut impl AsContextMut,
91 memory: Memory,
92 ptr: u32,
93 len: u32,
94 host_trace: &[FrameInfo],
95 trap: Trap,
96) {
97 let mut data = vec![0; len as usize];
98 memory
99 .read(&mut *store, ptr as usize, &mut data)
100 .expect("should be in bounds");
101
102 let mut wasm_trace = vec![];
103 for entry in data.chunks(4).rev() {
104 let mut bytes = [0; 4];
105 bytes.copy_from_slice(entry);
106 let entry = u32::from_le_bytes(bytes);
107 wasm_trace.push(entry);
108 }
109
110 let host_trace = if trap == Trap::StackOverflow {
117 assert_eq!(host_trace.len(), wasm_trace.len() + 1);
118 &host_trace[1..]
119 } else {
120 host_trace
121 };
122
123 log::debug!("Wasm thinks its stack is: {:?}", wasm_trace);
124 log::debug!(
125 "Host thinks the stack is: {:?}",
126 host_trace
127 .iter()
128 .map(|f| f.func_index())
129 .collect::<Vec<_>>()
130 );
131
132 assert_eq!(wasm_trace.len(), host_trace.len());
133 for (wasm_entry, host_entry) in wasm_trace.into_iter().zip(host_trace) {
134 assert_eq!(wasm_entry, host_entry.func_index());
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use arbitrary::{Arbitrary, Unstructured};
142 use rand::prelude::*;
143
144 const TARGET_STACK_DEPTH: usize = 10;
145
146 #[test]
147 fn smoke_test() {
148 let mut rng = SmallRng::seed_from_u64(0);
149 let mut buf = vec![0; 2048];
150
151 for _ in 0..1024 {
152 rng.fill_bytes(&mut buf);
153 let u = Unstructured::new(&buf);
154 if let Ok(stacks) = Stacks::arbitrary_take_rest(u) {
155 let max_stack_depth = check_stacks(stacks);
156 if max_stack_depth >= TARGET_STACK_DEPTH {
157 return;
158 }
159 }
160 }
161
162 panic!(
163 "never generated a `Stacks` test case that reached {TARGET_STACK_DEPTH} \
164 deep stack frames",
165 );
166 }
167}