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