15ec92d59SAndrew Brown //! Evaluate an exported Wasm function using the WebAssembly specification
25ec92d59SAndrew Brown //! reference interpreter.
35ec92d59SAndrew Brown
4543a4879SAlex Crichton use crate::generators::{Config, DiffValue, DiffValueType};
55ec92d59SAndrew Brown use crate::oracles::engine::{DiffEngine, DiffInstance};
6c3f8415aSAndrew Brown use wasm_spec_interpreter::SpecValue;
7*93d22fcdSNick Fitzgerald use wasmtime::{Error, Result, Trap, format_err};
85ec92d59SAndrew Brown
95ec92d59SAndrew Brown /// A wrapper for `wasm-spec-interpreter` as a [`DiffEngine`].
105ec92d59SAndrew Brown pub struct SpecInterpreter;
115ec92d59SAndrew Brown
125ec92d59SAndrew Brown impl SpecInterpreter {
new(config: &mut Config) -> Self13543a4879SAlex Crichton pub(crate) fn new(config: &mut Config) -> Self {
14543a4879SAlex Crichton let config = &mut config.module_config.config;
15543a4879SAlex Crichton
16543a4879SAlex Crichton config.min_memories = config.min_memories.min(1);
17543a4879SAlex Crichton config.max_memories = config.max_memories.min(1);
18c3f8415aSAndrew Brown config.min_tables = config.min_tables.min(1);
19c3f8415aSAndrew Brown config.max_tables = config.max_tables.min(1);
20fd98814bSAlex Crichton
21543a4879SAlex Crichton config.memory64_enabled = false;
22543a4879SAlex Crichton config.threads_enabled = false;
23543a4879SAlex Crichton config.bulk_memory_enabled = false;
24543a4879SAlex Crichton config.reference_types_enabled = false;
2580ed395eSAlex Crichton config.tail_call_enabled = false;
2680ed395eSAlex Crichton config.relaxed_simd_enabled = false;
27edad0bbcSNick Fitzgerald config.custom_page_sizes_enabled = false;
2872ded108SAlex Crichton config.wide_arithmetic_enabled = false;
29d79555adSAlex Crichton config.extended_const_enabled = false;
303aa39239SChris Fallin config.exceptions_enabled = false;
31543a4879SAlex Crichton
32543a4879SAlex Crichton Self
335ec92d59SAndrew Brown }
345ec92d59SAndrew Brown }
355ec92d59SAndrew Brown
365ec92d59SAndrew Brown impl DiffEngine for SpecInterpreter {
name(&self) -> &'static str375ec92d59SAndrew Brown fn name(&self) -> &'static str {
385ec92d59SAndrew Brown "spec"
395ec92d59SAndrew Brown }
405ec92d59SAndrew Brown
instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>>41fd98814bSAlex Crichton fn instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> {
42c3f8415aSAndrew Brown let instance = wasm_spec_interpreter::instantiate(wasm)
43*93d22fcdSNick Fitzgerald .map_err(|e| format_err!("failed to instantiate in spec interpreter: {e}"))?;
44c3f8415aSAndrew Brown Ok(Box::new(SpecInstance { instance }))
455ec92d59SAndrew Brown }
46fd98814bSAlex Crichton
assert_error_match(&self, err: &Error, trap: &Trap)47b3b50943SAlex Crichton fn assert_error_match(&self, err: &Error, trap: &Trap) {
48fd98814bSAlex Crichton // TODO: implement this for the spec interpreter
497f0228c9SAlex Crichton let _ = (trap, err);
50fd98814bSAlex Crichton }
5110dbb199SAlex Crichton
is_non_deterministic_error(&self, err: &Error) -> bool525b9e8765SNick Fitzgerald fn is_non_deterministic_error(&self, err: &Error) -> bool {
53c3f8415aSAndrew Brown err.to_string().contains("(Isabelle) call stack exhausted")
5410dbb199SAlex Crichton }
555ec92d59SAndrew Brown }
565ec92d59SAndrew Brown
575ec92d59SAndrew Brown struct SpecInstance {
58c3f8415aSAndrew Brown instance: wasm_spec_interpreter::SpecInstance,
595ec92d59SAndrew Brown }
605ec92d59SAndrew Brown
615ec92d59SAndrew Brown impl DiffInstance for SpecInstance {
name(&self) -> &'static str625ec92d59SAndrew Brown fn name(&self) -> &'static str {
635ec92d59SAndrew Brown "spec"
645ec92d59SAndrew Brown }
655ec92d59SAndrew Brown
evaluate( &mut self, function_name: &str, arguments: &[DiffValue], _results: &[DiffValueType], ) -> Result<Option<Vec<DiffValue>>>665ec92d59SAndrew Brown fn evaluate(
675ec92d59SAndrew Brown &mut self,
68c3f8415aSAndrew Brown function_name: &str,
695ec92d59SAndrew Brown arguments: &[DiffValue],
70fd98814bSAlex Crichton _results: &[DiffValueType],
71fd98814bSAlex Crichton ) -> Result<Option<Vec<DiffValue>>> {
72c3f8415aSAndrew Brown let arguments = arguments.iter().map(SpecValue::from).collect();
73c3f8415aSAndrew Brown match wasm_spec_interpreter::interpret(&self.instance, function_name, Some(arguments)) {
74c3f8415aSAndrew Brown Ok(results) => Ok(Some(results.into_iter().map(SpecValue::into).collect())),
75*93d22fcdSNick Fitzgerald Err(err) => Err(format_err!(err)),
765ec92d59SAndrew Brown }
775ec92d59SAndrew Brown }
785ec92d59SAndrew Brown
get_global(&mut self, name: &str, _ty: DiffValueType) -> Option<DiffValue>79c3f8415aSAndrew Brown fn get_global(&mut self, name: &str, _ty: DiffValueType) -> Option<DiffValue> {
8090ac295eSAlex Crichton use wasm_spec_interpreter::{SpecExport::Global, export};
81c3f8415aSAndrew Brown if let Ok(Global(g)) = export(&self.instance, name) {
82c3f8415aSAndrew Brown Some(g.into())
83c3f8415aSAndrew Brown } else {
84a0442ea0SHamir Mahal panic!("expected an exported global value at name `{name}`")
855ec92d59SAndrew Brown }
865ec92d59SAndrew Brown }
875ec92d59SAndrew Brown
get_memory(&mut self, name: &str, _shared: bool) -> Option<Vec<u8>>88c3f8415aSAndrew Brown fn get_memory(&mut self, name: &str, _shared: bool) -> Option<Vec<u8>> {
8990ac295eSAlex Crichton use wasm_spec_interpreter::{SpecExport::Memory, export};
90c3f8415aSAndrew Brown if let Ok(Memory(m)) = export(&self.instance, name) {
91c3f8415aSAndrew Brown Some(m)
92c3f8415aSAndrew Brown } else {
93a0442ea0SHamir Mahal panic!("expected an exported memory at name `{name}`")
94c3f8415aSAndrew Brown }
95c3f8415aSAndrew Brown }
96c3f8415aSAndrew Brown }
97c3f8415aSAndrew Brown
98c3f8415aSAndrew Brown impl From<&DiffValue> for SpecValue {
from(v: &DiffValue) -> Self995ec92d59SAndrew Brown fn from(v: &DiffValue) -> Self {
1005ec92d59SAndrew Brown match *v {
101c3f8415aSAndrew Brown DiffValue::I32(n) => SpecValue::I32(n),
102c3f8415aSAndrew Brown DiffValue::I64(n) => SpecValue::I64(n),
103c3f8415aSAndrew Brown DiffValue::F32(n) => SpecValue::F32(n as i32),
104c3f8415aSAndrew Brown DiffValue::F64(n) => SpecValue::F64(n as i64),
105c3f8415aSAndrew Brown DiffValue::V128(n) => SpecValue::V128(n.to_le_bytes().to_vec()),
106eaa4632eSChris Fallin DiffValue::FuncRef { .. }
107eaa4632eSChris Fallin | DiffValue::ExternRef { .. }
108eaa4632eSChris Fallin | DiffValue::AnyRef { .. }
109a631d20aSPaul Osborne | DiffValue::ExnRef { .. }
110a631d20aSPaul Osborne | DiffValue::ContRef { .. } => {
1110fa13013SNick Fitzgerald unimplemented!()
1120fa13013SNick Fitzgerald }
1135ec92d59SAndrew Brown }
1145ec92d59SAndrew Brown }
1155ec92d59SAndrew Brown }
1165ec92d59SAndrew Brown
117d3e7548eSAlex Crichton impl From<SpecValue> for DiffValue {
from(spec: SpecValue) -> DiffValue118d3e7548eSAlex Crichton fn from(spec: SpecValue) -> DiffValue {
119d3e7548eSAlex Crichton match spec {
120c3f8415aSAndrew Brown SpecValue::I32(n) => DiffValue::I32(n),
121c3f8415aSAndrew Brown SpecValue::I64(n) => DiffValue::I64(n),
122c3f8415aSAndrew Brown SpecValue::F32(n) => DiffValue::F32(n as u32),
123c3f8415aSAndrew Brown SpecValue::F64(n) => DiffValue::F64(n as u64),
124c3f8415aSAndrew Brown SpecValue::V128(n) => {
1255ec92d59SAndrew Brown assert_eq!(n.len(), 16);
1265ec92d59SAndrew Brown DiffValue::V128(u128::from_le_bytes(n.as_slice().try_into().unwrap()))
1275ec92d59SAndrew Brown }
1285ec92d59SAndrew Brown }
1295ec92d59SAndrew Brown }
1305ec92d59SAndrew Brown }
1315ec92d59SAndrew Brown
1325ec92d59SAndrew Brown /// Set up the OCaml runtime for triggering its signal handler configuration.
1335ec92d59SAndrew Brown ///
1345ec92d59SAndrew Brown /// Because both the OCaml runtime and Wasmtime set up signal handlers, we must
1355ec92d59SAndrew Brown /// carefully decide when to instantiate them; this function allows us to
1365ec92d59SAndrew Brown /// control when. Wasmtime uses these signal handlers for catching various
1375ec92d59SAndrew Brown /// WebAssembly failures. On certain OSes (e.g. Linux `x86_64`), the signal
1385ec92d59SAndrew Brown /// handlers interfere, observable as an uncaught `SIGSEGV`--not even caught by
1395ec92d59SAndrew Brown /// libFuzzer.
1405ec92d59SAndrew Brown ///
1415ec92d59SAndrew Brown /// This failure can be mitigated by always running Wasmtime second in
1425ec92d59SAndrew Brown /// differential fuzzing. In some cases, however, this is not possible because
1435ec92d59SAndrew Brown /// which engine will execute first is unknown. This function can be explicitly
1445ec92d59SAndrew Brown /// executed first, e.g., during global initialization, to avoid this issue.
setup_ocaml_runtime()1455ec92d59SAndrew Brown pub fn setup_ocaml_runtime() {
1465ec92d59SAndrew Brown wasm_spec_interpreter::setup_ocaml_runtime();
1475ec92d59SAndrew Brown }
148fd98814bSAlex Crichton
149ec3b2d22SNick Fitzgerald #[cfg(test)]
150ec3b2d22SNick Fitzgerald mod tests {
151ec3b2d22SNick Fitzgerald use super::*;
152ec3b2d22SNick Fitzgerald
153fd98814bSAlex Crichton #[test]
smoke()154fd98814bSAlex Crichton fn smoke() {
155fd98814bSAlex Crichton if !wasm_spec_interpreter::support_compiled_in() {
156fd98814bSAlex Crichton return;
157fd98814bSAlex Crichton }
158543a4879SAlex Crichton crate::oracles::engine::smoke_test_engine(|_, config| Ok(SpecInterpreter::new(config)))
159fd98814bSAlex Crichton }
160ec3b2d22SNick Fitzgerald }
161