Update wasmi used in differential fuzzing (#5104)
* Update `wasmi` used in differential fuzzing Closes #4818 Closes #5102 * Add audits
This commit is contained in:
@@ -25,7 +25,7 @@ wasm-encoder = { workspace = true }
|
||||
wasm-smith = { workspace = true }
|
||||
wasm-mutate = { workspace = true }
|
||||
wasm-spec-interpreter = { path = "./wasm-spec-interpreter", optional = true }
|
||||
wasmi = "0.11.0"
|
||||
wasmi = "0.19.0"
|
||||
|
||||
# We rely on precompiled v8 binaries, but rusty-v8 doesn't have a precompiled
|
||||
# binary for MinGW which is built on our CI. It does have one for Windows-msvc,
|
||||
|
||||
@@ -6,16 +6,15 @@ use anyhow::{Context, Error, Result};
|
||||
use wasmtime::{Trap, TrapCode};
|
||||
|
||||
/// A wrapper for `wasmi` as a [`DiffEngine`].
|
||||
pub struct WasmiEngine;
|
||||
pub struct WasmiEngine {
|
||||
engine: wasmi::Engine,
|
||||
}
|
||||
|
||||
impl WasmiEngine {
|
||||
pub(crate) fn new(config: &mut Config) -> Self {
|
||||
let config = &mut config.module_config.config;
|
||||
config.reference_types_enabled = false;
|
||||
config.simd_enabled = false;
|
||||
config.multi_value_enabled = false;
|
||||
config.saturating_float_to_int_enabled = false;
|
||||
config.sign_extension_enabled = false;
|
||||
config.memory64_enabled = false;
|
||||
config.bulk_memory_enabled = false;
|
||||
config.threads_enabled = false;
|
||||
@@ -24,7 +23,9 @@ impl WasmiEngine {
|
||||
config.max_tables = config.max_tables.min(1);
|
||||
config.min_tables = config.min_tables.min(1);
|
||||
|
||||
Self
|
||||
Self {
|
||||
engine: wasmi::Engine::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,11 +35,14 @@ impl DiffEngine for WasmiEngine {
|
||||
}
|
||||
|
||||
fn instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> {
|
||||
let module = wasmi::Module::from_buffer(wasm).context("unable to validate Wasm module")?;
|
||||
let instance = wasmi::ModuleInstance::new(&module, &wasmi::ImportsBuilder::default())
|
||||
let module =
|
||||
wasmi::Module::new(&self.engine, wasm).context("unable to validate Wasm module")?;
|
||||
let mut store = wasmi::Store::new(&self.engine, ());
|
||||
let instance = wasmi::Linker::<()>::new()
|
||||
.instantiate(&mut store, &module)
|
||||
.and_then(|i| i.start(&mut store))
|
||||
.context("unable to instantiate module in wasmi")?;
|
||||
let instance = instance.run_start(&mut wasmi::NopExternals)?;
|
||||
Ok(Box::new(WasmiInstance { module, instance }))
|
||||
Ok(Box::new(WasmiInstance { store, instance }))
|
||||
}
|
||||
|
||||
fn assert_error_match(&self, trap: &Trap, err: &Error) {
|
||||
@@ -69,38 +73,38 @@ impl DiffEngine for WasmiEngine {
|
||||
Some(other) => panic!("unexpected wasmi error: {}", other),
|
||||
|
||||
None => err
|
||||
.downcast_ref::<wasmi::Trap>()
|
||||
.downcast_ref::<wasmi::core::Trap>()
|
||||
.expect(&format!("not a trap: {:?}", err)),
|
||||
};
|
||||
match wasmi.kind() {
|
||||
wasmi::TrapKind::StackOverflow => {
|
||||
match wasmi.as_code() {
|
||||
Some(wasmi::core::TrapCode::StackOverflow) => {
|
||||
assert_eq!(trap.trap_code(), Some(TrapCode::StackOverflow))
|
||||
}
|
||||
wasmi::TrapKind::MemoryAccessOutOfBounds => {
|
||||
Some(wasmi::core::TrapCode::MemoryAccessOutOfBounds) => {
|
||||
assert_eq!(trap.trap_code(), Some(TrapCode::MemoryOutOfBounds))
|
||||
}
|
||||
wasmi::TrapKind::Unreachable => {
|
||||
Some(wasmi::core::TrapCode::Unreachable) => {
|
||||
assert_eq!(trap.trap_code(), Some(TrapCode::UnreachableCodeReached))
|
||||
}
|
||||
wasmi::TrapKind::TableAccessOutOfBounds => {
|
||||
Some(wasmi::core::TrapCode::TableAccessOutOfBounds) => {
|
||||
assert_eq!(trap.trap_code(), Some(TrapCode::TableOutOfBounds))
|
||||
}
|
||||
wasmi::TrapKind::ElemUninitialized => {
|
||||
Some(wasmi::core::TrapCode::ElemUninitialized) => {
|
||||
assert_eq!(trap.trap_code(), Some(TrapCode::IndirectCallToNull))
|
||||
}
|
||||
wasmi::TrapKind::DivisionByZero => {
|
||||
Some(wasmi::core::TrapCode::DivisionByZero) => {
|
||||
assert_eq!(trap.trap_code(), Some(TrapCode::IntegerDivisionByZero))
|
||||
}
|
||||
wasmi::TrapKind::IntegerOverflow => {
|
||||
Some(wasmi::core::TrapCode::IntegerOverflow) => {
|
||||
assert_eq!(trap.trap_code(), Some(TrapCode::IntegerOverflow))
|
||||
}
|
||||
wasmi::TrapKind::InvalidConversionToInt => {
|
||||
Some(wasmi::core::TrapCode::InvalidConversionToInt) => {
|
||||
assert_eq!(trap.trap_code(), Some(TrapCode::BadConversionToInteger))
|
||||
}
|
||||
wasmi::TrapKind::UnexpectedSignature => {
|
||||
Some(wasmi::core::TrapCode::UnexpectedSignature) => {
|
||||
assert_eq!(trap.trap_code(), Some(TrapCode::BadSignature))
|
||||
}
|
||||
wasmi::TrapKind::Host(_) => unreachable!(),
|
||||
None => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,13 +112,13 @@ impl DiffEngine for WasmiEngine {
|
||||
let trap = match err.downcast_ref::<wasmi::Error>() {
|
||||
Some(wasmi::Error::Trap(trap)) => trap,
|
||||
Some(_) => return false,
|
||||
None => match err.downcast_ref::<wasmi::Trap>() {
|
||||
None => match err.downcast_ref::<wasmi::core::Trap>() {
|
||||
Some(trap) => trap,
|
||||
None => return false,
|
||||
},
|
||||
};
|
||||
match trap.kind() {
|
||||
wasmi::TrapKind::StackOverflow => true,
|
||||
match trap.as_code() {
|
||||
Some(wasmi::core::TrapCode::StackOverflow) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -122,9 +126,8 @@ impl DiffEngine for WasmiEngine {
|
||||
|
||||
/// A wrapper for `wasmi` Wasm instances.
|
||||
struct WasmiInstance {
|
||||
#[allow(dead_code)] // reason = "the module must live as long as its reference"
|
||||
module: wasmi::Module,
|
||||
instance: wasmi::ModuleRef,
|
||||
store: wasmi::Store<()>,
|
||||
instance: wasmi::Instance,
|
||||
}
|
||||
|
||||
impl DiffInstance for WasmiInstance {
|
||||
@@ -136,57 +139,48 @@ impl DiffInstance for WasmiInstance {
|
||||
&mut self,
|
||||
function_name: &str,
|
||||
arguments: &[DiffValue],
|
||||
_results: &[DiffValueType],
|
||||
result_tys: &[DiffValueType],
|
||||
) -> Result<Option<Vec<DiffValue>>> {
|
||||
let arguments: Vec<_> = arguments.iter().map(wasmi::RuntimeValue::from).collect();
|
||||
let export = self
|
||||
let function = match self
|
||||
.instance
|
||||
.export_by_name(function_name)
|
||||
.context(format!(
|
||||
"unable to find function '{}' in wasmi instance",
|
||||
function_name
|
||||
))?;
|
||||
let function = export.as_func().context("wasmi export is not a function")?;
|
||||
let result = wasmi::FuncInstance::invoke(&function, &arguments, &mut wasmi::NopExternals)
|
||||
.context("failed while invoking function in wasmi")?;
|
||||
Ok(Some(if let Some(result) = result {
|
||||
vec![result.into()]
|
||||
} else {
|
||||
vec![]
|
||||
}))
|
||||
.get_export(&self.store, function_name)
|
||||
.unwrap()
|
||||
{
|
||||
wasmi::Extern::Func(f) => f,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let arguments: Vec<_> = arguments.iter().map(|x| x.into()).collect();
|
||||
let mut results = vec![wasmi::core::Value::I32(0); result_tys.len()];
|
||||
function
|
||||
.call(&mut self.store, &arguments, &mut results)
|
||||
.context("wasmi function trap")?;
|
||||
Ok(Some(results.into_iter().map(|x| x.into()).collect()))
|
||||
}
|
||||
|
||||
fn get_global(&mut self, name: &str, _ty: DiffValueType) -> Option<DiffValue> {
|
||||
match self.instance.export_by_name(name) {
|
||||
Some(wasmi::ExternVal::Global(g)) => Some(g.get().into()),
|
||||
match self.instance.get_export(&self.store, name).unwrap() {
|
||||
wasmi::Extern::Global(g) => Some(g.get(&self.store).into()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_memory(&mut self, name: &str, shared: bool) -> Option<Vec<u8>> {
|
||||
assert!(!shared);
|
||||
match self.instance.export_by_name(name) {
|
||||
Some(wasmi::ExternVal::Memory(m)) => {
|
||||
// `wasmi` memory may be stored non-contiguously; copy
|
||||
// it out to a contiguous chunk.
|
||||
let mut buffer: Vec<u8> = vec![0; m.current_size().0 * 65536];
|
||||
m.get_into(0, &mut buffer[..])
|
||||
.expect("can access wasmi memory");
|
||||
Some(buffer)
|
||||
}
|
||||
match self.instance.get_export(&self.store, name).unwrap() {
|
||||
wasmi::Extern::Memory(m) => Some(m.data(&self.store).to_vec()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&DiffValue> for wasmi::RuntimeValue {
|
||||
impl From<&DiffValue> for wasmi::core::Value {
|
||||
fn from(v: &DiffValue) -> Self {
|
||||
use wasmi::RuntimeValue::*;
|
||||
use wasmi::core::Value::*;
|
||||
match *v {
|
||||
DiffValue::I32(n) => I32(n),
|
||||
DiffValue::I64(n) => I64(n),
|
||||
DiffValue::F32(n) => F32(wasmi::nan_preserving_float::F32::from_bits(n)),
|
||||
DiffValue::F64(n) => F64(wasmi::nan_preserving_float::F64::from_bits(n)),
|
||||
DiffValue::F32(n) => F32(wasmi::core::F32::from_bits(n)),
|
||||
DiffValue::F64(n) => F64(wasmi::core::F64::from_bits(n)),
|
||||
DiffValue::V128(_) | DiffValue::FuncRef { .. } | DiffValue::ExternRef { .. } => {
|
||||
unimplemented!()
|
||||
}
|
||||
@@ -194,9 +188,9 @@ impl From<&DiffValue> for wasmi::RuntimeValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<DiffValue> for wasmi::RuntimeValue {
|
||||
impl Into<DiffValue> for wasmi::core::Value {
|
||||
fn into(self) -> DiffValue {
|
||||
use wasmi::RuntimeValue::*;
|
||||
use wasmi::core::Value::*;
|
||||
match self {
|
||||
I32(n) => DiffValue::I32(n),
|
||||
I64(n) => DiffValue::I64(n),
|
||||
|
||||
Reference in New Issue
Block a user