Path: blob/main/crates/fuzzing/src/oracles/diff_spec.rs
1693 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 anyhow::{Error, Result, anyhow};6use wasm_spec_interpreter::SpecValue;7use wasmtime::Trap;89/// A wrapper for `wasm-spec-interpreter` as a [`DiffEngine`].10pub struct SpecInterpreter;1112impl SpecInterpreter {13pub(crate) fn new(config: &mut Config) -> Self {14let config = &mut config.module_config.config;1516config.min_memories = config.min_memories.min(1);17config.max_memories = config.max_memories.min(1);18config.min_tables = config.min_tables.min(1);19config.max_tables = config.max_tables.min(1);2021config.memory64_enabled = false;22config.threads_enabled = false;23config.bulk_memory_enabled = false;24config.reference_types_enabled = false;25config.tail_call_enabled = false;26config.relaxed_simd_enabled = false;27config.custom_page_sizes_enabled = false;28config.wide_arithmetic_enabled = false;29config.extended_const_enabled = false;30config.exceptions_enabled = false;3132Self33}34}3536impl DiffEngine for SpecInterpreter {37fn name(&self) -> &'static str {38"spec"39}4041fn instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> {42let instance = wasm_spec_interpreter::instantiate(wasm)43.map_err(|e| anyhow!("failed to instantiate in spec interpreter: {}", e))?;44Ok(Box::new(SpecInstance { instance }))45}4647fn assert_error_match(&self, err: &Error, trap: &Trap) {48// TODO: implement this for the spec interpreter49let _ = (trap, err);50}5152fn is_non_deterministic_error(&self, err: &Error) -> bool {53err.to_string().contains("(Isabelle) call stack exhausted")54}55}5657struct SpecInstance {58instance: wasm_spec_interpreter::SpecInstance,59}6061impl DiffInstance for SpecInstance {62fn name(&self) -> &'static str {63"spec"64}6566fn evaluate(67&mut self,68function_name: &str,69arguments: &[DiffValue],70_results: &[DiffValueType],71) -> Result<Option<Vec<DiffValue>>> {72let arguments = arguments.iter().map(SpecValue::from).collect();73match wasm_spec_interpreter::interpret(&self.instance, function_name, Some(arguments)) {74Ok(results) => Ok(Some(results.into_iter().map(SpecValue::into).collect())),75Err(err) => Err(anyhow!(err)),76}77}7879fn get_global(&mut self, name: &str, _ty: DiffValueType) -> Option<DiffValue> {80use wasm_spec_interpreter::{SpecExport::Global, export};81if let Ok(Global(g)) = export(&self.instance, name) {82Some(g.into())83} else {84panic!("expected an exported global value at name `{name}`")85}86}8788fn get_memory(&mut self, name: &str, _shared: bool) -> Option<Vec<u8>> {89use wasm_spec_interpreter::{SpecExport::Memory, export};90if let Ok(Memory(m)) = export(&self.instance, name) {91Some(m)92} else {93panic!("expected an exported memory at name `{name}`")94}95}96}9798impl From<&DiffValue> for SpecValue {99fn from(v: &DiffValue) -> Self {100match *v {101DiffValue::I32(n) => SpecValue::I32(n),102DiffValue::I64(n) => SpecValue::I64(n),103DiffValue::F32(n) => SpecValue::F32(n as i32),104DiffValue::F64(n) => SpecValue::F64(n as i64),105DiffValue::V128(n) => SpecValue::V128(n.to_le_bytes().to_vec()),106DiffValue::FuncRef { .. }107| DiffValue::ExternRef { .. }108| DiffValue::AnyRef { .. }109| DiffValue::ExnRef { .. }110| DiffValue::ContRef { .. } => {111unimplemented!()112}113}114}115}116117impl From<SpecValue> for DiffValue {118fn from(spec: SpecValue) -> DiffValue {119match spec {120SpecValue::I32(n) => DiffValue::I32(n),121SpecValue::I64(n) => DiffValue::I64(n),122SpecValue::F32(n) => DiffValue::F32(n as u32),123SpecValue::F64(n) => DiffValue::F64(n as u64),124SpecValue::V128(n) => {125assert_eq!(n.len(), 16);126DiffValue::V128(u128::from_le_bytes(n.as_slice().try_into().unwrap()))127}128}129}130}131132/// Set up the OCaml runtime for triggering its signal handler configuration.133///134/// Because both the OCaml runtime and Wasmtime set up signal handlers, we must135/// carefully decide when to instantiate them; this function allows us to136/// control when. Wasmtime uses these signal handlers for catching various137/// WebAssembly failures. On certain OSes (e.g. Linux `x86_64`), the signal138/// handlers interfere, observable as an uncaught `SIGSEGV`--not even caught by139/// libFuzzer.140///141/// This failure can be mitigated by always running Wasmtime second in142/// differential fuzzing. In some cases, however, this is not possible because143/// which engine will execute first is unknown. This function can be explicitly144/// executed first, e.g., during global initialization, to avoid this issue.145pub fn setup_ocaml_runtime() {146wasm_spec_interpreter::setup_ocaml_runtime();147}148149#[cfg(test)]150mod tests {151use super::*;152153#[test]154fn smoke() {155if !wasm_spec_interpreter::support_compiled_in() {156return;157}158crate::oracles::engine::smoke_test_engine(|_, config| Ok(SpecInterpreter::new(config)))159}160}161162163