Skip to content

Commit 42e4093

Browse files
authored
Add docs for OOM-handling in Wasmtime (#13224)
* Expand the docs the `OutOfMemory` error And use this as a place to document which portions of Wasmtime attempt to handle OOM, how to enable `cfg(arc_try_new)` to handle more OOMs with a nightly rust toolchain, and clarify that OOM handling is not tier 1. * Add individual API docs for functions that will handle OOM * Address review feedback
1 parent 2f3082a commit 42e4093

20 files changed

Lines changed: 440 additions & 4 deletions

File tree

crates/core/src/error/oom.rs

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,95 @@
11
use crate::error::{Error, OomOrDynError};
22
use core::{fmt, mem, ptr::NonNull};
33

4-
/// Out-of-memory error.
4+
/// An out-of-memory (OOM) error.
55
///
66
/// This error is the sentinel for allocation failure due to memory exhaustion.
77
///
8-
/// Constructing an [`Error`] from an `OutOfMemory` does not
9-
/// allocate.
8+
/// Constructing an [`Error`] from an `OutOfMemory` does not allocate.
109
///
1110
/// Allocation failure inside any `Error` method that must allocate
1211
/// (e.g. [`Error::context`]) will propagate an `OutOfMemory` error.
12+
///
13+
/// # Out-of-Memory Handling in Wasmtime
14+
///
15+
/// Wasmtime performs out-of-memory (OOM) error handling on a **best-effort
16+
/// basis**. OOM handling does not have [tier 1
17+
/// support](https://docs.wasmtime.dev/stability-tiers.html) at this time and,
18+
/// therefore, while failure to handle OOM at some allocation site may be
19+
/// considered a bug, and might be a potential denial-of-service vector, it
20+
/// would not be considered a security vulnerability.[^limits]
21+
///
22+
/// [^limits]: Note that unconstrained guest-controlled resource usage is still
23+
/// considered a vulnerability. Wasmtime has tier 1 support for limiting guest
24+
/// resources, but not for handling OOMs within those limits.
25+
///
26+
/// ## Where Wasmtime Attempts to Handle OOM
27+
///
28+
/// It is important to note that **not all portions of Wasmtime attempt to
29+
/// handle out-of-memory errors**. Notably, Wasmtime only ever attempts to
30+
/// handle OOM in the core *runtime* and never in the *compiler*. No attempt is
31+
/// made to handle allocation failure in the middle of compiling new `Module`s
32+
/// or `Component`s from Wasm to machine code (or Pulley bytecode). However,
33+
/// Wasmtime will attempt to handle OOM when running *pre-compiled* Wasm code
34+
/// (loaded via `Module::deserialize` or `Component::deserialize`).
35+
///
36+
/// Wasmtime's interfaces allow *you* to handle OOM in your own embedding's WASI
37+
/// implementations and host APIs, but Wasmtime's provided WASI implementations
38+
/// (e.g. `wasmtime_wasi_http`) will generally not attempt to handle OOM (as
39+
/// they often depend on third-party crates that do not attempt to handle OOM).
40+
///
41+
/// The API documentation for individual functions and methods that handle OOM
42+
/// should generally document this fact by listing `OutOfMemory` as one of the
43+
/// potential errors returned.
44+
///
45+
/// | **Where** | **Handles OOM?** |
46+
/// |-------------------------------------------------|---------------------------------|
47+
/// | **Compiler** | **No** |
48+
/// |  `wasmtime::Module::new` | No |
49+
/// |  `wasmtime::Component::new` | No |
50+
/// |  `wasmtime::CodeBuilder` | No |
51+
/// |  Other compilation APIs... | No |
52+
/// | **Runtime** | **Yes** |
53+
/// |  `wasmtime::Store` | Yes |
54+
/// |  `wasmtime::Linker` | Yes |
55+
/// |  `wasmtime::Module::deserialize` | Yes |
56+
/// |  `wasmtime::Instance` | Yes |
57+
/// |  `wasmtime::Func::call` | Yes |
58+
/// |  Component Model concurrency/async APIs | Not yet |
59+
/// |  Other instantiation and execution APIs... | Yes |
60+
/// | **WASI Implementations and Host APIs** | **Depends** |
61+
/// |  `wasmtime_wasi` | No |
62+
/// |  `wasmtime_wasi_http` | No |
63+
/// |  `wasmtime_wasi_*` | No |
64+
/// |  Your embedding's APIs | If *you* implement OOM handling |
65+
///
66+
/// If you encounter an unhandled OOM inside Wasmtime, and it is within a
67+
/// portion of code where it should be handled, then please [file an
68+
/// issue](https://github.com/bytecodealliance/wasmtime/issues/new/choose).
69+
///
70+
/// ## Handling More OOMs with Rust Nightly APIs
71+
///
72+
/// Rust's standard library provides fallible allocation APIs, or the necessary
73+
/// building blocks for making our own fallible allocation APIs, for some of its
74+
/// types and collections. For example, it provides `Vec::try_reserve` which can
75+
/// be used to build a fallible version of `Vec::push` and fallible `Box`
76+
/// allocation can be built upon raw allocations from the global allocator and
77+
/// `Box::from_raw`.
78+
///
79+
/// However, the standard library does not provide these things for all the
80+
/// types and collections that Wasmtime uses. Some of these APIs are completely
81+
/// missing (such as a fallible version of
82+
/// `std::collections::hash_map::VacantEntry::insert`) and some APIs exist but
83+
/// are feature-gated on unstable, nightly-only Rust features. The most relevant
84+
/// API from this latter category is
85+
/// [`Arc::try_new`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.try_new),
86+
/// as Wasmtime's runtime uses a number of `Arc`s under the covers.
87+
///
88+
/// If handling OOMs is important for your Wasmtime embedding, then you should
89+
/// compile Wasmtime from source using a Nightly Rust toolchain and with the
90+
/// `RUSTFLAGS="--cfg arc_try_new"` environment variable set. This unlocks
91+
/// Wasmtime's internal usage of `Arc::try_new`, making more OOM handling at
92+
/// more allocation sites possible.
1393
#[derive(Clone, Copy)]
1494
// NB: `OutOfMemory`'s representation must be the same as `OomOrDynError`
1595
// (and therefore also `Error`).

crates/wasmtime/src/engine.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ impl Engine {
101101
/// For example, feature `reference_types` will need to set
102102
/// the compiler setting `unwind_info` to `true`, but explicitly
103103
/// disable these two compiler settings will cause errors.
104+
///
105+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
106+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
107+
/// details on Wasmtime's out-of-memory handling.
104108
pub fn new(config: &Config) -> Result<Engine> {
105109
let config = config.clone();
106110
let (mut tunables, features) = config.validate()?;

crates/wasmtime/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ pub use wasmtime_environ::{FuncIndex, StaticModuleIndex};
520520
#[cfg(feature = "anyhow")]
521521
pub use wasmtime_environ::anyhow;
522522

523-
pub use self::error::{Error, Result, bail, ensure, format_err};
523+
pub use self::error::{Error, OutOfMemory, Result, bail, ensure, format_err};
524524

525525
/// A re-exported instance of Wasmtime's `wasmparser` dependency.
526526
///

crates/wasmtime/src/runtime/component/component.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,12 @@ impl Component {
206206
///
207207
/// For more information see the [`Module::deserialize`] method.
208208
///
209+
/// # Errors
210+
///
211+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
212+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
213+
/// details on Wasmtime's out-of-memory handling.
214+
///
209215
/// # Unsafety
210216
///
211217
/// The unsafety of this method is the same as that of the

crates/wasmtime/src/runtime/component/func.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ impl Func {
9191
/// If the function does not actually take `Params` as its parameters or
9292
/// return `Return` then an error will be returned.
9393
///
94+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
95+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
96+
/// details on Wasmtime's out-of-memory handling.
97+
///
9498
/// # Panics
9599
///
96100
/// This function will panic if `self` is not owned by the `store`
@@ -223,6 +227,10 @@ impl Func {
223227
/// See [`TypedFunc::call`] for more information in addition to
224228
/// [`wasmtime::Func::call`](crate::Func::call).
225229
///
230+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
231+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
232+
/// details on Wasmtime's out-of-memory handling.
233+
///
226234
/// # Panics
227235
///
228236
/// Panics if `store` does not own this function.
@@ -240,6 +248,12 @@ impl Func {
240248

241249
/// Exactly like [`Self::call`] except for use on async stores.
242250
///
251+
/// # Errors
252+
///
253+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
254+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
255+
/// details on Wasmtime's out-of-memory handling.
256+
///
243257
/// # Panics
244258
///
245259
/// Panics if `store` does not own this function.

crates/wasmtime/src/runtime/component/func/typed.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ where
140140
/// information to understand which part of the canonical ABI went wrong
141141
/// and what to inspect.
142142
///
143+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
144+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
145+
/// details on Wasmtime's out-of-memory handling.
146+
///
143147
/// # Panics
144148
///
145149
/// Panics if `store` does not own this function.
@@ -152,6 +156,12 @@ where
152156
/// Exactly like [`Self::call`], except for invoking WebAssembly
153157
/// [asynchronously](crate#async).
154158
///
159+
/// # Errors
160+
///
161+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
162+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
163+
/// details on Wasmtime's out-of-memory handling.
164+
///
155165
/// # Panics
156166
///
157167
/// Panics if `store` does not own this function.

crates/wasmtime/src/runtime/component/instance.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,10 @@ impl Instance {
182182
/// Returns an error if `name` isn't a function export or if the export's
183183
/// type did not match `Params` or `Results`
184184
///
185+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
186+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
187+
/// details on Wasmtime's out-of-memory handling.
188+
///
185189
/// # Panics
186190
///
187191
/// Panics if `store` does not own this instance.
@@ -1159,6 +1163,12 @@ impl<T: 'static> InstancePre<T> {
11591163
}
11601164

11611165
/// Performs the instantiation process into the store specified.
1166+
///
1167+
/// # Errors
1168+
///
1169+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
1170+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
1171+
/// details on Wasmtime's out-of-memory handling.
11621172
//
11631173
// TODO: needs more docs
11641174
pub fn instantiate(&self, mut store: impl AsContextMut<Data = T>) -> Result<Instance> {
@@ -1175,6 +1185,12 @@ impl<T: 'static> InstancePre<T> {
11751185
/// Performs the instantiation process into the store specified.
11761186
///
11771187
/// Exactly like [`Self::instantiate`] except for use on async stores.
1188+
///
1189+
/// # Errors
1190+
///
1191+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
1192+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
1193+
/// details on Wasmtime's out-of-memory handling.
11781194
//
11791195
// TODO: needs more docs
11801196
#[cfg(feature = "async")]

crates/wasmtime/src/runtime/component/linker.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,12 @@ impl<T: 'static> Linker<T> {
186186

187187
/// Returns the [`types::Component`] corresponding to `component` with resource
188188
/// types imported by it replaced using imports present in [`Self`].
189+
///
190+
/// # Errors
191+
///
192+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
193+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
194+
/// details on Wasmtime's out-of-memory handling.
189195
pub fn substituted_component_type(&self, component: &Component) -> Result<types::Component> {
190196
let cx = self.typecheck(&component)?;
191197
Ok(types::Component::from(
@@ -215,6 +221,10 @@ impl<T: 'static> Linker<T> {
215221
/// Returns an error if this linker doesn't define a name that the
216222
/// `component` imports or if a name defined doesn't match the type of the
217223
/// item imported by the `component` provided.
224+
///
225+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
226+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
227+
/// details on Wasmtime's out-of-memory handling.
218228
pub fn instantiate_pre(&self, component: &Component) -> Result<InstancePre<T>> {
219229
let cx = self.typecheck(&component)?;
220230

@@ -279,6 +289,10 @@ impl<T: 'static> Linker<T> {
279289
/// `component` requires or if it is of the wrong type. Additionally this
280290
/// can return an error if something goes wrong during instantiation such as
281291
/// a runtime trap or a runtime limit being exceeded.
292+
///
293+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
294+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
295+
/// details on Wasmtime's out-of-memory handling.
282296
pub fn instantiate(
283297
&self,
284298
mut store: impl AsContextMut<Data = T>,
@@ -300,6 +314,10 @@ impl<T: 'static> Linker<T> {
300314
/// `component` requires or if it is of the wrong type. Additionally this
301315
/// can return an error if something goes wrong during instantiation such as
302316
/// a runtime trap or a runtime limit being exceeded.
317+
///
318+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
319+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
320+
/// details on Wasmtime's out-of-memory handling.
303321
#[cfg(feature = "async")]
304322
pub async fn instantiate_async(
305323
&self,
@@ -318,6 +336,12 @@ impl<T: 'static> Linker<T> {
318336
///
319337
/// By default a [`Linker`] will error when unknown imports are encountered when instantiating a [`Component`].
320338
/// This changes this behavior from an instant error to a trap that will happen if the import is called.
339+
///
340+
/// # Errors
341+
///
342+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
343+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
344+
/// details on Wasmtime's out-of-memory handling.
321345
pub fn define_unknown_imports_as_traps(&mut self, component: &Component) -> Result<()> {
322346
use wasmtime_environ::component::ComponentTypes;
323347
use wasmtime_environ::component::TypeDef;
@@ -433,6 +457,12 @@ impl<T: 'static> LinkerInstance<'_, T> {
433457
/// guest, see the [`func_wrap_async`] method.
434458
///
435459
/// [`func_wrap_async`]: LinkerInstance::func_wrap_async
460+
///
461+
/// # Errors
462+
///
463+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
464+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
465+
/// details on Wasmtime's out-of-memory handling.
436466
//
437467
// TODO: needs more words and examples
438468
pub fn func_wrap<F, Params, Return>(&mut self, name: &str, func: F) -> Result<()>
@@ -656,6 +686,12 @@ impl<T: 'static> LinkerInstance<'_, T> {
656686
/// # Ok(())
657687
/// # }
658688
/// ```
689+
///
690+
/// # Errors
691+
///
692+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
693+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
694+
/// details on Wasmtime's out-of-memory handling.
659695
pub fn func_new(
660696
&mut self,
661697
name: &str,
@@ -724,6 +760,12 @@ impl<T: 'static> LinkerInstance<'_, T> {
724760
/// This can be used to provide a core wasm [`Module`] as an import to a
725761
/// component. The [`Module`] provided is saved within the linker for the
726762
/// specified `name` in this instance.
763+
///
764+
/// # Errors
765+
///
766+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
767+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
768+
/// details on Wasmtime's out-of-memory handling.
727769
pub fn module(&mut self, name: &str, module: &Module) -> Result<()> {
728770
self.insert(name, Definition::Module(module.clone()))?;
729771
Ok(())
@@ -751,6 +793,10 @@ impl<T: 'static> LinkerInstance<'_, T> {
751793
/// The provided `dtor` closure returns an error if something goes wrong
752794
/// when a guest calls the `dtor` to drop a `Resource<T>` such as
753795
/// a runtime trap or a runtime limit being exceeded.
796+
///
797+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
798+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
799+
/// details on Wasmtime's out-of-memory handling.
754800
pub fn resource(
755801
&mut self,
756802
name: &str,

crates/wasmtime/src/runtime/component/resources/any.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ impl ResourceAny {
7272
/// This method will return an error if `resource` has already been "taken"
7373
/// and has ownership transferred elsewhere which can happen in situations
7474
/// such as when it's already lowered into a component.
75+
///
76+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
77+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
78+
/// details on Wasmtime's out-of-memory handling.
7579
pub fn try_from_resource<T: 'static>(
7680
resource: Resource<T>,
7781
store: impl AsContextMut,
@@ -80,6 +84,12 @@ impl ResourceAny {
8084
}
8185

8286
/// See [`Resource::try_from_resource_any`]
87+
///
88+
/// # Errors
89+
///
90+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
91+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
92+
/// details on Wasmtime's out-of-memory handling.
8393
pub fn try_into_resource<T: 'static>(self, store: impl AsContextMut) -> Result<Resource<T>> {
8494
Resource::try_from_resource_any(self, store)
8595
}
@@ -146,6 +156,12 @@ impl ResourceAny {
146156
/// properly cleaned up. For owned resources this may execute the
147157
/// guest-defined destructor if applicable (or the host-defined destructor
148158
/// if one was specified).
159+
///
160+
/// # Errors
161+
///
162+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
163+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
164+
/// details on Wasmtime's out-of-memory handling.
149165
pub fn resource_drop(self, mut store: impl AsContextMut) -> Result<()> {
150166
let mut store = store.as_context_mut();
151167
store.0.validate_sync_call()?;
@@ -154,6 +170,12 @@ impl ResourceAny {
154170

155171
/// Same as [`ResourceAny::resource_drop`] except for use with async stores
156172
/// to execute the destructor [asynchronously](crate#async).
173+
///
174+
/// # Errors
175+
///
176+
/// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
177+
/// memory allocation fails. See the `OutOfMemory` type's documentation for
178+
/// details on Wasmtime's out-of-memory handling.
157179
#[cfg(feature = "async")]
158180
pub async fn resource_drop_async(self, mut store: impl AsContextMut<Data: Send>) -> Result<()> {
159181
let mut store = store.as_context_mut();

0 commit comments

Comments
 (0)