15ec92d59SAndrew Brown //! Evaluate an exported Wasm function using Wasmtime.
25ec92d59SAndrew Brown 
3353dc273SAlex Crichton use crate::generators::{self, CompilerStrategy, DiffValue, DiffValueType, WasmtimeConfig};
4fd98814bSAlex Crichton use crate::oracles::dummy;
55ec92d59SAndrew Brown use crate::oracles::engine::DiffInstance;
690ac295eSAlex Crichton use crate::oracles::{StoreLimits, compile_module, engine::DiffEngine};
704c03b31SAlex Crichton use crate::single_module_fuzzer::KnownValid;
8543a4879SAlex Crichton use arbitrary::Unstructured;
993d22fcdSNick Fitzgerald use wasmtime::{
1093d22fcdSNick Fitzgerald     Error, Extern, FuncType, Instance, Module, Result, Store, Trap, Val, error::Context as _,
1193d22fcdSNick Fitzgerald };
125ec92d59SAndrew Brown 
135ec92d59SAndrew Brown /// A wrapper for using Wasmtime as a [`DiffEngine`].
145ec92d59SAndrew Brown pub struct WasmtimeEngine {
15543a4879SAlex Crichton     config: generators::Config,
165ec92d59SAndrew Brown }
175ec92d59SAndrew Brown 
185ec92d59SAndrew Brown impl WasmtimeEngine {
195ec92d59SAndrew Brown     /// Merely store the configuration; the engine is actually constructed
205ec92d59SAndrew Brown     /// later. Ideally the store and engine could be built here but
215ec92d59SAndrew Brown     /// `compile_module` takes a [`generators::Config`]; TODO re-factor this if
225ec92d59SAndrew Brown     /// that ever changes.
new( u: &mut Unstructured<'_>, config: &mut generators::Config, compiler_strategy: CompilerStrategy, ) -> arbitrary::Result<Self>23353dc273SAlex Crichton     pub fn new(
24353dc273SAlex Crichton         u: &mut Unstructured<'_>,
25353dc273SAlex Crichton         config: &mut generators::Config,
26353dc273SAlex Crichton         compiler_strategy: CompilerStrategy,
27353dc273SAlex Crichton     ) -> arbitrary::Result<Self> {
28543a4879SAlex Crichton         let mut new_config = u.arbitrary::<WasmtimeConfig>()?;
29353dc273SAlex Crichton         new_config.compiler_strategy = compiler_strategy;
305b9e8765SNick Fitzgerald         new_config.update_module_config(&mut config.module_config, u)?;
31543a4879SAlex Crichton         new_config.make_compatible_with(&config.wasmtime);
32353dc273SAlex Crichton 
33543a4879SAlex Crichton         let config = generators::Config {
34543a4879SAlex Crichton             wasmtime: new_config,
35543a4879SAlex Crichton             module_config: config.module_config.clone(),
36543a4879SAlex Crichton         };
375b9e8765SNick Fitzgerald         log::debug!("Created new Wasmtime differential engine with config: {config:?}");
385b9e8765SNick Fitzgerald 
39fd98814bSAlex Crichton         Ok(Self { config })
405ec92d59SAndrew Brown     }
415ec92d59SAndrew Brown }
425ec92d59SAndrew Brown 
435ec92d59SAndrew Brown impl DiffEngine for WasmtimeEngine {
name(&self) -> &'static str445ec92d59SAndrew Brown     fn name(&self) -> &'static str {
45353dc273SAlex Crichton         match self.config.wasmtime.compiler_strategy {
46c78d44efSAlex Crichton             CompilerStrategy::CraneliftNative => "wasmtime",
47353dc273SAlex Crichton             CompilerStrategy::Winch => "winch",
48c78d44efSAlex Crichton             CompilerStrategy::CraneliftPulley => "pulley",
49353dc273SAlex Crichton         }
505ec92d59SAndrew Brown     }
515ec92d59SAndrew Brown 
instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>>52fd98814bSAlex Crichton     fn instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> {
535ec92d59SAndrew Brown         let store = self.config.to_store();
5404c03b31SAlex Crichton         let module = compile_module(store.engine(), wasm, KnownValid::Yes, &self.config).unwrap();
555ec92d59SAndrew Brown         let instance = WasmtimeInstance::new(store, module)?;
565ec92d59SAndrew Brown         Ok(Box::new(instance))
575ec92d59SAndrew Brown     }
58fd98814bSAlex Crichton 
assert_error_match(&self, lhs: &Error, rhs: &Trap)59b3b50943SAlex Crichton     fn assert_error_match(&self, lhs: &Error, rhs: &Trap) {
60b3b50943SAlex Crichton         let lhs = lhs
6110dbb199SAlex Crichton             .downcast_ref::<Trap>()
62b3b50943SAlex Crichton             .expect(&format!("not a trap: {lhs:?}"));
635b9e8765SNick Fitzgerald 
64b3b50943SAlex Crichton         assert_eq!(lhs, rhs, "{lhs}\nis not equal to\n{rhs}");
65fd98814bSAlex Crichton     }
6610dbb199SAlex Crichton 
is_non_deterministic_error(&self, err: &Error) -> bool675b9e8765SNick Fitzgerald     fn is_non_deterministic_error(&self, err: &Error) -> bool {
6810dbb199SAlex Crichton         match err.downcast_ref::<Trap>() {
695b9e8765SNick Fitzgerald             Some(trap) => super::wasmtime_trap_is_non_deterministic(trap),
7010dbb199SAlex Crichton             None => false,
7110dbb199SAlex Crichton         }
7210dbb199SAlex Crichton     }
735ec92d59SAndrew Brown }
745ec92d59SAndrew Brown 
755ec92d59SAndrew Brown /// A wrapper around a Wasmtime instance.
765ec92d59SAndrew Brown ///
775ec92d59SAndrew Brown /// The Wasmtime engine constructs a new store and compiles an instance of a
785ec92d59SAndrew Brown /// Wasm module.
795ec92d59SAndrew Brown pub struct WasmtimeInstance {
805ec92d59SAndrew Brown     store: Store<StoreLimits>,
815ec92d59SAndrew Brown     instance: Instance,
825ec92d59SAndrew Brown }
835ec92d59SAndrew Brown 
845ec92d59SAndrew Brown impl WasmtimeInstance {
855ec92d59SAndrew Brown     /// Instantiate a new Wasmtime instance.
new(mut store: Store<StoreLimits>, module: Module) -> Result<Self>865ec92d59SAndrew Brown     pub fn new(mut store: Store<StoreLimits>, module: Module) -> Result<Self> {
87fd98814bSAlex Crichton         let instance = dummy::dummy_linker(&mut store, &module)
88fd98814bSAlex Crichton             .and_then(|l| l.instantiate(&mut store, &module))
895ec92d59SAndrew Brown             .context("unable to instantiate module in wasmtime")?;
905ec92d59SAndrew Brown         Ok(Self { store, instance })
915ec92d59SAndrew Brown     }
925ec92d59SAndrew Brown 
935ec92d59SAndrew Brown     /// Retrieve the names and types of all exported functions in the instance.
945ec92d59SAndrew Brown     ///
955ec92d59SAndrew Brown     /// This is useful for evaluating each exported function with different
965ec92d59SAndrew Brown     /// values. The [`DiffInstance`] trait asks for the function name and we
975ec92d59SAndrew Brown     /// need to know the function signature in order to pass in the right
985ec92d59SAndrew Brown     /// arguments.
exported_functions(&mut self) -> Vec<(String, FuncType)>995ec92d59SAndrew Brown     pub fn exported_functions(&mut self) -> Vec<(String, FuncType)> {
1005ec92d59SAndrew Brown         let exported_functions = self
1015ec92d59SAndrew Brown             .instance
1025ec92d59SAndrew Brown             .exports(&mut self.store)
1035ec92d59SAndrew Brown             .map(|e| (e.name().to_owned(), e.into_func()))
1045ec92d59SAndrew Brown             .filter_map(|(n, f)| f.map(|f| (n, f)))
1055ec92d59SAndrew Brown             .collect::<Vec<_>>();
1065ec92d59SAndrew Brown         exported_functions
1075ec92d59SAndrew Brown             .into_iter()
1085ec92d59SAndrew Brown             .map(|(n, f)| (n, f.ty(&self.store)))
1095ec92d59SAndrew Brown             .collect()
1105ec92d59SAndrew Brown     }
111fd98814bSAlex Crichton 
112fd98814bSAlex Crichton     /// Returns the list of globals and their types exported from this instance.
exported_globals(&mut self) -> Vec<(String, DiffValueType)>113fd98814bSAlex Crichton     pub fn exported_globals(&mut self) -> Vec<(String, DiffValueType)> {
114fd98814bSAlex Crichton         let globals = self
115fd98814bSAlex Crichton             .instance
116fd98814bSAlex Crichton             .exports(&mut self.store)
117fd98814bSAlex Crichton             .filter_map(|e| {
118fd98814bSAlex Crichton                 let name = e.name();
119fd98814bSAlex Crichton                 e.into_global().map(|g| (name.to_string(), g))
120fd98814bSAlex Crichton             })
121fd98814bSAlex Crichton             .collect::<Vec<_>>();
122fd98814bSAlex Crichton 
123fd98814bSAlex Crichton         globals
124fd98814bSAlex Crichton             .into_iter()
1255b9e8765SNick Fitzgerald             .filter_map(|(name, global)| {
1265b9e8765SNick Fitzgerald                 DiffValueType::try_from(global.ty(&self.store).content().clone())
1275b9e8765SNick Fitzgerald                     .map(|ty| (name, ty))
1285b9e8765SNick Fitzgerald                     .ok()
129fd98814bSAlex Crichton             })
130fd98814bSAlex Crichton             .collect()
131fd98814bSAlex Crichton     }
132fd98814bSAlex Crichton 
133fd98814bSAlex Crichton     /// Returns the list of exported memories and whether or not it's a shared
134fd98814bSAlex Crichton     /// memory.
exported_memories(&mut self) -> Vec<(String, bool)>135fd98814bSAlex Crichton     pub fn exported_memories(&mut self) -> Vec<(String, bool)> {
136fd98814bSAlex Crichton         self.instance
137fd98814bSAlex Crichton             .exports(&mut self.store)
138fd98814bSAlex Crichton             .filter_map(|e| {
139fd98814bSAlex Crichton                 let name = e.name();
140fd98814bSAlex Crichton                 match e.into_extern() {
141fd98814bSAlex Crichton                     Extern::Memory(_) => Some((name.to_string(), false)),
142fd98814bSAlex Crichton                     Extern::SharedMemory(_) => Some((name.to_string(), true)),
143fd98814bSAlex Crichton                     _ => None,
144fd98814bSAlex Crichton                 }
145fd98814bSAlex Crichton             })
146fd98814bSAlex Crichton             .collect()
147fd98814bSAlex Crichton     }
1484b703f9dSAlex Crichton 
1494b703f9dSAlex Crichton     /// Returns whether or not this instance has hit its OOM condition yet.
is_oom(&self) -> bool1504b703f9dSAlex Crichton     pub fn is_oom(&self) -> bool {
1514b703f9dSAlex Crichton         self.store.data().is_oom()
1524b703f9dSAlex Crichton     }
1535ec92d59SAndrew Brown }
1545ec92d59SAndrew Brown 
1555ec92d59SAndrew Brown impl DiffInstance for WasmtimeInstance {
name(&self) -> &'static str1565ec92d59SAndrew Brown     fn name(&self) -> &'static str {
1575ec92d59SAndrew Brown         "wasmtime"
1585ec92d59SAndrew Brown     }
1595ec92d59SAndrew Brown 
evaluate( &mut self, function_name: &str, arguments: &[DiffValue], _results: &[DiffValueType], ) -> Result<Option<Vec<DiffValue>>>160fd98814bSAlex Crichton     fn evaluate(
161fd98814bSAlex Crichton         &mut self,
162fd98814bSAlex Crichton         function_name: &str,
163fd98814bSAlex Crichton         arguments: &[DiffValue],
164fd98814bSAlex Crichton         _results: &[DiffValueType],
165fd98814bSAlex Crichton     ) -> Result<Option<Vec<DiffValue>>> {
1665ec92d59SAndrew Brown         let arguments: Vec<_> = arguments.iter().map(Val::from).collect();
1675ec92d59SAndrew Brown 
1685ec92d59SAndrew Brown         let function = self
1695ec92d59SAndrew Brown             .instance
1705ec92d59SAndrew Brown             .get_func(&mut self.store, function_name)
1715ec92d59SAndrew Brown             .expect("unable to access exported function");
1725ec92d59SAndrew Brown         let ty = function.ty(&self.store);
1735ec92d59SAndrew Brown         let mut results = vec![Val::I32(0); ty.results().len()];
1745ec92d59SAndrew Brown         function.call(&mut self.store, &arguments, &mut results)?;
1755ec92d59SAndrew Brown 
1765ec92d59SAndrew Brown         let results = results.into_iter().map(Val::into).collect();
177fd98814bSAlex Crichton         Ok(Some(results))
1785ec92d59SAndrew Brown     }
1795ec92d59SAndrew Brown 
get_global(&mut self, name: &str, _ty: DiffValueType) -> Option<DiffValue>180fd98814bSAlex Crichton     fn get_global(&mut self, name: &str, _ty: DiffValueType) -> Option<DiffValue> {
181fd98814bSAlex Crichton         Some(
182fd98814bSAlex Crichton             self.instance
183fd98814bSAlex Crichton                 .get_global(&mut self.store, name)
184fd98814bSAlex Crichton                 .unwrap()
185fd98814bSAlex Crichton                 .get(&mut self.store)
186fd98814bSAlex Crichton                 .into(),
187fd98814bSAlex Crichton         )
1885ec92d59SAndrew Brown     }
1895ec92d59SAndrew Brown 
get_memory(&mut self, name: &str, shared: bool) -> Option<Vec<u8>>190fd98814bSAlex Crichton     fn get_memory(&mut self, name: &str, shared: bool) -> Option<Vec<u8>> {
191fd98814bSAlex Crichton         Some(if shared {
1922be457c2SAlex Crichton             let memory = self
1935ec92d59SAndrew Brown                 .instance
194fd98814bSAlex Crichton                 .get_shared_memory(&mut self.store, name)
1952be457c2SAlex Crichton                 .unwrap();
1962be457c2SAlex Crichton             memory.data().iter().map(|i| unsafe { *i.get() }).collect()
197fd98814bSAlex Crichton         } else {
198fd98814bSAlex Crichton             self.instance
199fd98814bSAlex Crichton                 .get_memory(&mut self.store, name)
200fd98814bSAlex Crichton                 .unwrap()
201fd98814bSAlex Crichton                 .data(&self.store)
202fd98814bSAlex Crichton                 .to_vec()
203fd98814bSAlex Crichton         })
2045ec92d59SAndrew Brown     }
2055ec92d59SAndrew Brown }
2065ec92d59SAndrew Brown 
2075ec92d59SAndrew Brown impl From<&DiffValue> for Val {
from(v: &DiffValue) -> Self2085ec92d59SAndrew Brown     fn from(v: &DiffValue) -> Self {
2095ec92d59SAndrew Brown         match *v {
2105ec92d59SAndrew Brown             DiffValue::I32(n) => Val::I32(n),
2115ec92d59SAndrew Brown             DiffValue::I64(n) => Val::I64(n),
2125ec92d59SAndrew Brown             DiffValue::F32(n) => Val::F32(n),
2135ec92d59SAndrew Brown             DiffValue::F64(n) => Val::F64(n),
2148d7a2b89SAlex Crichton             DiffValue::V128(n) => Val::V128(n.into()),
215fd98814bSAlex Crichton             DiffValue::FuncRef { null } => {
216fd98814bSAlex Crichton                 assert!(null);
217fd98814bSAlex Crichton                 Val::FuncRef(None)
218fd98814bSAlex Crichton             }
219fd98814bSAlex Crichton             DiffValue::ExternRef { null } => {
220fd98814bSAlex Crichton                 assert!(null);
221fd98814bSAlex Crichton                 Val::ExternRef(None)
222fd98814bSAlex Crichton             }
2230fa13013SNick Fitzgerald             DiffValue::AnyRef { null } => {
2240fa13013SNick Fitzgerald                 assert!(null);
2250fa13013SNick Fitzgerald                 Val::AnyRef(None)
2260fa13013SNick Fitzgerald             }
227eaa4632eSChris Fallin             DiffValue::ExnRef { null } => {
228eaa4632eSChris Fallin                 assert!(null);
229eaa4632eSChris Fallin                 Val::ExnRef(None)
230eaa4632eSChris Fallin             }
231a631d20aSPaul Osborne             DiffValue::ContRef { null } => {
232a631d20aSPaul Osborne                 assert!(null);
233a631d20aSPaul Osborne                 Val::ExnRef(None)
234a631d20aSPaul Osborne             }
2355ec92d59SAndrew Brown         }
2365ec92d59SAndrew Brown     }
2375ec92d59SAndrew Brown }
2385ec92d59SAndrew Brown 
239d3e7548eSAlex Crichton impl From<Val> for DiffValue {
from(val: Val) -> DiffValue240d3e7548eSAlex Crichton     fn from(val: Val) -> DiffValue {
241d3e7548eSAlex Crichton         match val {
2425ec92d59SAndrew Brown             Val::I32(n) => DiffValue::I32(n),
2435ec92d59SAndrew Brown             Val::I64(n) => DiffValue::I64(n),
2445ec92d59SAndrew Brown             Val::F32(n) => DiffValue::F32(n),
2455ec92d59SAndrew Brown             Val::F64(n) => DiffValue::F64(n),
2468d7a2b89SAlex Crichton             Val::V128(n) => DiffValue::V128(n.into()),
247ff93bce0SNick Fitzgerald             Val::ExternRef(r) => DiffValue::ExternRef { null: r.is_none() },
248ff93bce0SNick Fitzgerald             Val::FuncRef(r) => DiffValue::FuncRef { null: r.is_none() },
2490fa13013SNick Fitzgerald             Val::AnyRef(r) => DiffValue::AnyRef { null: r.is_none() },
250eaa4632eSChris Fallin             Val::ExnRef(e) => DiffValue::ExnRef { null: e.is_none() },
251a631d20aSPaul Osborne             Val::ContRef(c) => DiffValue::ContRef { null: c.is_none() },
2525ec92d59SAndrew Brown         }
2535ec92d59SAndrew Brown     }
2545ec92d59SAndrew Brown }
255fd98814bSAlex Crichton 
256ec3b2d22SNick Fitzgerald #[cfg(test)]
257ec3b2d22SNick Fitzgerald mod tests {
258ec3b2d22SNick Fitzgerald     use super::*;
259ec3b2d22SNick Fitzgerald 
260fd98814bSAlex Crichton     #[test]
smoke_cranelift_native()261c78d44efSAlex Crichton     fn smoke_cranelift_native() {
262353dc273SAlex Crichton         crate::oracles::engine::smoke_test_engine(|u, config| {
263c78d44efSAlex Crichton             WasmtimeEngine::new(u, config, CompilerStrategy::CraneliftNative)
264c78d44efSAlex Crichton         })
265c78d44efSAlex Crichton     }
266c78d44efSAlex Crichton 
267c78d44efSAlex Crichton     #[test]
smoke_cranelift_pulley()268c78d44efSAlex Crichton     fn smoke_cranelift_pulley() {
269c78d44efSAlex Crichton         crate::oracles::engine::smoke_test_engine(|u, config| {
270c78d44efSAlex Crichton             WasmtimeEngine::new(u, config, CompilerStrategy::CraneliftPulley)
271353dc273SAlex Crichton         })
272353dc273SAlex Crichton     }
273353dc273SAlex Crichton 
274353dc273SAlex Crichton     #[test]
smoke_winch()275353dc273SAlex Crichton     fn smoke_winch() {
276*332b4ce6SSaúl Cabrera         if !cfg!(target_arch = "x86_64") || !cfg!(target_arch = "aarch64") {
277353dc273SAlex Crichton             return;
278353dc273SAlex Crichton         }
279353dc273SAlex Crichton         crate::oracles::engine::smoke_test_engine(|u, config| {
280353dc273SAlex Crichton             WasmtimeEngine::new(u, config, CompilerStrategy::Winch)
281353dc273SAlex Crichton         })
282fd98814bSAlex Crichton     }
283ec3b2d22SNick Fitzgerald }
284