Skip to content

Commit

Permalink
Refactor generation of tables/elements in wasm-smith (#1426)
Browse files Browse the repository at this point in the history
* Refactor generation of tables/elements in wasm-smith

This commit refactors `wasm-smith` and its generation of both table
types and element segments. The goal is to help generate modules of
shapes that Wasmtime does not currently support but should. Notably
constant expressions are allowed to use `global.get`, even in element
segments, and Wasmtime does not currently support this.

Furthermore this commit additionally enables generating tables with
initialization expressions which was not previously supported by
`wasm-smith`.

Internally this refactors a number of pieces of code that work with
constant expressions to instead use a shared helper for generating
constant expressions. This takes into account subtyping and such to try
to generate interesting shapes of expressions when GC is enabled in
particular.

* Don't duplicate `arbitrary_ref_type`

* Refactor core type generation in wasm-smith

Don't pass around `type_ref_limit` as an explicit parameter but instead
track it in the `Module` state. This enables reusing
`self.arbitrary_ref_type` in `self.arbitrary_valtype` and preserves the
property where self-referential types may be generated.

* Fix fuzz-stats test

* Fix wasm-smith tests
  • Loading branch information
alexcrichton authored Feb 27, 2024
1 parent a9223be commit b920742
Show file tree
Hide file tree
Showing 8 changed files with 418 additions and 304 deletions.
10 changes: 9 additions & 1 deletion crates/fuzz-stats/src/bin/failed-instantiations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,15 @@ impl State {
// 1gb of memory. That's half the default allocation of memory for
// libfuzzer-based fuzzers by default, and ideally we're not in a
// situation where most of the modules are above this threshold.
let module = Module::new(&self.engine, &wasm).expect("failed to compile module");
let module = match Module::new(&self.engine, &wasm) {
Ok(m) => m,
// NB: after bytecodealliance/wasm-tools#1426 wasm-smith is
// generating modules that Wasmtime can't handle until
// bytecodealliance/wasmtime#7996 is on crates.io, until that time
// ignore these errors.
Err(e) if format!("{e:?}").contains("unsupported init expr") => return Ok(()),
Err(e) => panic!("unexpected module compile error {e:?}"),
};
let mut store = Store::new(
&self.engine,
fuzz_stats::limits::StoreLimits {
Expand Down
14 changes: 14 additions & 0 deletions crates/wasm-encoder/src/core/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3302,6 +3302,20 @@ impl ConstExpr {
pub fn with_i64_mul(self) -> Self {
self.with_insn(Instruction::I64Mul)
}

/// Returns the function, if any, referenced by this global.
pub fn get_ref_func(&self) -> Option<u32> {
let prefix = *self.bytes.get(0)?;
// 0xd2 == `ref.func` opcode, and if that's found then load the leb
// corresponding to the function index.
if prefix != 0xd2 {
return None;
}
leb128::read::unsigned(&mut &self.bytes[1..])
.ok()?
.try_into()
.ok()
}
}

impl Encode for ConstExpr {
Expand Down
52 changes: 48 additions & 4 deletions crates/wasm-smith/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ use std::{
collections::{HashMap, HashSet},
rc::Rc,
};
use wasm_encoder::{ComponentTypeRef, ComponentValType, PrimitiveValType, TypeBounds, ValType};
use wasm_encoder::{
ComponentTypeRef, ComponentValType, HeapType, PrimitiveValType, RefType, TypeBounds, ValType,
};

mod encode;

Expand Down Expand Up @@ -539,7 +541,7 @@ impl ComponentBuilder {
}

let ty = match u.int_in_range::<u8>(0..=1)? {
0 => CoreType::Func(crate::core::arbitrary_func_type(
0 => CoreType::Func(arbitrary_func_type(
u,
&self.config,
&self.core_valtypes,
Expand Down Expand Up @@ -818,7 +820,7 @@ impl ComponentBuilder {

// Type definition.
2 => {
let ty = crate::core::arbitrary_func_type(
let ty = arbitrary_func_type(
u,
&self.config,
&self.core_valtypes,
Expand Down Expand Up @@ -954,7 +956,7 @@ impl ComponentBuilder {
}

fn arbitrary_core_table_type(&self, u: &mut Unstructured) -> Result<crate::core::TableType> {
crate::core::arbitrary_table_type(u, &self.config)
crate::core::arbitrary_table_type(u, &self.config, None)
}

fn arbitrary_core_memory_type(&self, u: &mut Unstructured) -> Result<crate::core::MemoryType> {
Expand Down Expand Up @@ -2175,3 +2177,45 @@ struct CoreInstanceSection {}
struct CoreTypeSection {
types: Vec<Rc<CoreType>>,
}

fn arbitrary_func_type(
u: &mut Unstructured,
config: &Config,
valtypes: &[ValType],
max_results: Option<usize>,
type_ref_limit: u32,
) -> Result<Rc<crate::core::FuncType>> {
let mut params = vec![];
let mut results = vec![];
arbitrary_loop(u, 0, 20, |u| {
params.push(arbitrary_valtype(u, config, valtypes, type_ref_limit)?);
Ok(true)
})?;
arbitrary_loop(u, 0, max_results.unwrap_or(20), |u| {
results.push(arbitrary_valtype(u, config, valtypes, type_ref_limit)?);
Ok(true)
})?;
Ok(Rc::new(crate::core::FuncType { params, results }))
}

fn arbitrary_valtype(
u: &mut Unstructured,
config: &Config,
valtypes: &[ValType],
type_ref_limit: u32,
) -> Result<ValType> {
if config.gc_enabled && type_ref_limit > 0 && u.ratio(1, 20)? {
Ok(ValType::Ref(RefType {
// TODO: For now, only create allow nullable reference
// types. Eventually we should support non-nullable reference types,
// but this means that we will also need to recognize when it is
// impossible to create an instance of the reference (eg `(ref
// nofunc)` has no instances, and self-referential types that
// contain a non-null self-reference are also impossible to create).
nullable: true,
heap_type: HeapType::Concrete(u.int_in_range(0..=type_ref_limit - 1)?),
}))
} else {
Ok(*u.choose(valtypes)?)
}
}
Loading

0 comments on commit b920742

Please sign in to comment.