Add a fuzz mode to stress unaligned wasm addresses (#3516)
Alignment on all memory instructions in wasm is currently best-effort and not actually required, meaning that whatever wasm actually uses as an address should work regardless of whether the address is aligned or not. This is theoretically tested in the fuzzers via wasm-smith-generated code, but wasm-smith doesn't today have too too high of a chance of generating an actual successful load/store. This commit adds a new configuration option to the `Config` generator for fuzzing which forces usage of a custom linear memory implementation which is backed by Rust's `Vec<u8>` and forces the base address of linear memory to be off-by-one relative to the base address of the `Vec<u8>` itself. This should theoretically force host addresses to almost always be unaligned, even if wasm addresses are otherwise aligned. The main interesting fuzz coverage here is likely to be in the existing `differential` target which compares running the same module in wasmtime with two different `Config` values to ensure the same results are produced. This probably won't increase coverage all that much in the near future due to wasm-smith rarely generating successful loads/stores, but in the meantime by hooking this up into `Config` it also means that we'll be running in comparison against v8 and also ensuring that all spec tests succeed if misalignment is forced at the hardware level. As a side effect this commit also cleans up the fuzzers slightly: * The `DifferentialConfig` struct is removed and folded into `Config` * The `init_hang_limit` processing is removed since we don't use `-ttf`-generated modules from binaryen any more. * Traps are now asserted to have the same trap code, otherwise differential fuzzing fails. * Some more debug logging was added to the differential fuzzer
This commit is contained in:
@@ -238,7 +238,7 @@ pub fn compile(wasm: &[u8], strategy: Strategy) {
|
||||
/// we call the exported functions for all of our different configs.
|
||||
pub fn differential_execution(
|
||||
module: &crate::generators::GeneratedModule,
|
||||
configs: &[crate::generators::DifferentialConfig],
|
||||
configs: &[crate::generators::Config],
|
||||
) {
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
@@ -252,18 +252,13 @@ pub fn differential_execution(
|
||||
return;
|
||||
}
|
||||
|
||||
let configs: Vec<_> = match configs.iter().map(|c| c.to_wasmtime_config()).collect() {
|
||||
Ok(cs) => cs,
|
||||
// If the config is trying to use something that was turned off at
|
||||
// compile time just continue to the next fuzz input.
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
let configs: Vec<_> = configs.iter().map(|c| (c.to_wasmtime(), c)).collect();
|
||||
let mut export_func_results: HashMap<String, Result<Box<[Val]>, Trap>> = Default::default();
|
||||
let wasm = module.module.to_bytes();
|
||||
log_wasm(&wasm);
|
||||
|
||||
for mut config in configs {
|
||||
for (mut config, fuzz_config) in configs {
|
||||
log::debug!("fuzz config: {:?}", fuzz_config);
|
||||
// Disable module linking since it isn't enabled by default for
|
||||
// `GeneratedModule` but is enabled by default for our fuzz config.
|
||||
// Since module linking is currently a breaking change this is required
|
||||
@@ -272,6 +267,9 @@ pub fn differential_execution(
|
||||
|
||||
let engine = Engine::new(&config).unwrap();
|
||||
let mut store = create_store(&engine);
|
||||
if fuzz_config.consume_fuel {
|
||||
store.add_fuel(u64::max_value()).unwrap();
|
||||
}
|
||||
|
||||
let module = Module::new(&engine, &wasm).unwrap();
|
||||
|
||||
@@ -293,10 +291,7 @@ pub fn differential_execution(
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
for (name, f) in exports {
|
||||
// Always call the hang limit initializer first, so that we don't
|
||||
// infinite loop when calling another export.
|
||||
init_hang_limit(&mut store, instance);
|
||||
|
||||
log::debug!("invoke export {:?}", name);
|
||||
let ty = f.ty(&store);
|
||||
let params = dummy::dummy_values(ty.params());
|
||||
let mut results = vec![Val::I32(0); ty.results().len()];
|
||||
@@ -312,17 +307,6 @@ pub fn differential_execution(
|
||||
}
|
||||
}
|
||||
|
||||
fn init_hang_limit<T>(store: &mut Store<T>, instance: Instance) {
|
||||
match instance.get_export(&mut *store, "hangLimitInitializer") {
|
||||
None => return,
|
||||
Some(Extern::Func(f)) => {
|
||||
f.call(store, &[], &mut [])
|
||||
.expect("initializing the hang limit should not fail");
|
||||
}
|
||||
Some(_) => panic!("unexpected hangLimitInitializer export"),
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_same_export_func_result(
|
||||
lhs: &Result<Box<[Val]>, Trap>,
|
||||
rhs: &Result<Box<[Val]>, Trap>,
|
||||
@@ -337,7 +321,11 @@ pub fn differential_execution(
|
||||
};
|
||||
|
||||
match (lhs, rhs) {
|
||||
(Err(_), Err(_)) => {}
|
||||
(Err(a), Err(b)) => {
|
||||
if a.trap_code() != b.trap_code() {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
(Ok(lhs), Ok(rhs)) => {
|
||||
if lhs.len() != rhs.len() {
|
||||
fail();
|
||||
|
||||
Reference in New Issue
Block a user