Path: blob/main/crates/fuzzing/src/oracles/diff_wasmi.rs
3054 views
//! Evaluate an exported Wasm function using the wasmi interpreter.12use crate::generators::{Config, DiffValue, DiffValueType};3use crate::oracles::engine::{DiffEngine, DiffInstance};4use wasmtime::{Error, Result, Trap, error::Context as _};56/// A wrapper for `wasmi` as a [`DiffEngine`].7pub struct WasmiEngine {8engine: wasmi::Engine,9}1011impl WasmiEngine {12pub(crate) fn new(config: &mut Config) -> Self {13let config = &mut config.module_config.config;14// Force generated Wasm modules to never have features that Wasmi doesn't support.15config.relaxed_simd_enabled = false;16config.threads_enabled = false;17config.exceptions_enabled = false;18config.gc_enabled = false;1920let mut wasmi_config = wasmi::Config::default();21wasmi_config22.consume_fuel(false)23.floats(true)24.wasm_mutable_global(true)25.wasm_sign_extension(config.sign_extension_ops_enabled)26.wasm_saturating_float_to_int(config.saturating_float_to_int_enabled)27.wasm_multi_value(config.multi_value_enabled)28.wasm_bulk_memory(config.bulk_memory_enabled)29.wasm_reference_types(config.reference_types_enabled)30.wasm_tail_call(config.tail_call_enabled)31.wasm_multi_memory(config.max_memories > 1)32.wasm_extended_const(config.extended_const_enabled)33.wasm_custom_page_sizes(config.custom_page_sizes_enabled)34.wasm_memory64(config.memory64_enabled)35.wasm_simd(config.simd_enabled)36.wasm_wide_arithmetic(config.wide_arithmetic_enabled);37Self {38engine: wasmi::Engine::new(&wasmi_config),39}40}4142fn trap_code(&self, err: &Error) -> Option<wasmi::TrapCode> {43let err = err.downcast_ref::<wasmi::Error>()?;44if let Some(code) = err.as_trap_code() {45return Some(code);46}4748match err.kind() {49wasmi::errors::ErrorKind::Instantiation(50wasmi::errors::InstantiationError::ElementSegmentDoesNotFit { .. },51) => Some(wasmi::TrapCode::TableOutOfBounds),52wasmi::errors::ErrorKind::Memory(wasmi::errors::MemoryError::OutOfBoundsAccess) => {53Some(wasmi::TrapCode::MemoryOutOfBounds)54}55wasmi::errors::ErrorKind::Table(wasmi::errors::TableError::CopyOutOfBounds) => {56Some(wasmi::TrapCode::TableOutOfBounds)57}58_ => {59log::trace!("unknown wasmi error: {:?}", err.kind());60None61}62}63}64}6566impl DiffEngine for WasmiEngine {67fn name(&self) -> &'static str {68"wasmi"69}7071fn instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> {72let module =73wasmi::Module::new(&self.engine, wasm).context("unable to validate Wasm module")?;74let mut store = wasmi::Store::new(&self.engine, ());75let instance = wasmi::Linker::<()>::new(&self.engine)76.instantiate_and_start(&mut store, &module)77.context("unable to instantiate module in wasmi")?;78Ok(Box::new(WasmiInstance { store, instance }))79}8081fn assert_error_match(&self, lhs: &Error, rhs: &Trap) {82match self.trap_code(lhs) {83Some(code) => assert_eq!(wasmi_to_wasmtime_trap_code(code), *rhs),84None => panic!("unexpected wasmi error {lhs:?}"),85}86}8788fn is_non_deterministic_error(&self, err: &Error) -> bool {89matches!(self.trap_code(err), Some(wasmi::TrapCode::StackOverflow))90}91}9293/// Converts `wasmi` trap code to `wasmtime` trap code.94fn wasmi_to_wasmtime_trap_code(trap: wasmi::TrapCode) -> Trap {95use wasmi::TrapCode;96match trap {97TrapCode::UnreachableCodeReached => Trap::UnreachableCodeReached,98TrapCode::MemoryOutOfBounds => Trap::MemoryOutOfBounds,99TrapCode::TableOutOfBounds => Trap::TableOutOfBounds,100TrapCode::IndirectCallToNull => Trap::IndirectCallToNull,101TrapCode::IntegerDivisionByZero => Trap::IntegerDivisionByZero,102TrapCode::IntegerOverflow => Trap::IntegerOverflow,103TrapCode::BadConversionToInteger => Trap::BadConversionToInteger,104TrapCode::StackOverflow => Trap::StackOverflow,105TrapCode::BadSignature => Trap::BadSignature,106TrapCode::OutOfFuel => unimplemented!("built-in fuel metering is unused"),107TrapCode::GrowthOperationLimited => unimplemented!("resource limiter is unused"),108}109}110111/// A wrapper for `wasmi` Wasm instances.112struct WasmiInstance {113store: wasmi::Store<()>,114instance: wasmi::Instance,115}116117impl DiffInstance for WasmiInstance {118fn name(&self) -> &'static str {119"wasmi"120}121122fn evaluate(123&mut self,124function_name: &str,125arguments: &[DiffValue],126result_tys: &[DiffValueType],127) -> Result<Option<Vec<DiffValue>>> {128let function = self129.instance130.get_export(&self.store, function_name)131.and_then(wasmi::Extern::into_func)132.unwrap();133let arguments: Vec<_> = arguments.iter().map(|x| x.into()).collect();134let mut results = vec![wasmi::Val::I32(0); result_tys.len()];135function136.call(&mut self.store, &arguments, &mut results)137.context("wasmi function trap")?;138Ok(Some(results.into_iter().map(Into::into).collect()))139}140141fn get_global(&mut self, name: &str, _ty: DiffValueType) -> Option<DiffValue> {142Some(143self.instance144.get_export(&self.store, name)145.unwrap()146.into_global()147.unwrap()148.get(&self.store)149.into(),150)151}152153fn get_memory(&mut self, name: &str, shared: bool) -> Option<Vec<u8>> {154assert!(!shared);155Some(156self.instance157.get_export(&self.store, name)158.unwrap()159.into_memory()160.unwrap()161.data(&self.store)162.to_vec(),163)164}165}166167impl From<&DiffValue> for wasmi::Val {168fn from(v: &DiffValue) -> Self {169use wasmi::Val as WasmiValue;170match *v {171DiffValue::I32(n) => WasmiValue::I32(n),172DiffValue::I64(n) => WasmiValue::I64(n),173DiffValue::F32(n) => WasmiValue::F32(wasmi::F32::from_bits(n)),174DiffValue::F64(n) => WasmiValue::F64(wasmi::F64::from_bits(n)),175DiffValue::V128(n) => WasmiValue::V128(wasmi::V128::from(n)),176DiffValue::FuncRef { null } => {177assert!(null);178WasmiValue::default(wasmi::ValType::FuncRef)179}180DiffValue::ExternRef { null } => {181assert!(null);182WasmiValue::default(wasmi::ValType::ExternRef)183}184DiffValue::AnyRef { .. } => unimplemented!(),185DiffValue::ExnRef { .. } => unimplemented!(),186DiffValue::ContRef { .. } => unimplemented!(),187}188}189}190191impl From<wasmi::Val> for DiffValue {192fn from(value: wasmi::Val) -> Self {193use wasmi::Val as WasmiValue;194match value {195WasmiValue::I32(n) => DiffValue::I32(n),196WasmiValue::I64(n) => DiffValue::I64(n),197WasmiValue::F32(n) => DiffValue::F32(n.to_bits()),198WasmiValue::F64(n) => DiffValue::F64(n.to_bits()),199WasmiValue::V128(n) => DiffValue::V128(n.as_u128()),200WasmiValue::FuncRef(f) => DiffValue::FuncRef { null: f.is_null() },201WasmiValue::ExternRef(e) => DiffValue::ExternRef { null: e.is_null() },202}203}204}205206#[cfg(test)]207mod tests {208use super::*;209210#[test]211fn smoke() {212crate::oracles::engine::smoke_test_engine(|_, config| Ok(WasmiEngine::new(config)))213}214}215216217