1 //! Evaluate an exported Wasm function using the WebAssembly specification 2 //! reference interpreter. 3 4 use crate::generators::{Config, DiffValue, DiffValueType}; 5 use crate::oracles::engine::{DiffEngine, DiffInstance}; 6 use anyhow::{Error, Result, anyhow}; 7 use wasm_spec_interpreter::SpecValue; 8 use wasmtime::Trap; 9 10 /// A wrapper for `wasm-spec-interpreter` as a [`DiffEngine`]. 11 pub struct SpecInterpreter; 12 13 impl SpecInterpreter { 14 pub(crate) fn new(config: &mut Config) -> Self { 15 let config = &mut config.module_config.config; 16 17 config.min_memories = config.min_memories.min(1); 18 config.max_memories = config.max_memories.min(1); 19 config.min_tables = config.min_tables.min(1); 20 config.max_tables = config.max_tables.min(1); 21 22 config.memory64_enabled = false; 23 config.threads_enabled = false; 24 config.bulk_memory_enabled = false; 25 config.reference_types_enabled = false; 26 config.tail_call_enabled = false; 27 config.relaxed_simd_enabled = false; 28 config.custom_page_sizes_enabled = false; 29 config.wide_arithmetic_enabled = false; 30 config.extended_const_enabled = false; 31 32 Self 33 } 34 } 35 36 impl DiffEngine for SpecInterpreter { 37 fn name(&self) -> &'static str { 38 "spec" 39 } 40 41 fn instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> { 42 let instance = wasm_spec_interpreter::instantiate(wasm) 43 .map_err(|e| anyhow!("failed to instantiate in spec interpreter: {}", e))?; 44 Ok(Box::new(SpecInstance { instance })) 45 } 46 47 fn assert_error_match(&self, err: &Error, trap: &Trap) { 48 // TODO: implement this for the spec interpreter 49 let _ = (trap, err); 50 } 51 52 fn is_non_deterministic_error(&self, err: &Error) -> bool { 53 err.to_string().contains("(Isabelle) call stack exhausted") 54 } 55 } 56 57 struct SpecInstance { 58 instance: wasm_spec_interpreter::SpecInstance, 59 } 60 61 impl DiffInstance for SpecInstance { 62 fn name(&self) -> &'static str { 63 "spec" 64 } 65 66 fn evaluate( 67 &mut self, 68 function_name: &str, 69 arguments: &[DiffValue], 70 _results: &[DiffValueType], 71 ) -> Result<Option<Vec<DiffValue>>> { 72 let arguments = arguments.iter().map(SpecValue::from).collect(); 73 match wasm_spec_interpreter::interpret(&self.instance, function_name, Some(arguments)) { 74 Ok(results) => Ok(Some(results.into_iter().map(SpecValue::into).collect())), 75 Err(err) => Err(anyhow!(err)), 76 } 77 } 78 79 fn get_global(&mut self, name: &str, _ty: DiffValueType) -> Option<DiffValue> { 80 use wasm_spec_interpreter::{SpecExport::Global, export}; 81 if let Ok(Global(g)) = export(&self.instance, name) { 82 Some(g.into()) 83 } else { 84 panic!("expected an exported global value at name `{name}`") 85 } 86 } 87 88 fn get_memory(&mut self, name: &str, _shared: bool) -> Option<Vec<u8>> { 89 use wasm_spec_interpreter::{SpecExport::Memory, export}; 90 if let Ok(Memory(m)) = export(&self.instance, name) { 91 Some(m) 92 } else { 93 panic!("expected an exported memory at name `{name}`") 94 } 95 } 96 } 97 98 impl From<&DiffValue> for SpecValue { 99 fn from(v: &DiffValue) -> Self { 100 match *v { 101 DiffValue::I32(n) => SpecValue::I32(n), 102 DiffValue::I64(n) => SpecValue::I64(n), 103 DiffValue::F32(n) => SpecValue::F32(n as i32), 104 DiffValue::F64(n) => SpecValue::F64(n as i64), 105 DiffValue::V128(n) => SpecValue::V128(n.to_le_bytes().to_vec()), 106 DiffValue::FuncRef { .. } | DiffValue::ExternRef { .. } | DiffValue::AnyRef { .. } => { 107 unimplemented!() 108 } 109 } 110 } 111 } 112 113 impl From<SpecValue> for DiffValue { 114 fn from(spec: SpecValue) -> DiffValue { 115 match spec { 116 SpecValue::I32(n) => DiffValue::I32(n), 117 SpecValue::I64(n) => DiffValue::I64(n), 118 SpecValue::F32(n) => DiffValue::F32(n as u32), 119 SpecValue::F64(n) => DiffValue::F64(n as u64), 120 SpecValue::V128(n) => { 121 assert_eq!(n.len(), 16); 122 DiffValue::V128(u128::from_le_bytes(n.as_slice().try_into().unwrap())) 123 } 124 } 125 } 126 } 127 128 /// Set up the OCaml runtime for triggering its signal handler configuration. 129 /// 130 /// Because both the OCaml runtime and Wasmtime set up signal handlers, we must 131 /// carefully decide when to instantiate them; this function allows us to 132 /// control when. Wasmtime uses these signal handlers for catching various 133 /// WebAssembly failures. On certain OSes (e.g. Linux `x86_64`), the signal 134 /// handlers interfere, observable as an uncaught `SIGSEGV`--not even caught by 135 /// libFuzzer. 136 /// 137 /// This failure can be mitigated by always running Wasmtime second in 138 /// differential fuzzing. In some cases, however, this is not possible because 139 /// which engine will execute first is unknown. This function can be explicitly 140 /// executed first, e.g., during global initialization, to avoid this issue. 141 pub fn setup_ocaml_runtime() { 142 wasm_spec_interpreter::setup_ocaml_runtime(); 143 } 144 145 #[cfg(test)] 146 mod tests { 147 use super::*; 148 149 #[test] 150 fn smoke() { 151 if !wasm_spec_interpreter::support_compiled_in() { 152 return; 153 } 154 crate::oracles::engine::smoke_test_engine(|_, config| Ok(SpecInterpreter::new(config))) 155 } 156 } 157