Limit the type hierarchies in component fuzzing (#4668)

* 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
This commit is contained in:
Alex Crichton
2022-08-10 15:49:51 -05:00
committed by GitHub
parent 54f9587569
commit 597eb6f4ce

View File

@@ -25,10 +25,6 @@ pub const IMPORT_FUNCTION: &str = "echo";
/// The name of the exported guest function which the host should call /// The name of the exported guest function which the host should call
pub const EXPORT_FUNCTION: &str = "echo"; 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)] #[derive(Copy, Clone, PartialEq, Eq)]
enum CoreType { enum CoreType {
I32, I32,
@@ -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 /// 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 /// or equal to the longest tuple for which Wasmtime generates a `ComponentType` impl
#[derive(Debug)] #[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> { fn arbitrary(input: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Self( let mut ret = Vec::new();
input input.arbitrary_loop(Some(L), Some(H), |input| {
.arbitrary_iter()? ret.push(input.arbitrary()?);
.take(MAX_TUPLE_LENGTH) Ok(std::ops::ControlFlow::Continue(()))
.collect::<arbitrary::Result<_>>()?, })?;
)) 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]; type Target = [T];
fn deref(&self) -> &[T] { fn deref(&self) -> &[T] {
@@ -141,13 +115,28 @@ pub enum Type {
Char, Char,
String, String,
List(Box<Type>), List(Box<Type>),
Record(Box<[Type]>),
Tuple(TupleArray<Type>), // Give records the ability to generate a generous amount of fields but
Variant(NonEmptyArray<Type>), // 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>), Enum(UsizeInRange<1, 257>),
Union(NonEmptyArray<Type>), Union(VecInRange<Type, 1, 200>),
Option(Box<Type>), Option(Box<Type>),
Expected { ok: Box<Type>, err: 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>), Flags(UsizeInRange<0, 65>),
} }