Skip to content
40 changes: 40 additions & 0 deletions crates/fuzzing/tests/oom/component_component.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#![cfg(arc_try_new)]

use wasmtime::component::Component;
use wasmtime::{Config, Engine, Result};
use wasmtime_fuzzing::oom::OomTest;

#[test]
fn component_serialize() -> Result<()> {
let component_bytes = {
let mut config = Config::new();
config.concurrency_support(false);
let engine = Engine::new(&config)?;
Component::new(
&engine,
r#"
(component
(core module $m
(func (export "id") (param i32) (result i32) (local.get 0))
)
(core instance $i (instantiate $m))
(func (export "id") (param "x" s32) (result s32)
(canon lift (core func $i "id"))
)
)
"#,
)?
.serialize()?
};
let mut config = Config::new();
config.enable_compiler(false);
config.concurrency_support(false);
let engine = Engine::new(&config)?;
let component = unsafe { Component::deserialize(&engine, &component_bytes)? };

// Error propagation via anyhow allocates after OOM.
OomTest::new().allow_alloc_after_oom(true).test(|| {
let _bytes = component.serialize()?;
Ok(())
})
}
39 changes: 39 additions & 0 deletions crates/fuzzing/tests/oom/component_func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,3 +246,42 @@ fn component_typed_func_call() -> Result<()> {
Ok(())
})
}

#[test]
fn component_func_typed() -> Result<()> {
let component_bytes = {
let mut config = Config::new();
config.concurrency_support(false);
let engine = Engine::new(&config)?;
Component::new(
&engine,
r#"
(component
(core module $m
(func (export "id") (param i32) (result i32) (local.get 0))
)
(core instance $i (instantiate $m))
(func (export "id") (param "x" s32) (result s32)
(canon lift (core func $i "id"))
)
)
"#,
)?
.serialize()?
};
let mut config = Config::new();
config.enable_compiler(false);
config.concurrency_support(false);
let engine = Engine::new(&config)?;
let component = unsafe { Component::deserialize(&engine, &component_bytes)? };
let linker = Linker::<()>::new(&engine);
let instance_pre = linker.instantiate_pre(&component)?;

OomTest::new().test(|| {
let mut store = Store::try_new(&engine, ())?;
let instance = instance_pre.instantiate(&mut store)?;
let func = instance.get_func(&mut store, "id").unwrap();
let _typed = func.typed::<(i32,), (i32,)>(&store)?;
Ok(())
})
}
38 changes: 38 additions & 0 deletions crates/fuzzing/tests/oom/component_instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,41 @@ fn instantiate_in_pooling_allocator() -> Result<()> {
Ok(())
})
}

#[test]
fn component_instance_get_export() -> Result<()> {
let component_bytes = {
let mut config = Config::new();
config.concurrency_support(false);
let engine = Engine::new(&config)?;
Component::new(
&engine,
r#"
(component
(core module $m
(func (export "id") (param i32) (result i32) (local.get 0))
)
(core instance $i (instantiate $m))
(func (export "id") (param "x" s32) (result s32)
(canon lift (core func $i "id"))
)
)
"#,
)?
.serialize()?
};
let mut config = Config::new();
config.enable_compiler(false);
config.concurrency_support(false);
let engine = Engine::new(&config)?;
let component = unsafe { Component::deserialize(&engine, &component_bytes)? };
let linker = Linker::<()>::new(&engine);
let instance_pre = linker.instantiate_pre(&component)?;

OomTest::new().test(|| {
let mut store = Store::try_new(&engine, ())?;
let instance = instance_pre.instantiate(&mut store)?;
let _export = instance.get_export(&mut store, None, "id");
Ok(())
})
}
104 changes: 102 additions & 2 deletions crates/fuzzing/tests/oom/component_linker.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![cfg(arc_try_new)]

use wasmtime::component::{Component, Linker};
use wasmtime::{Config, Engine, Result, Store};
use wasmtime::component::{Component, Linker, ResourceType};
use wasmtime::{Config, Engine, Module, Result, Store};
use wasmtime_fuzzing::oom::OomTest;

