Skip to content

Commit 439de7f

Browse files
authored
Handle OOM in the rest of Wasmtime's non-component, -async, -compilation APIs (#12858)
* Handle OOM in more places in the public API A bunch of random places: * Add: `Trap::try_new` to handle OOM while creating traps * Use: `TryVec` inside `Func::call_impl_do_call` and `wasm_val_raw_storage` to hold the args and rets * Add: `Instance::try_exports` for iterating over an instance's exports while handling OOM * `Linker:try_get`, like `Linker::get` but handling OOM * `Linker:try_get_by_import`, like `Linker::get_by_import` but handling OOM * Use `try_new` to box things in `SharedMemory::new` * Use `TryVec` instead of `Vec` in our dynamic tables * Add OOM tests for most of Wasmtime's public API Excludes component-, async-, and compilation-related APIs. * address review feedback * fix test compilation * fix c-api
1 parent d70a517 commit 439de7f

28 files changed

Lines changed: 1512 additions & 112 deletions

File tree

crates/c-api/src/linker.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,11 +204,11 @@ pub unsafe extern "C" fn wasmtime_linker_get(
204204
Err(_) => return false,
205205
};
206206
match linker.get(store, module, name) {
207-
Some(which) => {
207+
Ok(which) => {
208208
crate::initialize(item_ptr, which.into());
209209
true
210210
}
211-
None => false,
211+
Err(_) => false,
212212
}
213213
}
214214

crates/core/src/alloc/vec.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,20 @@ impl<T> TryVec<T> {
191191
Ok(())
192192
}
193193

194+
/// Same as [`std::vec::Vec::resize_with`] but returns an error on
195+
/// allocation failure.
196+
pub fn resize_with<F>(&mut self, new_len: usize, f: F) -> Result<(), OutOfMemory>
197+
where
198+
F: FnMut() -> T,
199+
{
200+
let len = self.len();
201+
if new_len > len {
202+
self.reserve(new_len - len)?;
203+
}
204+
self.inner.resize_with(new_len, f);
205+
Ok(())
206+
}
207+
194208
/// Same as [`std::vec::Vec::retain`].
195209
pub fn retain<F>(&mut self, f: F)
196210
where

crates/fuzzing/tests/oom/caller.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#![cfg(arc_try_new)]
2+
3+
use wasmtime::{Config, Engine, Func, FuncType, Module, Result, Store};
4+
use wasmtime_fuzzing::oom::OomTest;
5+
6+
#[test]
7+
fn caller_get_export() -> Result<()> {
8+
let module_bytes = {
9+
let mut config = Config::new();
10+
config.concurrency_support(false);
11+
let engine = Engine::new(&config)?;
12+
Module::new(
13+
&engine,
14+
r#"(module
15+
(import "" "f" (func))
16+
(memory (export "m") 1)
17+
(func (export "run") (call 0))
18+
)"#,
19+
)?
20+
.serialize()?
21+
};
22+
let mut config = Config::new();
23+
config.enable_compiler(false);
24+
config.concurrency_support(false);
25+
let engine = Engine::new(&config)?;
26+
let module = unsafe { Module::deserialize(&engine, &module_bytes)? };
27+
28+
OomTest::new().test(|| {
29+
let mut store = Store::try_new(&engine, ())?;
30+
let host_func = Func::try_new(
31+
&mut store,
32+
FuncType::try_new(&engine, [], [])?,
33+
|mut caller, _params, _results| {
34+
let mem = caller.get_export("m");
35+
assert!(mem.is_some());
36+
Ok(())
37+
},
38+
)?;
39+
let instance = wasmtime::Instance::new(&mut store, &module, &[host_func.into()])?;
40+
let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;
41+
run.call(&mut store, ())?;
42+
Ok(())
43+
})
44+
}
45+
46+
#[test]
47+
fn caller_data() -> Result<()> {
48+
let module_bytes = {
49+
let mut config = Config::new();
50+
config.concurrency_support(false);
51+
let engine = Engine::new(&config)?;
52+
Module::new(
53+
&engine,
54+
r#"(module
55+
(import "" "f" (func))
56+
(func (export "run") (call 0))
57+
)"#,
58+
)?
59+
.serialize()?
60+
};
61+
let mut config = Config::new();
62+
config.enable_compiler(false);
63+
config.concurrency_support(false);
64+
let engine = Engine::new(&config)?;
65+
let module = unsafe { Module::deserialize(&engine, &module_bytes)? };
66+
67+
OomTest::new().test(|| {
68+
let mut store = Store::try_new(&engine, 42u32)?;
69+
let host_func = Func::try_new(
70+
&mut store,
71+
FuncType::try_new(&engine, [], [])?,
72+
|caller, _params, _results| {
73+
assert_eq!(*caller.data(), 42u32);
74+
Ok(())
75+
},
76+
)?;
77+
let instance = wasmtime::Instance::new(&mut store, &module, &[host_func.into()])?;
78+
let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;
79+
run.call(&mut store, ())?;
80+
Ok(())
81+
})
82+
}
83+
84+
#[test]
85+
fn caller_engine() -> Result<()> {
86+
let module_bytes = {
87+
let mut config = Config::new();
88+
config.concurrency_support(false);
89+
let engine = Engine::new(&config)?;
90+
Module::new(
91+
&engine,
92+
r#"(module
93+
(import "" "f" (func))
94+
(func (export "run") (call 0))
95+
)"#,
96+
)?
97+
.serialize()?
98+
};
99+
let mut config = Config::new();
100+
config.enable_compiler(false);
101+
config.concurrency_support(false);
102+
let engine = Engine::new(&config)?;
103+
let module = unsafe { Module::deserialize(&engine, &module_bytes)? };
104+
105+
OomTest::new().test(|| {
106+
let mut store = Store::try_new(&engine, ())?;
107+
let host_func = Func::try_new(
108+
&mut store,
109+
FuncType::try_new(&engine, [], [])?,
110+
|caller, _params, _results| {
111+
let _engine = caller.engine();
112+
Ok(())
113+
},
114+
)?;
115+
let instance = wasmtime::Instance::new(&mut store, &module, &[host_func.into()])?;
116+
let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;
117+
run.call(&mut store, ())?;
118+
Ok(())
119+
})
120+
}

