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, 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!(trap, trap2, "{}\nis not equal to\n{}", trap, trap2); 49 } 50 51 fn is_stack_overflow(&self, err: &Error) -> bool { 52 match err.downcast_ref::<Trap>() { 53 Some(trap) => *trap == Trap::StackOverflow, 54 None => false, 55 } 56 } 57 } 58 59 /// A wrapper around a Wasmtime instance. 60 /// 61 /// The Wasmtime engine constructs a new store and compiles an instance of a 62 /// Wasm module. 63 pub struct WasmtimeInstance { 64 store: Store<StoreLimits>, 65 instance: Instance, 66 } 67 68 impl WasmtimeInstance { 69 /// Instantiate a new Wasmtime instance. 70 pub fn new(mut store: Store<StoreLimits>, module: Module) -> Result<Self> { 71 let instance = dummy::dummy_linker(&mut store, &module) 72 .and_then(|l| l.instantiate(&mut store, &module)) 73 .context("unable to instantiate module in wasmtime")?; 74 Ok(Self { store, instance }) 75 } 76 77 /// Retrieve the names and types of all exported functions in the instance. 78 /// 79 /// This is useful for evaluating each exported function with different 80 /// values. The [`DiffInstance`] trait asks for the function name and we 81 /// need to know the function signature in order to pass in the right 82 /// arguments. 83 pub fn exported_functions(&mut self) -> Vec<(String, FuncType)> { 84 let exported_functions = self 85 .instance 86 .exports(&mut self.store) 87 .map(|e| (e.name().to_owned(), e.into_func())) 88 .filter_map(|(n, f)| f.map(|f| (n, f))) 89 .collect::<Vec<_>>(); 90 exported_functions 91 .into_iter() 92 .map(|(n, f)| (n, f.ty(&self.store))) 93 .collect() 94 } 95 96 /// Returns the list of globals and their types exported from this instance. 97 pub fn exported_globals(&mut self) -> Vec<(String, DiffValueType)> { 98 let globals = self 99 .instance 100 .exports(&mut self.store) 101 .filter_map(|e| { 102 let name = e.name(); 103 e.into_global().map(|g| (name.to_string(), g)) 104 }) 105 .collect::<Vec<_>>(); 106 107 globals 108 .into_iter() 109 .map(|(name, global)| { 110 ( 111 name, 112 global.ty(&self.store).content().clone().try_into().unwrap(), 113 ) 114 }) 115 .collect() 116 } 117 118 /// Returns the list of exported memories and whether or not it's a shared 119 /// memory. 120 pub fn exported_memories(&mut self) -> Vec<(String, bool)> { 121 self.instance 122 .exports(&mut self.store) 123 .filter_map(|e| { 124 let name = e.name(); 125 match e.into_extern() { 126 Extern::Memory(_) => Some((name.to_string(), false)), 127 Extern::SharedMemory(_) => Some((name.to_string(), true)), 128 _ => None, 129 } 130 }) 131 .collect() 132 } 133 134 /// Returns whether or not this instance has hit its OOM condition yet. 135 pub fn is_oom(&self) -> bool { 136 self.store.data().is_oom() 137 } 138 } 139 140 impl DiffInstance for WasmtimeInstance { 141 fn name(&self) -> &'static str { 142 "wasmtime" 143 } 144 145 fn evaluate( 146 &mut self, 147 function_name: &str, 148 arguments: &[DiffValue], 149 _results: &[DiffValueType], 150 ) -> Result<Option<Vec<DiffValue>>> { 151 let arguments: Vec<_> = arguments.iter().map(Val::from).collect(); 152 153 let function = self 154 .instance 155 .get_func(&mut self.store, function_name) 156 .expect("unable to access exported function"); 157 let ty = function.ty(&self.store); 158 let mut results = vec![Val::I32(0); ty.results().len()]; 159 function.call(&mut self.store, &arguments, &mut results)?; 160 161 let results = results.into_iter().map(Val::into).collect(); 162 Ok(Some(results)) 163 } 164 165 fn get_global(&mut self, name: &str, _ty: DiffValueType) -> Option<DiffValue> { 166 Some( 167 self.instance 168 .get_global(&mut self.store, name) 169 .unwrap() 170 .get(&mut self.store) 171 .into(), 172 ) 173 } 174 175 fn get_memory(&mut self, name: &str, shared: bool) -> Option<Vec<u8>> { 176 Some(if shared { 177 let memory = self 178 .instance 179 .get_shared_memory(&mut self.store, name) 180 .unwrap(); 181 memory.data().iter().map(|i| unsafe { *i.get() }).collect() 182 } else { 183 self.instance 184 .get_memory(&mut self.store, name) 185 .unwrap() 186 .data(&self.store) 187 .to_vec() 188 }) 189 } 190 } 191 192 impl From<&DiffValue> for Val { 193 fn from(v: &DiffValue) -> Self { 194 match *v { 195 DiffValue::I32(n) => Val::I32(n), 196 DiffValue::I64(n) => Val::I64(n), 197 DiffValue::F32(n) => Val::F32(n), 198 DiffValue::F64(n) => Val::F64(n), 199 DiffValue::V128(n) => Val::V128(n), 200 DiffValue::FuncRef { null } => { 201 assert!(null); 202 Val::FuncRef(None) 203 } 204 DiffValue::ExternRef { null } => { 205 assert!(null); 206 Val::ExternRef(None) 207 } 208 } 209 } 210 } 211 212 impl Into<DiffValue> for Val { 213 fn into(self) -> DiffValue { 214 match self { 215 Val::I32(n) => DiffValue::I32(n), 216 Val::I64(n) => DiffValue::I64(n), 217 Val::F32(n) => DiffValue::F32(n), 218 Val::F64(n) => DiffValue::F64(n), 219 Val::V128(n) => DiffValue::V128(n), 220 Val::FuncRef(f) => DiffValue::FuncRef { null: f.is_none() }, 221 Val::ExternRef(e) => DiffValue::ExternRef { null: e.is_none() }, 222 } 223 } 224 } 225 226 #[test] 227 fn smoke() { 228 crate::oracles::engine::smoke_test_engine(|u, config| WasmtimeEngine::new(u, config)) 229 } 230