1 //! Evaluate an exported Wasm function using the wasmi interpreter. 2 3 use crate::generators::{Config, DiffValue, DiffValueType}; 4 use crate::oracles::engine::{DiffEngine, DiffInstance}; 5 use anyhow::{Context, Error, Result}; 6 use wasmtime::Trap; 7 8 /// A wrapper for `wasmi` as a [`DiffEngine`]. 9 pub struct WasmiEngine { 10 engine: wasmi::Engine, 11 } 12 13 impl WasmiEngine { 14 pub(crate) fn new(config: &mut Config) -> Self { 15 let config = &mut config.module_config.config; 16 config.reference_types_enabled = false; 17 config.simd_enabled = false; 18 config.memory64_enabled = false; 19 config.bulk_memory_enabled = false; 20 config.threads_enabled = false; 21 config.max_memories = config.max_memories.min(1); 22 config.min_memories = config.min_memories.min(1); 23 config.max_tables = config.max_tables.min(1); 24 config.min_tables = config.min_tables.min(1); 25 26 Self { 27 engine: wasmi::Engine::default(), 28 } 29 } 30 } 31 32 impl DiffEngine for WasmiEngine { 33 fn name(&self) -> &'static str { 34 "wasmi" 35 } 36 37 fn instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> { 38 let module = 39 wasmi::Module::new(&self.engine, wasm).context("unable to validate Wasm module")?; 40 let mut store = wasmi::Store::new(&self.engine, ()); 41 let instance = wasmi::Linker::<()>::new() 42 .instantiate(&mut store, &module) 43 .and_then(|i| i.start(&mut store)) 44 .context("unable to instantiate module in wasmi")?; 45 Ok(Box::new(WasmiInstance { store, instance })) 46 } 47 48 fn assert_error_match(&self, trap: &Trap, err: &Error) { 49 // Acquire a `wasmi::Trap` from the wasmi error which we'll use to 50 // assert that it has the same kind of trap as the wasmtime-based trap. 51 let wasmi = match err.downcast_ref::<wasmi::Error>() { 52 Some(wasmi::Error::Trap(trap)) => trap, 53 54 // Out-of-bounds data segments turn into this category which 55 // Wasmtime reports as a `MemoryOutOfBounds`. 56 Some(wasmi::Error::Memory(msg)) => { 57 assert_eq!( 58 *trap, 59 Trap::MemoryOutOfBounds, 60 "wasmtime error did not match wasmi: {msg}" 61 ); 62 return; 63 } 64 65 // Ignore this for now, looks like "elements segment does not fit" 66 // falls into this category and to avoid doing string matching this 67 // is just ignored. 68 Some(wasmi::Error::Instantiation(msg)) => { 69 log::debug!("ignoring wasmi instantiation error: {msg}"); 70 return; 71 } 72 73 Some(other) => panic!("unexpected wasmi error: {}", other), 74 75 None => err 76 .downcast_ref::<wasmi::core::Trap>() 77 .expect(&format!("not a trap: {:?}", err)), 78 }; 79 assert!(wasmi.as_code().is_some()); 80 assert_eq!(wasmi_to_wasmtime_trap_code(wasmi.as_code().unwrap()), *trap); 81 } 82 83 fn is_stack_overflow(&self, err: &Error) -> bool { 84 let trap = match err.downcast_ref::<wasmi::Error>() { 85 Some(wasmi::Error::Trap(trap)) => trap, 86 Some(_) => return false, 87 None => match err.downcast_ref::<wasmi::core::Trap>() { 88 Some(trap) => trap, 89 None => return false, 90 }, 91 }; 92 matches!(trap.as_code(), Some(wasmi::core::TrapCode::StackOverflow)) 93 } 94 } 95 96 /// Converts `wasmi` trap code to `wasmtime` trap code. 97 fn wasmi_to_wasmtime_trap_code(trap: wasmi::core::TrapCode) -> Trap { 98 use wasmi::core::TrapCode; 99 match trap { 100 TrapCode::Unreachable => Trap::UnreachableCodeReached, 101 TrapCode::MemoryAccessOutOfBounds => Trap::MemoryOutOfBounds, 102 TrapCode::TableAccessOutOfBounds => Trap::TableOutOfBounds, 103 TrapCode::ElemUninitialized => Trap::IndirectCallToNull, 104 TrapCode::DivisionByZero => Trap::IntegerDivisionByZero, 105 TrapCode::IntegerOverflow => Trap::IntegerOverflow, 106 TrapCode::InvalidConversionToInt => Trap::BadConversionToInteger, 107 TrapCode::StackOverflow => Trap::StackOverflow, 108 TrapCode::UnexpectedSignature => Trap::BadSignature, 109 } 110 } 111 112 /// A wrapper for `wasmi` Wasm instances. 113 struct WasmiInstance { 114 store: wasmi::Store<()>, 115 instance: wasmi::Instance, 116 } 117 118 impl DiffInstance for WasmiInstance { 119 fn name(&self) -> &'static str { 120 "wasmi" 121 } 122 123 fn evaluate( 124 &mut self, 125 function_name: &str, 126 arguments: &[DiffValue], 127 result_tys: &[DiffValueType], 128 ) -> Result<Option<Vec<DiffValue>>> { 129 let function = self 130 .instance 131 .get_export(&self.store, function_name) 132 .and_then(wasmi::Extern::into_func) 133 .unwrap(); 134 let arguments: Vec<_> = arguments.iter().map(|x| x.into()).collect(); 135 let mut results = vec![wasmi::core::Value::I32(0); result_tys.len()]; 136 function 137 .call(&mut self.store, &arguments, &mut results) 138 .context("wasmi function trap")?; 139 Ok(Some(results.into_iter().map(Into::into).collect())) 140 } 141 142 fn get_global(&mut self, name: &str, _ty: DiffValueType) -> Option<DiffValue> { 143 Some( 144 self.instance 145 .get_export(&self.store, name) 146 .unwrap() 147 .into_global() 148 .unwrap() 149 .get(&self.store) 150 .into(), 151 ) 152 } 153 154 fn get_memory(&mut self, name: &str, shared: bool) -> Option<Vec<u8>> { 155 assert!(!shared); 156 Some( 157 self.instance 158 .get_export(&self.store, name) 159 .unwrap() 160 .into_memory() 161 .unwrap() 162 .data(&self.store) 163 .to_vec(), 164 ) 165 } 166 } 167 168 impl From<&DiffValue> for wasmi::core::Value { 169 fn from(v: &DiffValue) -> Self { 170 use wasmi::core::Value::*; 171 match *v { 172 DiffValue::I32(n) => I32(n), 173 DiffValue::I64(n) => I64(n), 174 DiffValue::F32(n) => F32(wasmi::core::F32::from_bits(n)), 175 DiffValue::F64(n) => F64(wasmi::core::F64::from_bits(n)), 176 DiffValue::V128(_) | DiffValue::FuncRef { .. } | DiffValue::ExternRef { .. } => { 177 unimplemented!() 178 } 179 } 180 } 181 } 182 183 impl From<wasmi::core::Value> for DiffValue { 184 fn from(value: wasmi::core::Value) -> Self { 185 use wasmi::core::Value as WasmiValue; 186 match value { 187 WasmiValue::I32(n) => DiffValue::I32(n), 188 WasmiValue::I64(n) => DiffValue::I64(n), 189 WasmiValue::F32(n) => DiffValue::F32(n.to_bits()), 190 WasmiValue::F64(n) => DiffValue::F64(n.to_bits()), 191 } 192 } 193 } 194 195 #[test] 196 fn smoke() { 197 crate::oracles::engine::smoke_test_engine(|_, config| Ok(WasmiEngine::new(config))) 198 } 199