crates/fuzzing/tests/oom/func.rs

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![cfg(arc_try_new)]
22

3-
use wasmtime::{Config, Engine, Func, Result, Store};
3+
use wasmtime::{Config, Engine, Func, FuncType, Linker, Module, Result, Store, Val, ValType};
44
use wasmtime_fuzzing::oom::OomTest;
55

66
#[test]
@@ -16,3 +16,99 @@ fn func_new() -> Result<()> {
1616
Ok(())
1717
})
1818
}
19+
20+
#[test]
21+
fn func_new_with_type() -> Result<()> {
22+
let mut config = Config::new();
23+
config.enable_compiler(false);
24+
config.concurrency_support(false);
25+
let engine = Engine::new(&config)?;
26+
27+
OomTest::new().test(|| {
28+
let mut store = Store::try_new(&engine, ())?;
29+
let ty = FuncType::try_new(&engine, [ValType::I32], [ValType::I32])?;
30+
let _func = Func::try_new(&mut store, ty, |_caller, params, results| {
31+
results[0] = params[0].clone();
32+
Ok(())
33+
})?;
34+
Ok(())
35+
})
36+
}
37+
38+
#[test]
39+
fn func_call() -> Result<()> {
40+
let module_bytes = {
41+
let mut config = Config::new();
42+
config.concurrency_support(false);
43+
let engine = Engine::new(&config)?;
44+
Module::new(
45+
&engine,
46+
r#"(module (func (export "id") (param i32) (result i32) (local.get 0)))"#,
47+
)?
48+
.serialize()?
49+
};
50+
let mut config = Config::new();
51+
config.enable_compiler(false);
52+
config.concurrency_support(false);
53+
let engine = Engine::new(&config)?;
54+
let module = unsafe { Module::deserialize(&engine, &module_bytes)? };
55+
let linker = Linker::<()>::new(&engine);
56+
let instance_pre = linker.instantiate_pre(&module)?;
57+
58+
OomTest::new().test(|| {
59+
let mut store = Store::try_new(&engine, ())?;
60+
let instance = instance_pre.instantiate(&mut store)?;
61+
let id = instance.get_func(&mut store, "id").unwrap();
62+
let mut results = [Val::I32(0)];
63+
id.call(&mut store, &[Val::I32(42)], &mut results)?;
64+
assert_eq!(results[0].unwrap_i32(), 42);
65+
Ok(())
66+
})
67+
}
68+
69+
#[test]
70+
fn func_typed() -> Result<()> {
71+
let module_bytes = {
72+
let mut config = Config::new();
73+
config.concurrency_support(false);
74+
let engine = Engine::new(&config)?;
75+
Module::new(
76+
&engine,
77+
r#"(module (func (export "id") (param i32) (result i32) (local.get 0)))"#,
78+
)?
79+
.serialize()?
80+
};
81+
let mut config = Config::new();
82+
config.enable_compiler(false);
83+
config.concurrency_support(false);
84+
let engine = Engine::new(&config)?;
85+
let module = unsafe { Module::deserialize(&engine, &module_bytes)? };
86+
let linker = Linker::<()>::new(&engine);
87+
let instance_pre = linker.instantiate_pre(&module)?;
88+
89+
OomTest::new().test(|| {
90+
let mut store = Store::try_new(&engine, ())?;
91+
let instance = instance_pre.instantiate(&mut store)?;
92+
let id = instance.get_typed_func::<i32, i32>(&mut store, "id")?;
93+
let result = id.call(&mut store, 42)?;
94+
assert_eq!(result, 42);
95+
Ok(())
96+
})
97+
}
98+
99+
#[test]
100+
fn func_ty() -> Result<()> {
101+
let mut config = Config::new();
102+
config.enable_compiler(false);
103+
config.concurrency_support(false);
104+
let engine = Engine::new(&config)?;
105+
106+
OomTest::new().test(|| {
107+
let mut store = Store::try_new(&engine, ())?;
108+
let func = Func::try_wrap(&mut store, |x: i32| x * 2)?;
109+
let ty = func.ty(&store);
110+
assert_eq!(ty.params().len(), 1);
111+
assert_eq!(ty.results().len(), 1);
112+
Ok(())
113+
})
114+
}

