Path: blob/main/crates/fuzzing/src/oracles/diff_spec.rs
3063 views
//! Evaluate an exported Wasm function using the WebAssembly specification1//! reference interpreter.23use crate::generators::{Config, DiffValue, DiffValueType};4use crate::oracles::engine::{DiffEngine, DiffInstance};5use wasm_spec_interpreter::SpecValue;6use wasmtime::{Error, Result, Trap, format_err};78/// A wrapper for `wasm-spec-interpreter` as a [`DiffEngine`].9pub struct SpecInterpreter;1011impl SpecInterpreter {12pub(crate) fn new(config: &mut Config) -> Self {13let config = &mut config.module_config.config;1415config.min_memories = config.min_memories.min(1);16config.max_memories = config.max_memories.min(1);17config.min_tables = config.min_tables.min(1);18config.max_tables = config.max_tables.min(1);1920config.memory64_enabled = false;21config.threads_enabled = false;22config.bulk_memory_enabled = false;23config.reference_types_enabled = false;24config.tail_call_enabled = false;25config.relaxed_simd_enabled = false;26config.custom_page_sizes_enabled = false;27config.wide_arithmetic_enabled = false;28config.extended_const_enabled = false;29config.exceptions_enabled = false;3031Self32}33}3435impl DiffEngine for SpecInterpreter {36fn name(&self) -> &'static str {37"spec"38}3940fn instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> {41let instance = wasm_spec_interpreter::instantiate(wasm)42.map_err(|e| format_err!("failed to instantiate in spec interpreter: {e}"))?;43Ok(Box::new(SpecInstance { instance }))44}4546fn assert_error_match(&self, err: &Error, trap: &Trap) {47// TODO: implement this for the spec interpreter48let _ = (trap, err);49}5051fn is_non_deterministic_error(&self, err: &Error) -> bool {52err.to_string().contains("(Isabelle) call stack exhausted")53}54}5556struct SpecInstance {57instance: wasm_spec_interpreter::SpecInstance,58}5960impl DiffInstance for SpecInstance {61fn name(&self) -> &'static str {62"spec"63}6465fn evaluate(66&mut self,67function_name: &str,68arguments: &[DiffValue],69_results: &[DiffValueType],70) -> Result<Option<Vec<DiffValue>>> {71let arguments = arguments.iter().map(SpecValue::from).collect();72match wasm_spec_interpreter::interpret(&self.instance, function_name, Some(arguments)) {73Ok(results) => Ok(Some(results.into_iter().map(SpecValue::into).collect())),74Err(err) => Err(format_err!(err)),75}76}7778fn get_global(&mut self, name: &str, _ty: DiffValueType) -> Option<DiffValue> {79use wasm_spec_interpreter::{SpecExport::Global, export};80if let Ok(Global(g)) = export(&self.instance, name) {81Some(g.into())82} else {83panic!("expected an exported global value at name `{name}`")84}85}8687fn get_memory(&mut self, name: &str, _shared: bool) -> Option<Vec<u8>> {88use wasm_spec_interpreter::{SpecExport::Memory, export};89if let Ok(Memory(m)) = export(&self.instance, name) {90Some(m)91} else {92panic!("expected an exported memory at name `{name}`")93}94}95}9697impl From<&DiffValue> for SpecValue {98fn from(v: &DiffValue) -> Self {99match *v {100DiffValue::I32(n) => SpecValue::I32(n),101DiffValue::I64(n) => SpecValue::I64(n),102DiffValue::F32(n) => SpecValue::F32(n as i32),103DiffValue::F64(n) => SpecValue::F64(n as i64),104DiffValue::V128(n) => SpecValue::V128(n.to_le_bytes().to_vec()),105DiffValue::FuncRef { .. }106| DiffValue::ExternRef { .. }107| DiffValue::AnyRef { .. }108| DiffValue::ExnRef { .. }109| DiffValue::ContRef { .. } => {110unimplemented!()111}112}113}114}115116impl From<SpecValue> for DiffValue {117fn from(spec: SpecValue) -> DiffValue {118match spec {119SpecValue::I32(n) => DiffValue::I32(n),120SpecValue::I64(n) => DiffValue::I64(n),121SpecValue::F32(n) => DiffValue::F32(n as u32),122SpecValue::F64(n) => DiffValue::F64(n as u64),123SpecValue::V128(n) => {124assert_eq!(n.len(), 16);125DiffValue::V128(u128::from_le_bytes(n.as_slice().try_into().unwrap()))126}127}128}129}130131/// Set up the OCaml runtime for triggering its signal handler configuration.132///133/// Because both the OCaml runtime and Wasmtime set up signal handlers, we must134/// carefully decide when to instantiate them; this function allows us to135/// control when. Wasmtime uses these signal handlers for catching various136/// WebAssembly failures. On certain OSes (e.g. Linux `x86_64`), the signal137/// handlers interfere, observable as an uncaught `SIGSEGV`--not even caught by138/// libFuzzer.139///140/// This failure can be mitigated by always running Wasmtime second in141/// differential fuzzing. In some cases, however, this is not possible because142/// which engine will execute first is unknown. This function can be explicitly143/// executed first, e.g., during global initialization, to avoid this issue.144pub fn setup_ocaml_runtime() {145wasm_spec_interpreter::setup_ocaml_runtime();146}147148#[cfg(test)]149mod tests {150use super::*;151152#[test]153fn smoke() {154if !wasm_spec_interpreter::support_compiled_in() {155return;156}157crate::oracles::engine::smoke_test_engine(|_, config| Ok(SpecInterpreter::new(config)))158}159}160161162