#[tokio::test]
Expand Down Expand Up @@ -147,3 +147,103 @@ fn component_linker_substituted_component_type() -> Result<()> {
Ok(())
})
}

#[test]
fn component_linker_define_unknown_imports_as_traps() -> Result<()> {
let component_bytes = {
let mut config = Config::new();
config.concurrency_support(false);
let engine = Engine::new(&config)?;
Component::new(
&engine,
r#"
(component
(import "f" (func))
)
"#,
)?
.serialize()?
};
let mut config = Config::new();
config.enable_compiler(false);
config.concurrency_support(false);
let engine = Engine::new(&config)?;
let component = unsafe { Component::deserialize(&engine, &component_bytes)? };

// Error propagation via anyhow allocates after OOM.
OomTest::new().allow_alloc_after_oom(true).test(|| {
let mut linker = Linker::<()>::new(&engine);
linker.define_unknown_imports_as_traps(&component)?;
Ok(())
})
}

#[test]
fn component_linker_instance_func_wrap() -> Result<()> {
let mut config = Config::new();
config.concurrency_support(false);
let engine = Engine::new(&config)?;

// Error propagation via anyhow allocates after OOM.
OomTest::new().allow_alloc_after_oom(true).test(|| {
let mut linker = Linker::<()>::new(&engine);
linker
.root()
.func_wrap("f", |_cx: wasmtime::StoreContextMut<'_, ()>, (): ()| Ok(()))?;
Ok(())
})
}

#[test]
fn component_linker_instance_func_new() -> Result<()> {
let mut config = Config::new();
config.concurrency_support(false);
let engine = Engine::new(&config)?;

// Error propagation via anyhow allocates after OOM.
OomTest::new().allow_alloc_after_oom(true).test(|| {
let mut linker = Linker::<()>::new(&engine);
linker
.root()
.func_new("f", |_cx, _func_ty, _params, _results| Ok(()))?;
Ok(())
})
}

#[test]
fn component_linker_instance_module() -> Result<()> {
let module_bytes = {
let mut config = Config::new();
config.concurrency_support(false);
let engine = Engine::new(&config)?;
Module::new(&engine, "(module)")?.serialize()?
};
let mut config = Config::new();
config.enable_compiler(false);
config.concurrency_support(false);
let engine = Engine::new(&config)?;
let module = unsafe { Module::deserialize(&engine, &module_bytes)? };

// Error propagation via anyhow allocates after OOM.
OomTest::new().allow_alloc_after_oom(true).test(|| {
let mut linker = Linker::<()>::new(&engine);
linker.root().module("m", &module)?;
Ok(())
})
}

#[test]
fn component_linker_instance_resource() -> Result<()> {
let mut config = Config::new();
config.concurrency_support(false);
let engine = Engine::new(&config)?;

// Error propagation from HostFunc::wrap allocates via anyhow.
OomTest::new().allow_alloc_after_oom(true).test(|| {
let mut linker = Linker::<()>::new(&engine);
linker
.root()
.resource("r", ResourceType::host::<()>(), |_, _| Ok(()))?;
Ok(())
})
}
80 changes: 79 additions & 1 deletion crates/fuzzing/tests/oom/component_resource.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![cfg(arc_try_new)]

use wasmtime::component::{Component, Linker, ResourceAny};
use wasmtime::component::{Component, Linker, Resource, ResourceAny, ResourceType};
use wasmtime::{Config, Engine, Result, Store};
use wasmtime_fuzzing::oom::OomTest;

Expand Down Expand Up @@ -87,3 +87,81 @@ fn component_resource_any_resource_drop() -> Result<()> {
Ok(())
})
}

struct MyResource;