crates/fuzzing/tests/oom/global.rs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![cfg(arc_try_new)]
22

3-
use wasmtime::{Config, Engine, GlobalType, Mutability, Result, Store, Val, ValType};
3+
use wasmtime::{Config, Engine, Global, GlobalType, Mutability, Result, Store, Val, ValType};
44
use wasmtime_fuzzing::oom::OomTest;
55

66
#[test]
@@ -17,3 +17,55 @@ fn global_new() -> Result<()> {
1717
Ok(())
1818
})
1919
}
20+
21+
#[test]
22+
fn global_get() -> Result<()> {
23+
let mut config = Config::new();
24+
config.enable_compiler(false);
25+
config.concurrency_support(false);
26+
let engine = Engine::new(&config)?;
27+
28+
OomTest::new().test(|| {
29+
let mut store = Store::try_new(&engine, ())?;
30+
let ty = GlobalType::new(ValType::I32, Mutability::Var);
31+
let global = Global::new(&mut store, ty, Val::I32(42))?;
32+
let val = global.get(&mut store);
33+
assert_eq!(val.unwrap_i32(), 42);
34+
Ok(())
35+
})
36+
}
37+
38+
#[test]
39+
fn global_set() -> Result<()> {
40+
let mut config = Config::new();
41+
config.enable_compiler(false);
42+
config.concurrency_support(false);
43+
let engine = Engine::new(&config)?;
44+
45+
OomTest::new().test(|| {
46+
let mut store = Store::try_new(&engine, ())?;
47+
let ty = GlobalType::new(ValType::I32, Mutability::Var);
48+
let global = Global::new(&mut store, ty, Val::I32(42))?;
49+
global.set(&mut store, Val::I32(99))?;
50+
let val = global.get(&mut store);
51+
assert_eq!(val.unwrap_i32(), 99);
52+
Ok(())
53+
})
54+
}
55+
56+
#[test]
57+
fn global_ty() -> Result<()> {
58+
let mut config = Config::new();
59+
config.enable_compiler(false);
60+
config.concurrency_support(false);
61+
let engine = Engine::new(&config)?;
62+
63+
OomTest::new().test(|| {
64+
let mut store = Store::try_new(&engine, ())?;
65+
let ty = GlobalType::new(ValType::I32, Mutability::Var);
66+
let global = Global::new(&mut store, ty, Val::I32(42))?;
67+
let ty = global.ty(&store);
68+
assert!(ty.content().is_i32());
69+
Ok(())
70+
})
71+
}

0 commit comments

Comments
 (0)