wasmtime_fuzzing/oracles/
diff_wasmtime.rs1use crate::generators::{self, CompilerStrategy, DiffValue, DiffValueType, WasmtimeConfig};
4use crate::oracles::dummy;
5use crate::oracles::engine::DiffInstance;
6use crate::oracles::{StoreLimits, compile_module, engine::DiffEngine};
7use crate::single_module_fuzzer::KnownValid;
8use arbitrary::Unstructured;
9use wasmtime::{
10 Error, Extern, FuncType, Instance, Module, Result, Store, Trap, Val, error::Context as _,
11};
12
13pub struct WasmtimeEngine {
15 config: generators::Config,
16}
17
18impl WasmtimeEngine {
19 pub fn new(
24 u: &mut Unstructured<'_>,
25 config: &mut generators::Config,
26 compiler_strategy: CompilerStrategy,
27 ) -> arbitrary::Result<Self> {
28 let mut new_config = u.arbitrary::<WasmtimeConfig>()?;
29 new_config.compiler_strategy = compiler_strategy;
30 new_config.update_module_config(&mut config.module_config, u)?;
31 new_config.make_compatible_with(&config.wasmtime);
32
33 let config = generators::Config {
34 wasmtime: new_config,
35 module_config: config.module_config.clone(),
36 };
37 log::debug!("Created new Wasmtime differential engine with config: {config:?}");
38
39 Ok(Self { config })
40 }
41}
42
43impl DiffEngine for WasmtimeEngine {
44 fn name(&self) -> &'static str {
45 match self.config.wasmtime.compiler_strategy {
46 CompilerStrategy::CraneliftNative => "wasmtime",
47 CompilerStrategy::Winch => "winch",
48 CompilerStrategy::CraneliftPulley => "pulley",
49 }
50 }
51
52 fn instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> {
53 let store = self.config.to_store();
54 let module = compile_module(store.engine(), wasm, KnownValid::Yes, &self.config).unwrap();
55 let instance = WasmtimeInstance::new(store, module)?;
56 Ok(Box::new(instance))
57 }
58
59 fn assert_error_match(&self, lhs: &Error, rhs: &Trap) {
60 let lhs = lhs
61 .downcast_ref::<Trap>()
62 .expect(&format!("not a trap: {lhs:?}"));
63
64 assert_eq!(lhs, rhs, "{lhs}\nis not equal to\n{rhs}");
65 }
66
67 fn is_non_deterministic_error(&self, err: &Error) -> bool {
68 super::wasmtime_error_is_non_deterministic(err)
69 }
70}
71
72pub struct WasmtimeInstance {
77 store: Store<StoreLimits>,
78 instance: Instance,
79}
80
81impl WasmtimeInstance {
82 pub fn new(mut store: Store<StoreLimits>, module: Module) -> Result<Self> {
84 let instance = dummy::dummy_linker(&mut store, &module)
85 .and_then(|l| l.instantiate(&mut store, &module))
86 .context("unable to instantiate module in wasmtime")?;
87 Ok(Self { store, instance })
88 }
89
90 pub fn exported_functions(&mut self) -> Vec<(String, FuncType)> {
97 let exported_functions = self
98 .instance
99 .exports(&mut self.store)
100 .map(|e| (e.name().to_owned(), e.into_func()))
101 .filter_map(|(n, f)| f.map(|f| (n, f)))
102 .collect::<Vec<_>>();
103 exported_functions
104 .into_iter()
105 .map(|(n, f)| (n, f.ty(&self.store)))
106 .collect()
107 }
108
109 pub fn exported_globals(&mut self) -> Vec<(String, DiffValueType)> {
111 let globals = self
112 .instance
113 .exports(&mut self.store)
114 .filter_map(|e| {
115 let name = e.name();
116 e.into_global().map(|g| (name.to_string(), g))
117 })
118 .collect::<Vec<_>>();
119
120 globals
121 .into_iter()
122 .filter_map(|(name, global)| {
123 DiffValueType::try_from(global.ty(&self.store).content().clone())
124 .map(|ty| (name, ty))
125 .ok()
126 })
127 .collect()
128 }
129
130 pub fn exported_memories(&mut self) -> Vec<(String, bool)> {
133 self.instance
134 .exports(&mut self.store)
135 .filter_map(|e| {
136 let name = e.name();
137 match e.into_extern() {
138 Extern::Memory(_) => Some((name.to_string(), false)),
139 Extern::SharedMemory(_) => Some((name.to_string(), true)),
140 _ => None,
141 }
142 })
143 .collect()
144 }
145
146 pub fn is_oom(&self) -> bool {
148 self.store.data().is_oom()
149 }
150}
151
152impl DiffInstance for WasmtimeInstance {
153 fn name(&self) -> &'static str {
154 "wasmtime"
155 }
156
157 fn evaluate(
158 &mut self,
159 function_name: &str,
160 arguments: &[DiffValue],
161 _results: &[DiffValueType],
162 ) -> Result<Option<Vec<DiffValue>>> {
163 let arguments: Vec<_> = arguments.iter().map(Val::from).collect();
164
165 let function = self
166 .instance
167 .get_func(&mut self.store, function_name)
168 .expect("unable to access exported function");
169 let ty = function.ty(&self.store);
170 let mut results = vec![Val::I32(0); ty.results().len()];
171 function.call(&mut self.store, &arguments, &mut results)?;
172
173 let results = results.into_iter().map(Val::into).collect();
174 Ok(Some(results))
175 }
176
177 fn get_global(&mut self, name: &str, _ty: DiffValueType) -> Option<DiffValue> {
178 Some(
179 self.instance
180 .get_global(&mut self.store, name)
181 .unwrap()
182 .get(&mut self.store)
183 .into(),
184 )
185 }
186
187 fn get_memory(&mut self, name: &str, shared: bool) -> Option<Vec<u8>> {
188 Some(if shared {
189 let memory = self
190 .instance
191 .get_shared_memory(&mut self.store, name)
192 .unwrap();
193 memory.data().iter().map(|i| unsafe { *i.get() }).collect()
194 } else {
195 self.instance
196 .get_memory(&mut self.store, name)
197 .unwrap()
198 .data(&self.store)
199 .to_vec()
200 })
201 }
202}
203
204impl From<&DiffValue> for Val {
205 fn from(v: &DiffValue) -> Self {
206 match *v {
207 DiffValue::I32(n) => Val::I32(n),
208 DiffValue::I64(n) => Val::I64(n),
209 DiffValue::F32(n) => Val::F32(n),
210 DiffValue::F64(n) => Val::F64(n),
211 DiffValue::V128(n) => Val::V128(n.into()),
212 DiffValue::FuncRef { null } => {
213 assert!(null);
214 Val::FuncRef(None)
215 }
216 DiffValue::ExternRef { null } => {
217 assert!(null);
218 Val::ExternRef(None)
219 }
220 DiffValue::AnyRef { null } => {
221 assert!(null);
222 Val::AnyRef(None)
223 }
224 DiffValue::ExnRef { null } => {
225 assert!(null);
226 Val::ExnRef(None)
227 }
228 DiffValue::ContRef { null } => {
229 assert!(null);
230 Val::ExnRef(None)
231 }
232 }
233 }
234}
235
236impl From<Val> for DiffValue {
237 fn from(val: Val) -> DiffValue {
238 match val {
239 Val::I32(n) => DiffValue::I32(n),
240 Val::I64(n) => DiffValue::I64(n),
241 Val::F32(n) => DiffValue::F32(n),
242 Val::F64(n) => DiffValue::F64(n),
243 Val::V128(n) => DiffValue::V128(n.into()),
244 Val::ExternRef(r) => DiffValue::ExternRef { null: r.is_none() },
245 Val::FuncRef(r) => DiffValue::FuncRef { null: r.is_none() },
246 Val::AnyRef(r) => DiffValue::AnyRef { null: r.is_none() },
247 Val::ExnRef(e) => DiffValue::ExnRef { null: e.is_none() },
248 Val::ContRef(c) => DiffValue::ContRef { null: c.is_none() },
249 }
250 }
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256
257 #[test]
258 fn smoke_cranelift_native() {
259 if !CompilerStrategy::CRANELIFT_NATIVE_SUPPORTED {
260 return;
261 }
262 crate::oracles::engine::smoke_test_engine(|u, config| {
263 WasmtimeEngine::new(u, config, CompilerStrategy::CraneliftNative)
264 })
265 }
266
267 #[test]
268 fn smoke_cranelift_pulley() {
269 crate::oracles::engine::smoke_test_engine(|u, config| {
270 WasmtimeEngine::new(u, config, CompilerStrategy::CraneliftPulley)
271 })
272 }
273
274 #[test]
275 fn smoke_winch() {
276 if !CompilerStrategy::WINCH_SUPPORTED {
277 return;
278 }
279 crate::oracles::engine::smoke_test_engine(|u, config| {
280 WasmtimeEngine::new(u, config, CompilerStrategy::Winch)
281 })
282 }
283}