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