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