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 wasm_spec_interpreter::SpecValue; 7 use wasmtime::{Error, Result, Trap, format_err}; 8 9 /// A wrapper for `wasm-spec-interpreter` as a [`DiffEngine`]. 10 pub struct SpecInterpreter; 11 12 impl SpecInterpreter { 13 pub(crate) fn new(config: &mut Config) -> Self { 14 let config = &mut config.module_config.config; 15 16 config.min_memories = config.min_memories.min(1); 17 config.max_memories = config.max_memories.min(1); 18 config.min_tables = config.min_tables.min(1); 19 config.max_tables = config.max_tables.min(1); 20 21 config.memory64_enabled = false; 22 config.threads_enabled = false; 23 config.bulk_memory_enabled = false; 24 config.reference_types_enabled = false; 25 config.tail_call_enabled = false; 26 config.relaxed_simd_enabled = false; 27 config.custom_page_sizes_enabled = false; 28 config.wide_arithmetic_enabled = false; 29 config.extended_const_enabled = false; 30 config.exceptions_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| format_err!("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(format_err!(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 { .. } 107 | DiffValue::ExternRef { .. } 108 | DiffValue::AnyRef { .. } 109 | DiffValue::ExnRef { .. } 110 | DiffValue::ContRef { .. } => { 111 unimplemented!() 112 } 113 } 114 } 115 } 116 117 impl From<SpecValue> for DiffValue { 118 fn from(spec: SpecValue) -> DiffValue { 119 match spec { 120 SpecValue::I32(n) => DiffValue::I32(n), 121 SpecValue::I64(n) => DiffValue::I64(n), 122 SpecValue::F32(n) => DiffValue::F32(n as u32), 123 SpecValue::F64(n) => DiffValue::F64(n as u64), 124 SpecValue::V128(n) => { 125 assert_eq!(n.len(), 16); 126 DiffValue::V128(u128::from_le_bytes(n.as_slice().try_into().unwrap())) 127 } 128 } 129 } 130 } 131 132 /// Set up the OCaml runtime for triggering its signal handler configuration. 133 /// 134 /// Because both the OCaml runtime and Wasmtime set up signal handlers, we must 135 /// carefully decide when to instantiate them; this function allows us to 136 /// control when. Wasmtime uses these signal handlers for catching various 137 /// WebAssembly failures. On certain OSes (e.g. Linux `x86_64`), the signal 138 /// handlers interfere, observable as an uncaught `SIGSEGV`--not even caught by 139 /// libFuzzer. 140 /// 141 /// This failure can be mitigated by always running Wasmtime second in 142 /// differential fuzzing. In some cases, however, this is not possible because 143 /// which engine will execute first is unknown. This function can be explicitly 144 /// executed first, e.g., during global initialization, to avoid this issue. 145 pub fn setup_ocaml_runtime() { 146 wasm_spec_interpreter::setup_ocaml_runtime(); 147 } 148 149 #[cfg(test)] 150 mod tests { 151 use super::*; 152 153 #[test] 154 fn smoke() { 155 if !wasm_spec_interpreter::support_compiled_in() { 156 return; 157 } 158 crate::oracles::engine::smoke_test_engine(|_, config| Ok(SpecInterpreter::new(config))) 159 } 160 } 161