Update wasmi used in differential fuzzing (#5104)

* Update `wasmi` used in differential fuzzing

Closes #4818
Closes #5102

* Add audits
This commit is contained in:
Alex Crichton
2022-10-24 11:41:40 -05:00
committed by GitHub
parent 37c3342374
commit 95f02eb67d
6 changed files with 163 additions and 134 deletions

View File

@@ -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,

View File

@@ -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),