1 //! Evaluate an exported Wasm function using the WebAssembly specification
2 //! reference interpreter.
3 
4 use crate::generators::{DiffValue, ModuleConfig};
5 use crate::oracles::engine::{DiffEngine, DiffInstance};
6 use anyhow::{anyhow, bail, Result};
7 use wasm_spec_interpreter::Value;
8 
9 /// A wrapper for `wasm-spec-interpreter` as a [`DiffEngine`].
10 pub struct SpecInterpreter;
11 
12 impl SpecInterpreter {
13     /// Build a new [`SpecInterpreter`] but only if the configuration does not
14     /// rely on features that the current bindings (i.e.,
15     /// `wasm-spec-interpreter`) do not support.
16     pub fn new(config: &ModuleConfig) -> Result<Box<Self>> {
17         if config.config.reference_types_enabled {
18             bail!("the spec interpreter bindings do not support reference types")
19         }
20         if config.config.max_funcs > 1 {
21             // TODO
22             bail!("the spec interpreter bindings can only support one function for now")
23         }
24         if config.config.max_tables > 0 {
25             // TODO
26             bail!("the spec interpreter bindings do not fail as they should with out-of-bounds table accesses")
27         }
28         Ok(Box::new(Self))
29     }
30 }
31 
32 impl DiffEngine for SpecInterpreter {
33     fn name(&self) -> &'static str {
34         "spec"
35     }
36 
37     fn instantiate(&self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> {
38         // TODO: ideally we would avoid copying the module bytes here.
39         Ok(Box::new(SpecInstance {
40             wasm: wasm.to_vec(),
41         }))
42     }
43 }
44 
45 struct SpecInstance {
46     wasm: Vec<u8>,
47 }
48 
49 impl DiffInstance for SpecInstance {
50     fn name(&self) -> &'static str {
51         "spec"
52     }
53 
54     fn evaluate(
55         &mut self,
56         _function_name: &str,
57         arguments: &[DiffValue],
58     ) -> Result<Vec<DiffValue>> {
59         // The spec interpreter needs some work before it can fully support this
60         // interface:
61         //  - TODO adapt `wasm-spec-interpreter` to use function name to select
62         //    function to run
63         //  - TODO adapt `wasm-spec-interpreter` to expose an "instance" with
64         //    so we can hash memory, globals, etc.
65         let arguments = arguments.iter().map(Value::from).collect();
66         match wasm_spec_interpreter::interpret(&self.wasm, Some(arguments)) {
67             Ok(results) => Ok(results.into_iter().map(Value::into).collect()),
68             Err(err) => Err(anyhow!(err)),
69         }
70     }
71 
72     fn is_hashable(&self) -> bool {
73         false
74     }
75 
76     fn hash(&mut self, _state: &mut std::collections::hash_map::DefaultHasher) -> Result<()> {
77         unimplemented!()
78     }
79 }
80 
81 impl From<&DiffValue> for Value {
82     fn from(v: &DiffValue) -> Self {
83         match *v {
84             DiffValue::I32(n) => Value::I32(n),
85             DiffValue::I64(n) => Value::I64(n),
86             DiffValue::F32(n) => Value::F32(n as i32),
87             DiffValue::F64(n) => Value::F64(n as i64),
88             DiffValue::V128(n) => Value::V128(n.to_le_bytes().to_vec()),
89         }
90     }
91 }
92 
93 impl Into<DiffValue> for Value {
94     fn into(self) -> DiffValue {
95         match self {
96             Value::I32(n) => DiffValue::I32(n),
97             Value::I64(n) => DiffValue::I64(n),
98             Value::F32(n) => DiffValue::F32(n as u32),
99             Value::F64(n) => DiffValue::F64(n as u64),
100             Value::V128(n) => {
101                 assert_eq!(n.len(), 16);
102                 DiffValue::V128(u128::from_le_bytes(n.as_slice().try_into().unwrap()))
103             }
104         }
105     }
106 }
107 
108 /// Set up the OCaml runtime for triggering its signal handler configuration.
109 ///
110 /// Because both the OCaml runtime and Wasmtime set up signal handlers, we must
111 /// carefully decide when to instantiate them; this function allows us to
112 /// control when. Wasmtime uses these signal handlers for catching various
113 /// WebAssembly failures. On certain OSes (e.g. Linux `x86_64`), the signal
114 /// handlers interfere, observable as an uncaught `SIGSEGV`--not even caught by
115 /// libFuzzer.
116 ///
117 /// This failure can be mitigated by always running Wasmtime second in
118 /// differential fuzzing. In some cases, however, this is not possible because
119 /// which engine will execute first is unknown. This function can be explicitly
120 /// executed first, e.g., during global initialization, to avoid this issue.
121 pub fn setup_ocaml_runtime() {
122     wasm_spec_interpreter::setup_ocaml_runtime();
123 }
124