#[test]
fn component_resource_any_try_from_resource() -> Result<()> {
let component_bytes = {
let mut config = Config::new();
config.concurrency_support(false);
let engine = Engine::new(&config)?;
Component::new(
&engine,
r#"
(component
(import "t" (type $t (sub resource)))
)
"#,
)?
.serialize()?
};
let mut config = Config::new();
config.enable_compiler(false);
config.concurrency_support(false);
let engine = Engine::new(&config)?;
let component = unsafe { Component::deserialize(&engine, &component_bytes)? };
let mut linker = Linker::<()>::new(&engine);
linker
.root()
.resource("t", ResourceType::host::<MyResource>(), |_, _| Ok(()))?;
let instance_pre = linker.instantiate_pre(&component)?;

// Error propagation via anyhow allocates after OOM.
OomTest::new().allow_alloc_after_oom(true).test(|| {
let mut store = Store::try_new(&engine, ())?;
let _instance = instance_pre.instantiate(&mut store)?;
let resource = Resource::<MyResource>::new_own(42);
let any = ResourceAny::try_from_resource(resource, &mut store)?;
let _typed: Resource<MyResource> = any.try_into_resource(&mut store)?;
Ok(())
})
}

#[test]
fn component_resource_any_try_into_resource() -> Result<()> {
let component_bytes = {
let mut config = Config::new();
config.concurrency_support(false);
let engine = Engine::new(&config)?;
Component::new(
&engine,
r#"
(component
(import "t" (type $t (sub resource)))
)
"#,
)?
.serialize()?
};
let mut config = Config::new();
config.enable_compiler(false);
config.concurrency_support(false);
let engine = Engine::new(&config)?;
let component = unsafe { Component::deserialize(&engine, &component_bytes)? };
let mut linker = Linker::<()>::new(&engine);
linker
.root()
.resource("t", ResourceType::host::<MyResource>(), |_, _| Ok(()))?;
let instance_pre = linker.instantiate_pre(&component)?;

// Error propagation via anyhow allocates after OOM.
OomTest::new().allow_alloc_after_oom(true).test(|| {
let mut store = Store::try_new(&engine, ())?;
let _instance = instance_pre.instantiate(&mut store)?;
let resource = Resource::<MyResource>::new_own(100);
let any = ResourceAny::try_from_resource(resource, &mut store)?;
let _back: Resource<MyResource> = any.try_into_resource(&mut store)?;
Ok(())
})
}
1 change: 1 addition & 0 deletions crates/fuzzing/tests/oom/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod bit_set;
mod boxed;
mod btree_map;
mod caller;
mod component_component;
mod component_func;
mod component_instance;
mod component_linker;
Expand Down
6 changes: 5 additions & 1 deletion crates/wasmtime/src/runtime/component/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,11 @@ impl Component {
/// [`Module::serialize`]: crate::Module::serialize
/// [`Module`]: crate::Module
pub fn serialize(&self) -> Result<Vec<u8>> {
Ok(self.engine_code().image().to_vec())
let image = self.engine_code().image();
let mut v = TryVec::new();
v.reserve(image.len())?;
v.try_extend(image.iter().copied())?;
Ok(v.into())
}

/// Creates a new `VMFuncRef` with all fields filled out for the destructor
Expand Down
8 changes: 4 additions & 4 deletions crates/wasmtime/src/runtime/component/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -757,10 +757,10 @@ impl<T: 'static> LinkerInstance<'_, T> {
ty: ResourceType,
dtor: impl Fn(StoreContextMut<'_, T>, u32) -> Result<()> + Send + Sync + 'static,
) -> Result<()> {
let dtor = Arc::new(crate::func::HostFunc::wrap(
let dtor = try_new::<Arc<_>>(crate::func::HostFunc::wrap(
&self.engine,
move |mut cx: crate::Caller<'_, T>, (param,): (u32,)| dtor(cx.as_context_mut(), param),
)?);
)?)?;
self.insert(name, Definition::Resource(ty, dtor))?;
Ok(())
}
Expand All @@ -775,10 +775,10 @@ impl<T: 'static> LinkerInstance<'_, T> {
+ Sync
+ 'static,
{
let dtor = Arc::new(crate::func::HostFunc::wrap_async(
let dtor = try_new::<Arc<_>>(crate::func::HostFunc::wrap_async(
&self.engine,
move |cx: crate::Caller<'_, T>, (param,): (u32,)| dtor(cx.into(), param),
)?);
)?)?;
self.insert(name, Definition::Resource(ty, dtor))?;
Ok(())
}
Expand Down
Loading