implement fuzzing for component types (#4537)
This addresses #4307. For the static API we generate 100 arbitrary test cases at build time, each of which includes 0-5 parameter types, a result type, and a WAT fragment containing an imported function and an exported function. The exported function calls the imported function, which is implemented by the host. At runtime, the fuzz test selects a test case at random and feeds it zero or more sets of arbitrary parameters and results, checking that values which flow host-to-guest and guest-to-host make the transition unchanged. The fuzz test for the dynamic API follows a similar pattern, the only difference being that test cases are generated at runtime. Signed-off-by: Joel Dice <joel.dice@fermyon.com>
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
use anyhow::Result;
|
||||
use component_test_util::{engine, TypedFuncExt};
|
||||
use std::fmt::Write;
|
||||
use std::iter;
|
||||
use wasmtime::component::{Component, ComponentParams, Lift, Lower, TypedFunc};
|
||||
use wasmtime::{AsContextMut, Config, Engine};
|
||||
use wasmtime::component::Component;
|
||||
use wasmtime_component_util::REALLOC_AND_FREE;
|
||||
|
||||
mod dynamic;
|
||||
mod func;
|
||||
@@ -12,97 +13,6 @@ mod macros;
|
||||
mod nested;
|
||||
mod post_return;
|
||||
|
||||
trait TypedFuncExt<P, R> {
|
||||
fn call_and_post_return(&self, store: impl AsContextMut, params: P) -> Result<R>;
|
||||
}
|
||||
|
||||
impl<P, R> TypedFuncExt<P, R> for TypedFunc<P, R>
|
||||
where
|
||||
P: ComponentParams + Lower,
|
||||
R: Lift,
|
||||
{
|
||||
fn call_and_post_return(&self, mut store: impl AsContextMut, params: P) -> Result<R> {
|
||||
let result = self.call(&mut store, params)?;
|
||||
self.post_return(&mut store)?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
// A simple bump allocator which can be used with modules
|
||||
const REALLOC_AND_FREE: &str = r#"
|
||||
(global $last (mut i32) (i32.const 8))
|
||||
(func $realloc (export "realloc")
|
||||
(param $old_ptr i32)
|
||||
(param $old_size i32)
|
||||
(param $align i32)
|
||||
(param $new_size i32)
|
||||
(result i32)
|
||||
|
||||
;; Test if the old pointer is non-null
|
||||
local.get $old_ptr
|
||||
if
|
||||
;; If the old size is bigger than the new size then
|
||||
;; this is a shrink and transparently allow it
|
||||
local.get $old_size
|
||||
local.get $new_size
|
||||
i32.gt_u
|
||||
if
|
||||
local.get $old_ptr
|
||||
return
|
||||
end
|
||||
|
||||
;; ... otherwise this is unimplemented
|
||||
unreachable
|
||||
end
|
||||
|
||||
;; align up `$last`
|
||||
(global.set $last
|
||||
(i32.and
|
||||
(i32.add
|
||||
(global.get $last)
|
||||
(i32.add
|
||||
(local.get $align)
|
||||
(i32.const -1)))
|
||||
(i32.xor
|
||||
(i32.add
|
||||
(local.get $align)
|
||||
(i32.const -1))
|
||||
(i32.const -1))))
|
||||
|
||||
;; save the current value of `$last` as the return value
|
||||
global.get $last
|
||||
|
||||
;; ensure anything necessary is set to valid data by spraying a bit
|
||||
;; pattern that is invalid
|
||||
global.get $last
|
||||
i32.const 0xde
|
||||
local.get $new_size
|
||||
memory.fill
|
||||
|
||||
;; bump our pointer
|
||||
(global.set $last
|
||||
(i32.add
|
||||
(global.get $last)
|
||||
(local.get $new_size)))
|
||||
)
|
||||
"#;
|
||||
|
||||
fn engine() -> Engine {
|
||||
drop(env_logger::try_init());
|
||||
|
||||
let mut config = Config::new();
|
||||
config.wasm_component_model(true);
|
||||
|
||||
// When pooling allocator tests are skipped it means we're in qemu. The
|
||||
// component model tests create a disproportionate number of instances so
|
||||
// try to cut down on virtual memory usage by avoiding 4G reservations.
|
||||
if crate::skip_pooling_allocator_tests() {
|
||||
config.static_memory_maximum_size(0);
|
||||
config.dynamic_memory_guard_size(0);
|
||||
}
|
||||
Engine::new(&config).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn components_importing_modules() -> Result<()> {
|
||||
let engine = engine();
|
||||
@@ -113,49 +23,49 @@ fn components_importing_modules() -> Result<()> {
|
||||
Component::new(
|
||||
&engine,
|
||||
r#"
|
||||
(component
|
||||
(import "" (core module))
|
||||
)
|
||||
(component
|
||||
(import "" (core module))
|
||||
)
|
||||
"#,
|
||||
)?;
|
||||
|
||||
Component::new(
|
||||
&engine,
|
||||
r#"
|
||||
(component
|
||||
(import "" (core module $m1
|
||||
(import "" "" (func))
|
||||
(import "" "x" (global i32))
|
||||
(component
|
||||
(import "" (core module $m1
|
||||
(import "" "" (func))
|
||||
(import "" "x" (global i32))
|
||||
|
||||
(export "a" (table 1 funcref))
|
||||
(export "b" (memory 1))
|
||||
(export "c" (func (result f32)))
|
||||
(export "d" (global i64))
|
||||
))
|
||||
(export "a" (table 1 funcref))
|
||||
(export "b" (memory 1))
|
||||
(export "c" (func (result f32)))
|
||||
(export "d" (global i64))
|
||||
))
|
||||
|
||||
(core module $m2
|
||||
(func (export ""))
|
||||
(global (export "x") i32 i32.const 0)
|
||||
)
|
||||
(core instance $i2 (instantiate (module $m2)))
|
||||
(core instance $i1 (instantiate (module $m1) (with "" (instance $i2))))
|
||||
|
||||
(core module $m3
|
||||
(import "mod" "1" (memory 1))
|
||||
(import "mod" "2" (table 1 funcref))
|
||||
(import "mod" "3" (global i64))
|
||||
(import "mod" "4" (func (result f32)))
|
||||
)
|
||||
|
||||
(core instance $i3 (instantiate (module $m3)
|
||||
(with "mod" (instance
|
||||
(export "1" (memory $i1 "b"))
|
||||
(export "2" (table $i1 "a"))
|
||||
(export "3" (global $i1 "d"))
|
||||
(export "4" (func $i1 "c"))
|
||||
))
|
||||
))
|
||||
(core module $m2
|
||||
(func (export ""))
|
||||
(global (export "x") i32 i32.const 0)
|
||||
)
|
||||
(core instance $i2 (instantiate (module $m2)))
|
||||
(core instance $i1 (instantiate (module $m1) (with "" (instance $i2))))
|
||||
|
||||
(core module $m3
|
||||
(import "mod" "1" (memory 1))
|
||||
(import "mod" "2" (table 1 funcref))
|
||||
(import "mod" "3" (global i64))
|
||||
(import "mod" "4" (func (result f32)))
|
||||
)
|
||||
|
||||
(core instance $i3 (instantiate (module $m3)
|
||||
(with "mod" (instance
|
||||
(export "1" (memory $i1 "b"))
|
||||
(export "2" (table $i1 "a"))
|
||||
(export "3" (global $i1 "d"))
|
||||
(export "4" (func $i1 "c"))
|
||||
))
|
||||
))
|
||||
)
|
||||
"#,
|
||||
)?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user