Impact
Wasmtime's implementation of the wasi:http/types.fields resource is susceptible to panics when too many fields are added to the set of headers. Wasmtime's implementation in the wasmtime-wasi-http crate is backed by a data structure which panics when it reaches excessive capacity and this condition was not handled gracefully in Wasmtime. Panicking in a WASI implementation is a Denial of Service vector for embedders and is treated as a security vulnerability in Wasmtime.
Patches
Wasmtime 24.0.6, 36.0.6, 40.0.4, 41.0.4, and 42.0.0 patch this vulnerability and return a trap to the guest instead of panicking.
Workarounds
There are no known workarounds at this time, embedders are encouraged to update to a patched version of Wasmtime.
Resources
Limitations of http::HeaderMap
Impact
Wasmtime's implementation of WASI host interfaces are susceptible to guest-controlled resource exhaustion on the host. Wasmtime did not appropriately place limits on resource allocations requested by the guests. This serves as a Denial of Service vector where a guest can induce a range of crashing behaviors on the host such as:
Allocating arbitrarily large amounts of host memory. Causing an allocation failure on the host, which in Rust defaults to aborting the process. Causing a panic on the host due to over-large allocations being performed. Cause degredation in performance of the host by holding excessive host memory alive.
Wasmtime's security bug policy considers all of these behaviors a security vulnerability. Wasmtime's implementation of WASI has a number of different ways that resource exhaustion could happen, and fixing any one of them is insufficient from solving this vulnerability. A number of individual issues are grouped within this advisory and as a whole represent the known ways that guests can exhaust resources on the host.
An example of guest-controlled resource exhaustion within Wasmtime's implementation of WASI is guests could repeatedly allocate handles to themselves without limit. Some APIs also caused the host to perform a guest-controlled-sized allocation of a buffer on the host for I/O operations. Other APIs could force the host to buffer arbitrary amounts of data for the guest. Finally the guest could hand arbitrarily large allocations from itself to the host which could cause the host to perform an arbitrarily sized copy of memory which in some situations could result in quadratically sized allocations.
Wasmtime's implementations of WASIp1 and WASIp2 are affected by this vulnerability. Any host API modeled with the Component Model (or WIT) which operates on a string or list<T> type is also affected. Not all WIT and WASI APIs are affected by this issue, but that's more of an exception so it's recommended for all embedders to consider themselves affected.
To address this issue a number of mitigations are being applied to limit the behavior of a guest in WASI. All of these mitigations manifest in the form of a limit of some kind applied to various situations, and as such all of these mitigations are backwards-incompatible as they run the risk of breaking preexisting programs. To address this all backports to previous stable releases have these limits tuned to overly-large values. This ensures that preexisting guests do not break while still providing embedders the knobs to prevent this DoS vector as well. The limits added to Wasmtime are:
-Smax-resources=N or ResourceTable::setmaxcapacity - the maximum number of resources that a guest is allowed to allocate for itself. -Shostcall-fuel=N or Store::sethostcallfuel - the maximum amount of data that the guest may copy to the host in a single function call. -Smax-random-size=N or WasiCtxBuilder::maxrandomsize - the maximum size of the return value of get-random-bytes and get-insecure-random-bytes in the wasi:random implementations. -Smax-http-fields-size=N or WasiHttpCtx::setmaxfieldssize - the maximum size of headers for an HTTP request/response.
These settings are equally applicable to both WASIp1 and WASIp2. Wasmtime 41.0.x and prior previously did not limit these settings and the knobs being released are set to very large values by default to avoid any breaking behavior. Embedders will need to proactively tune these knobs as appropriate for their embeddings. The default settings in the unreleased Wasmtime 42.0.0 are 1M for max resources, 128MiB for hostcall fuel, 64MiB for max-random-size, and 32KiB for http fields size. Tuning is not expected for Wasmtime 42.0.0+.
Hosts/embedders affected by this issue are encouraged to audit and double-check their own host APIs they have implemented to see whether they are affected by this issue as well. The -Shostcall-fuel setting is intended to be a relatively coarse fix for many possible issues by limiting the amount of data for all host APIs at once, so many embedders may not need to take further action beyond updating Wasmtime and configuring it appropriately (if not updating to 42.0.0). Embedders should audit to see, however, if the guest is able to force the host to allocate on its behalf and ensure that the allocation is limited or tracked somehow.
Patches
Wasmtime 24.0.6, 36.0.6, 40.0.4, 41.0.4, and 42.0.0 have all been released with the fix for this issue. These versions do not prevent this issue in their default configuration to avoid breaking preexisting behaviors. All versions of Wasmtime have appropriate knobs to prevent this behavior, and Wasmtime 42.0.0-and-later will have these knobs tuned by default to prevent this issue from happening.
Workarounds
There are no known workarounds for this issue without upgrading. Embedders are recommended to upgrade and configure their embeddings as necessary to prevent possibly-malicious guests from triggering this issue.
Resources
Store::sethostcallfuel ResourceTable::setmaxcapacity WasiCtxBuilder::maxrandomsize Original PR showing resource exhaustion Issue about limiting max resource handles per-guest
The affected versions of Wasmtime can panic if the host embedder drops the future returned by wasmtime::component::[Typed]Func::callasync before it resolves.
Details
Starting with Wasmtime 39.0.0, the component-model-async feature became the default, which brought with it a new implementation of [Typed]Func::callasync which made it capable of calling async-typed guest export functions. However, that implementation had a bug leading to a panic under certain circumstances:
1. The host embedding calls [Typed]Func::callasync on a function exported by a component, polling the returned Future once. 2. The component function yields control to the async runtime (e.g. Tokio), e.g. due to a call to host function registered using LinkerInstance::funcwrapasync which yields, or due an epoch interruption. 3. The host embedding drops the Future after polling it once. This leaves the component instance in a non-reenterable state since the call never had a chance to complete. 4. The host embedding calls [Typed]Func::callasync again, polling the returned Future. Since the component instance cannot be entered at this point, the call traps, but not before allocating a task and thread for the call. 5. The host embedding ignores the trap and drops the Future. This panics due to the runtime attempting to dispose of the task created above, which panics since the thread has not yet exited.
Impact When a host embedder using the affected versions of Wasmtime calls wasmtime::component::[Typed]Func::callasync on a guest export and then drops the returned future without waiting for it to resolve, and then does so again with the same component instance, Wasmtime will panic. Embeddings that have the component-model-async compile-time feature disabled are unaffected.
Patches Wasmtime 40.0.4 and 41.0.4 have been patched to fix this issue. Versions 42.0.0 and later are not affected.
Workarounds If an embedding is not actually using any component-model-async features then disabling the component-model-async Cargo feature can work around this issue. This issue can also be worked around by either ensuring every callasync future is awaited until it completes or refraining from using the Store again after dropping a not-yet-resolved callasync future.
Resources This was first reported in https://bytecodealliance.zulipchat.com/#narrow/channel/206238-general/topic/Panic.20in.20Wasmtime.2041.2E0.2E3.20.28runtime.2Fconcurrent.2Fcomponent.29
On x86-64 platforms with AVX Wasmtime's compilation of the f64.copysign WebAssembly instruction with Cranelift may load 8 more bytes than is necessary. When [signals-based-traps] are disabled this can result in a uncaught segfault due to loading from unmapped guard pages. With guard pages disabled it's possible for out-of-sandbox data to be loaded, but unless there is another bug in Cranelift this data is not visible to WebAssembly guests.
Details
The f64.copysign operator, when operating on a value loaded from a memory (for example with f64.load), compiles with Cranelift to code on x86-64 with AVX that loads 128 bits (16 bytes) rather than the expected 64 bits (8 bytes) from memory. When the address is in-bounds for a (correct) 8-byte load but not an (incorrect) 16-byte load, this can load beyond memory by up to 8 bytes. This can result in three different behaviors depending on Wasmtime's configuration:
1. If guard pages are disabled then this extra data will be loaded. The extra data is present in the upper bits of a register, but the upper bits are not visible to WebAssembly guests. Actually witnessing this data would require a different bug in Cranelift, of which none are known. Thus in this situation while it's something we're patching in Cranelift it's not a security issue. 2. If guard pages are enabled, and [signals-based-traps] are enabled, then this operation will result in a safe WebAssembly trap. The trap is incorrect because the load is not out-of-bounds as defined by WebAssembly, but this mistakenly widened load will load bytes from an unmapped guard page, causing a segfault which is caught and handled as a Wasm trap. In this situation this is not a security issue, but we're patching Cranelift to fix the WebAssembly behavior. 3. If guard pages are enabled, and [signals-based-traps] are disabled, then this operation results in an uncaught segfault. Like the previous case with guard pages enabled this will load from an unmapped guard page. Unlike before, however, signals-based-traps are disabled meaning that signal handlers aren't configured. The resulting segfault will, by default, terminate the process. This is a security issue from a DoS perspective, but does not represent an arbitrary read or write from WebAssembly, for example.
Wasmtime's default configuration is case (2) in this case. That means that Wasmtime, by default, incorrectly executes this WebAssembly instruction but does not have insecure behavior.
Impact
If [signals-based-traps] are disabled and guard pages are enabled then guests can trigger an uncaught segfault in the host, likely aborting the host process. This represents, for example, a DoS vector for WebAssembly guests.
This bug does not affect Wasmtime's default configuration and requires [signals-based-traps] to be disabled. This bug only affects the x86-64 target with the AVX feature enabled and the Cranelift backend (Wasmtime's default backend).
Patches
Wasmtime 36.0.5, 40.0.3, and 41.0.1 have been released to fix this issue. Users are recommended to upgrade to the patched versions of Wasmtime. Other affected versions are not patched and users should updated to supported major version instead.
Workarounds
This bug can be worked around by enabling [signals-based-traps]. While disabling guard pages can be a quick fix in some situations, it's not recommended to disabled guard pages as it is a key defense-in-depth measure of Wasmtime.
Resources
[signals-based-traps configuration][signals-based-traps] guard pages configuration
[signals-based-traps]: https://docs.rs/wasmtime/latest/wasmtime/struct.Config.html#method.signalsbasedtraps