1 //! Evaluate an exported Wasm function using Wasmtime. 2 3 use crate::generators::{self, DiffValue, DiffValueType, WasmtimeConfig}; 4 use crate::oracles::dummy; 5 use crate::oracles::engine::DiffInstance; 6 use crate::oracles::{compile_module, engine::DiffEngine, StoreLimits}; 7 use anyhow::{Context, Error, Result}; 8 use arbitrary::Unstructured; 9 use wasmtime::{Extern, FuncType, Instance, Module, Store, Trap, TrapCode, Val}; 10 11 /// A wrapper for using Wasmtime as a [`DiffEngine`]. 12 pub struct WasmtimeEngine { 13 config: generators::Config, 14 } 15 16 impl WasmtimeEngine { 17 /// Merely store the configuration; the engine is actually constructed 18 /// later. Ideally the store and engine could be built here but 19 /// `compile_module` takes a [`generators::Config`]; TODO re-factor this if 20 /// that ever changes. 21 pub fn new(u: &mut Unstructured<'_>, config: &generators::Config) -> arbitrary::Result<Self> { 22 let mut new_config = u.arbitrary::<WasmtimeConfig>()?; 23 new_config.make_compatible_with(&config.wasmtime); 24 let config = generators::Config { 25 wasmtime: new_config, 26 module_config: config.module_config.clone(), 27 }; 28 Ok(Self { config }) 29 } 30 } 31 32 impl DiffEngine for WasmtimeEngine { 33 fn name(&self) -> &'static str { 34 "wasmtime" 35 } 36 37 fn instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> { 38 let store = self.config.to_store(); 39 let module = compile_module(store.engine(), wasm, true, &self.config).unwrap(); 40 let instance = WasmtimeInstance::new(store, module)?; 41 Ok(Box::new(instance)) 42 } 43 44 fn assert_error_match(&self, trap: &Trap, err: &Error) { 45 let trap2 = err 46 .downcast_ref::<Trap>() 47 .expect(&format!("not a trap: {:?}", err)); 48 assert_eq!( 49 trap.trap_code(), 50 trap2.trap_code(), 51 "{}\nis not equal to\n{}", 52 trap, 53 trap2 54 ); 55 } 56 57 fn is_stack_overflow(&self, err: &Error) -> bool { 58 match err.downcast_ref::<Trap>() { 59 Some(trap) => trap.trap_code() == Some(TrapCode::StackOverflow), 60 None => false, 61 } 62 } 63 } 64 65 /// A wrapper around a Wasmtime instance. 66 /// 67 /// The Wasmtime engine constructs a new store and compiles an instance of a 68 /// Wasm module. 69 pub struct WasmtimeInstance { 70 store: Store<StoreLimits>, 71 instance: Instance, 72 } 73 74 impl WasmtimeInstance { 75 /// Instantiate a new Wasmtime instance. 76 pub fn new(mut store: Store<StoreLimits>, module: Module) -> Result<Self> { 77 let instance = dummy::dummy_linker(&mut store, &module) 78 .and_then(|l| l.instantiate(&mut store, &module)) 79 .context("unable to instantiate module in wasmtime")?; 80 Ok(Self { store, instance }) 81 } 82 83 /// Retrieve the names and types of all exported functions in the instance. 84 /// 85 /// This is useful for evaluating each exported function with different 86 /// values. The [`DiffInstance`] trait asks for the function name and we 87 /// need to know the function signature in order to pass in the right 88 /// arguments. 89 pub fn exported_functions(&mut self) -> Vec<(String, FuncType)> { 90 let exported_functions = self 91 .instance 92 .exports(&mut self.store) 93 .map(|e| (e.name().to_owned(), e.into_func())) 94 .filter_map(|(n, f)| f.map(|f| (n, f))) 95 .collect::<Vec<_>>(); 96 exported_functions 97 .into_iter() 98 .map(|(n, f)| (n, f.ty(&self.store))) 99 .collect() 100 } 101 102 /// Returns the list of globals and their types exported from this instance. 103 pub fn exported_globals(&mut self) -> Vec<(String, DiffValueType)> { 104 let globals = self 105 .instance 106 .exports(&mut self.store) 107 .filter_map(|e| { 108 let name = e.name(); 109 e.into_global().map(|g| (name.to_string(), g)) 110 }) 111 .collect::<Vec<_>>(); 112 113 globals 114 .into_iter() 115 .map(|(name, global)| { 116 ( 117 name, 118 global.ty(&self.store).content().clone().try_into().unwrap(), 119 ) 120 }) 121 .collect() 122 } 123 124 /// Returns the list of exported memories and whether or not it's a shared 125 /// memory. 126 pub fn exported_memories(&mut self) -> Vec<(String, bool)> { 127 self.instance 128 .exports(&mut self.store) 129 .filter_map(|e| { 130 let name = e.name(); 131 match e.into_extern() { 132 Extern::Memory(_) => Some((name.to_string(), false)), 133 Extern::SharedMemory(_) => Some((name.to_string(), true)), 134 _ => None, 135 } 136 }) 137 .collect() 138 } 139 } 140 141 impl DiffInstance for WasmtimeInstance { 142 fn name(&self) -> &'static str { 143 "wasmtime" 144 } 145 146 fn evaluate( 147 &mut self, 148 function_name: &str, 149 arguments: &[DiffValue], 150 _results: &[DiffValueType], 151 ) -> Result<Option<Vec<DiffValue>>> { 152 let arguments: Vec<_> = arguments.iter().map(Val::from).collect(); 153 154 let function = self 155 .instance 156 .get_func(&mut self.store, function_name) 157 .expect("unable to access exported function"); 158 let ty = function.ty(&self.store); 159 let mut results = vec![Val::I32(0); ty.results().len()]; 160 function.call(&mut self.store, &arguments, &mut results)?; 161 162 let results = results.into_iter().map(Val::into).collect(); 163 Ok(Some(results)) 164 } 165 166 fn get_global(&mut self, name: &str, _ty: DiffValueType) -> Option<DiffValue> { 167 Some( 168 self.instance 169 .get_global(&mut self.store, name) 170 .unwrap() 171 .get(&mut self.store) 172 .into(), 173 ) 174 } 175 176 fn get_memory(&mut self, name: &str, shared: bool) -> Option<Vec<u8>> { 177 Some(if shared { 178 let data = self 179 .instance 180 .get_shared_memory(&mut self.store, name) 181 .unwrap() 182 .data(); 183 unsafe { (*data).to_vec() } 184 } else { 185 self.instance 186 .get_memory(&mut self.store, name) 187 .unwrap() 188 .data(&self.store) 189 .to_vec() 190 }) 191 } 192 } 193 194 impl From<&DiffValue> for Val { 195 fn from(v: &DiffValue) -> Self { 196 match *v { 197 DiffValue::I32(n) => Val::I32(n), 198 DiffValue::I64(n) => Val::I64(n), 199 DiffValue::F32(n) => Val::F32(n), 200 DiffValue::F64(n) => Val::F64(n), 201 DiffValue::V128(n) => Val::V128(n), 202 DiffValue::FuncRef { null } => { 203 assert!(null); 204 Val::FuncRef(None) 205 } 206 DiffValue::ExternRef { null } => { 207 assert!(null); 208 Val::ExternRef(None) 209 } 210 } 211 } 212 } 213 214 impl Into<DiffValue> for Val { 215 fn into(self) -> DiffValue { 216 match self { 217 Val::I32(n) => DiffValue::I32(n), 218 Val::I64(n) => DiffValue::I64(n), 219 Val::F32(n) => DiffValue::F32(n), 220 Val::F64(n) => DiffValue::F64(n), 221 Val::V128(n) => DiffValue::V128(n), 222 Val::FuncRef(f) => DiffValue::FuncRef { null: f.is_none() }, 223 Val::ExternRef(e) => DiffValue::ExternRef { null: e.is_none() }, 224 } 225 } 226 } 227 228 #[test] 229 fn smoke() { 230 crate::oracles::engine::smoke_test_engine(|u, config| WasmtimeEngine::new(u, config)) 231 } 232