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