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