Skip to content

Commit

Permalink
Limit the type hierarchies in component fuzzing (#4668)
Browse files Browse the repository at this point in the history
* Limit the type hierarchies in component fuzzing

For now `wasmparser` has a hard limit on the size of tuples and such at
1000 recursive types within the tuple itself. Respect this limit by
limiting the width of recursive types generated for the `component_api`
fuzzer. This commit unifies this new requirement with the preexisting
`TupleArray` and `NonEmptyArray` types into one `VecInRange<T, L, H>`
which allow expressing all of these various requirements in one type.

* Fix a compile error on `main`

* Review comments
  • Loading branch information
alexcrichton authored Aug 10, 2022
1 parent 54f9587 commit 597eb6f
Showing 1 changed file with 28 additions and 39 deletions.
67 changes: 28 additions & 39 deletions crates/misc/component-fuzz-util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ pub const IMPORT_FUNCTION: &str = "echo";
/// The name of the exported guest function which the host should call
pub const EXPORT_FUNCTION: &str = "echo";

/// Maximum length of an arbitrary tuple type. As of this writing, the `wasmtime::component::func::typed` module
/// only implements the `ComponentType` trait for tuples up to this length.
const MAX_TUPLE_LENGTH: usize = 16;

#[derive(Copy, Clone, PartialEq, Eq)]
enum CoreType {
I32,
Expand Down Expand Up @@ -76,45 +72,23 @@ impl<'a, const L: usize, const H: usize> Arbitrary<'a> for UsizeInRange<L, H> {
}
}

/// Wraps a `Box<[T]>` and provides an `Arbitrary` implementation that always generates non-empty slices
#[derive(Debug)]
pub struct NonEmptyArray<T>(Box<[T]>);

impl<'a, T: Arbitrary<'a>> Arbitrary<'a> for NonEmptyArray<T> {
fn arbitrary(input: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Self(
iter::once(input.arbitrary())
.chain(input.arbitrary_iter()?)
.collect::<arbitrary::Result<_>>()?,
))
}
}

impl<T> Deref for NonEmptyArray<T> {
type Target = [T];

fn deref(&self) -> &[T] {
self.0.deref()
}
}

/// Wraps a `Box<[T]>` and provides an `Arbitrary` implementation that always generates slices of length less than
/// or equal to the longest tuple for which Wasmtime generates a `ComponentType` impl
#[derive(Debug)]
pub struct TupleArray<T>(Box<[T]>);
pub struct VecInRange<T, const L: u32, const H: u32>(Vec<T>);

impl<'a, T: Arbitrary<'a>> Arbitrary<'a> for TupleArray<T> {
impl<'a, T: Arbitrary<'a>, const L: u32, const H: u32> Arbitrary<'a> for VecInRange<T, L, H> {
fn arbitrary(input: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Self(
input
.arbitrary_iter()?
.take(MAX_TUPLE_LENGTH)
.collect::<arbitrary::Result<_>>()?,
))
let mut ret = Vec::new();
input.arbitrary_loop(Some(L), Some(H), |input| {
ret.push(input.arbitrary()?);
Ok(std::ops::ControlFlow::Continue(()))
})?;
Ok(Self(ret))
}
}

impl<T> Deref for TupleArray<T> {
impl<T, const L: u32, const H: u32> Deref for VecInRange<T, L, H> {
type Target = [T];

fn deref(&self) -> &[T] {
Expand All @@ -141,13 +115,28 @@ pub enum Type {
Char,
String,
List(Box<Type>),
Record(Box<[Type]>),
Tuple(TupleArray<Type>),
Variant(NonEmptyArray<Type>),

// Give records the ability to generate a generous amount of fields but
// don't let the fuzzer go too wild since `wasmparser`'s validator currently
// has hard limits in the 1000-ish range on the number of fields a record
// may contain.
Record(VecInRange<Type, 0, 200>),

// Tuples can only have up to 16 type parameters in wasmtime right now for
// the static API.
Tuple(VecInRange<Type, 0, 16>),

// Like records, allow a good number of variants, but variants require at
// least one case.
Variant(VecInRange<Type, 1, 200>),
Enum(UsizeInRange<1, 257>),
Union(NonEmptyArray<Type>),
Union(VecInRange<Type, 1, 200>),

Option(Box<Type>),
Expected { ok: Box<Type>, err: Box<Type> },

// Generate 0 flags all the way up to 65 flags which exercises the 0 to
// 3 x u32 cases.
Flags(UsizeInRange<0, 65>),
}

Expand Down

0 comments on commit 597eb6f

Please sign in to comment.