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::{anyhow, Error, Result}; 7 use wasm_spec_interpreter::Value; 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 // TODO: right now the interpreter bindings only execute the first 18 // function in the module so if there's possibly more than one function 19 // it's not possible to run the other function. This should be fixed 20 // with improvements to the ocaml bindings to the interpreter. 21 config.min_funcs = 1; 22 config.max_funcs = 1; 23 24 // TODO: right now the instantiation step for the interpreter does 25 // nothing and the evaluation step performs an instantiation followed by 26 // an execution. This means that instantiations which fail in other 27 // engines will "succeed" in the interpreter because the error is 28 // delayed to the execution. This should be fixed by making 29 // instantiation a first-class primitive in our interpreter bindings. 30 config.min_tables = 0; 31 config.max_tables = 0; 32 33 config.min_memories = config.min_memories.min(1); 34 config.max_memories = config.max_memories.min(1); 35 36 config.memory64_enabled = false; 37 config.threads_enabled = false; 38 config.bulk_memory_enabled = false; 39 config.reference_types_enabled = false; 40 41 Self 42 } 43 } 44 45 impl DiffEngine for SpecInterpreter { 46 fn name(&self) -> &'static str { 47 "spec" 48 } 49 50 fn instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> { 51 // TODO: ideally we would avoid copying the module bytes here. 52 Ok(Box::new(SpecInstance { 53 wasm: wasm.to_vec(), 54 })) 55 } 56 57 fn assert_error_match(&self, trap: &Trap, err: &Error) { 58 // TODO: implement this for the spec interpreter 59 drop((trap, err)); 60 } 61 62 fn is_stack_overflow(&self, err: &Error) -> bool { 63 // TODO: implement this for the spec interpreter 64 drop(err); 65 false 66 } 67 } 68 69 struct SpecInstance { 70 wasm: Vec<u8>, 71 } 72 73 impl DiffInstance for SpecInstance { 74 fn name(&self) -> &'static str { 75 "spec" 76 } 77 78 fn evaluate( 79 &mut self, 80 _function_name: &str, 81 arguments: &[DiffValue], 82 _results: &[DiffValueType], 83 ) -> Result<Option<Vec<DiffValue>>> { 84 // The spec interpreter needs some work before it can fully support this 85 // interface: 86 // - TODO adapt `wasm-spec-interpreter` to use function name to select 87 // function to run 88 // - TODO adapt `wasm-spec-interpreter` to expose an "instance" with 89 // so we can hash memory, globals, etc. 90 let arguments = arguments.iter().map(Value::from).collect(); 91 match wasm_spec_interpreter::interpret(&self.wasm, Some(arguments)) { 92 Ok(results) => Ok(Some(results.into_iter().map(Value::into).collect())), 93 Err(err) => Err(anyhow!(err)), 94 } 95 } 96 97 fn get_global(&mut self, _name: &str, _ty: DiffValueType) -> Option<DiffValue> { 98 // TODO: should implement this 99 None 100 } 101 102 fn get_memory(&mut self, _name: &str, _shared: bool) -> Option<Vec<u8>> { 103 // TODO: should implement this 104 None 105 } 106 } 107 108 impl From<&DiffValue> for Value { 109 fn from(v: &DiffValue) -> Self { 110 match *v { 111 DiffValue::I32(n) => Value::I32(n), 112 DiffValue::I64(n) => Value::I64(n), 113 DiffValue::F32(n) => Value::F32(n as i32), 114 DiffValue::F64(n) => Value::F64(n as i64), 115 DiffValue::V128(n) => Value::V128(n.to_le_bytes().to_vec()), 116 DiffValue::FuncRef { .. } | DiffValue::ExternRef { .. } => unimplemented!(), 117 } 118 } 119 } 120 121 impl Into<DiffValue> for Value { 122 fn into(self) -> DiffValue { 123 match self { 124 Value::I32(n) => DiffValue::I32(n), 125 Value::I64(n) => DiffValue::I64(n), 126 Value::F32(n) => DiffValue::F32(n as u32), 127 Value::F64(n) => DiffValue::F64(n as u64), 128 Value::V128(n) => { 129 assert_eq!(n.len(), 16); 130 DiffValue::V128(u128::from_le_bytes(n.as_slice().try_into().unwrap())) 131 } 132 } 133 } 134 } 135 136 /// Set up the OCaml runtime for triggering its signal handler configuration. 137 /// 138 /// Because both the OCaml runtime and Wasmtime set up signal handlers, we must 139 /// carefully decide when to instantiate them; this function allows us to 140 /// control when. Wasmtime uses these signal handlers for catching various 141 /// WebAssembly failures. On certain OSes (e.g. Linux `x86_64`), the signal 142 /// handlers interfere, observable as an uncaught `SIGSEGV`--not even caught by 143 /// libFuzzer. 144 /// 145 /// This failure can be mitigated by always running Wasmtime second in 146 /// differential fuzzing. In some cases, however, this is not possible because 147 /// which engine will execute first is unknown. This function can be explicitly 148 /// executed first, e.g., during global initialization, to avoid this issue. 149 pub fn setup_ocaml_runtime() { 150 wasm_spec_interpreter::setup_ocaml_runtime(); 151 } 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