Port v8 fuzzer to the new framework (#4739)

* Port v8 fuzzer to the new framework

This commit aims to improve the support for the new "meta" differential
fuzzer added in #4515 by ensuring that all existing differential fuzzing
is migrated to this new fuzzer. This PR includes features such as:

* The V8 differential execution is migrated to the new framework.
* `Config::set_differential_config` no longer force-disables wasm
  features, instead allowing them to be enabled as per the fuzz input.
* `DiffInstance::{hash, hash}` was replaced with
  `DiffInstance::get_{memory,global}` to allow more fine-grained
  assertions.
* Support for `FuncRef` and `ExternRef` have been added to `DiffValue`
  and `DiffValueType`. For now though generating an arbitrary
  `ExternRef` and `FuncRef` simply generates a null value.
* Arbitrary `DiffValue::{F32,F64}` values are guaranteed to use
  canonical NaN representations to fix an issue with v8 where with the
  v8 engine we can't communicate non-canonical NaN values through JS.
* `DiffEngine::evaluate` allows "successful failure" for cases where
  engines can't support that particular invocation, for example v8 can't
  support `v128` arguments or return values.
* Smoke tests were added for each engine to ensure that a simple wasm
  module works at PR-time.
* Statistics printed from the main fuzzer now include percentage-rates
  for chosen engines as well as percentage rates for styles-of-module.

There's also a few small refactorings here and there but mostly just
things I saw along the way.

* Update the fuzzing README
This commit is contained in:
Alex Crichton
2022-08-19 14:19:00 -05:00
committed by GitHub
parent 9758f5420e
commit fd98814b96
14 changed files with 891 additions and 695 deletions

View File

@@ -27,14 +27,23 @@ pub struct Config {
impl Config {
/// Indicates that this configuration is being used for differential
/// execution so only a single function should be generated since that's all
/// that's going to be exercised.
/// execution.
///
/// The purpose of this function is to update the configuration which was
/// generated to be compatible with execution in multiple engines. The goal
/// is to produce the exact same result in all engines so we need to paper
/// over things like nan differences and memory/table behavior differences.
pub fn set_differential_config(&mut self) {
let config = &mut self.module_config.config;
// Disable the start function for now.
//
// TODO: should probably allow this after testing it works with the new
// differential setup in all engines.
config.allow_start_export = false;
// Make sure there's a type available for the function.
// Make it more likely that there are types available to generate a
// function with.
config.min_types = 1;
config.max_types = config.max_types.max(1);
@@ -70,15 +79,6 @@ impl Config {
// can paper over NaN differences between engines.
config.canonicalize_nans = true;
// When diffing against a non-wasmtime engine then disable wasm
// features to get selectively re-enabled against each differential
// engine.
config.bulk_memory_enabled = false;
config.reference_types_enabled = false;
config.simd_enabled = false;
config.memory64_enabled = false;
config.threads_enabled = false;
// If using the pooling allocator, update the instance limits too
if let InstanceAllocationStrategy::Pooling {
instance_limits: limits,
@@ -103,34 +103,6 @@ impl Config {
}
}
/// Force `self` to be a configuration compatible with `other`. This is
/// useful for differential execution to avoid unhelpful fuzz crashes when
/// one engine has a feature enabled and the other does not.
pub fn make_compatible_with(&mut self, other: &Self) {
// Use the same `wasm-smith` configuration as `other` because this is
// used for determining what Wasm features are enabled in the engine
// (see `to_wasmtime`).
self.module_config = other.module_config.clone();
// Use the same allocation strategy between the two configs.
//
// Ideally this wouldn't be necessary, but, during differential
// evaluation, if the `lhs` is using ondemand and the `rhs` is using the
// pooling allocator (or vice versa), then the module may have been
// generated in such a way that is incompatible with the other
// allocation strategy.
//
// We can remove this in the future when it's possible to access the
// fields of `wasm_smith::Module` to constrain the pooling allocator
// based on what was actually generated.
self.wasmtime.strategy = other.wasmtime.strategy.clone();
if let InstanceAllocationStrategy::Pooling { .. } = &other.wasmtime.strategy {
// Also use the same memory configuration when using the pooling
// allocator.
self.wasmtime.memory_config = other.wasmtime.memory_config.clone();
}
}
/// Uses this configuration and the supplied source of data to generate
/// a wasm module.
///
@@ -416,6 +388,31 @@ pub struct WasmtimeConfig {
native_unwind_info: bool,
}
impl WasmtimeConfig {
/// Force `self` to be a configuration compatible with `other`. This is
/// useful for differential execution to avoid unhelpful fuzz crashes when
/// one engine has a feature enabled and the other does not.
pub fn make_compatible_with(&mut self, other: &Self) {
// Use the same allocation strategy between the two configs.
//
// Ideally this wouldn't be necessary, but, during differential
// evaluation, if the `lhs` is using ondemand and the `rhs` is using the
// pooling allocator (or vice versa), then the module may have been
// generated in such a way that is incompatible with the other
// allocation strategy.
//
// We can remove this in the future when it's possible to access the
// fields of `wasm_smith::Module` to constrain the pooling allocator
// based on what was actually generated.
self.strategy = other.strategy.clone();
if let InstanceAllocationStrategy::Pooling { .. } = &other.strategy {
// Also use the same memory configuration when using the pooling
// allocator.
self.memory_config = other.memory_config.clone();
}
}
}
#[derive(Arbitrary, Clone, Debug, PartialEq, Eq, Hash)]
enum OptLevel {
None,