Invalid drop of partially-initialized instances in wasmtime
Description
Wasmtime is an open source runtime for WebAssembly & WASI. Prior to versions 0.34.1 and 0.33.1, there exists a bug in the pooling instance allocator in Wasmtime's runtime where a failure to instantiate an instance for a module that defines an externref global will result in an invalid drop of a VMExternRef via an uninitialized pointer. A number of conditions listed in the GitHub Security Advisory must be true in order for an instance to be vulnerable to this issue. Maintainers believe that the effective impact of this bug is relatively small because the usage of externref is still uncommon and without a resource limiter configured on the Store, which is not the default configuration, it is only possible to trigger the bug from an error returned by mprotect or VirtualAlloc. Note that on Linux with the uffd feature enabled, it is only possible to trigger the bug from a resource limiter as the call to mprotect is skipped. The bug has been fixed in 0.34.1 and 0.33.1 and users are encouraged to upgrade as soon as possible. If it is not possible to upgrade to version 0.34.1 or 0.33.1 of the wasmtime crate, it is recommend that support for the reference types proposal be disabled by passing false to Config::wasm_reference_types. Doing so will prevent modules that use externref from being loaded entirely.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
wasmtimecrates.io | >= 0.34.0, < 0.34.1 | 0.34.1 |
wasmtimecrates.io | < 0.33.1 | 0.33.1 |
Affected products
1- Range: < 0.33.0
Patches
1886ecc562040Merge pull request from GHSA-88xq-w8cq-xfg7
3 files changed · +74 −1
crates/runtime/src/instance/allocator.rs+5 −0 modified@@ -475,6 +475,8 @@ fn initialize_instance( } unsafe fn initialize_vmcontext(instance: &mut Instance, req: InstanceAllocationRequest) { + assert!(!instance.vmctx_initialized); + if let Some(store) = req.store.as_raw() { *instance.interrupts() = (*store).vminterrupts(); *instance.epoch_ptr() = (*store).epoch_ptr(); @@ -570,6 +572,9 @@ unsafe fn initialize_vmcontext(instance: &mut Instance, req: InstanceAllocationR // Initialize the defined globals initialize_vmcontext_globals(instance); + + // Mark the vmctx as initialized + instance.vmctx_initialized = true; } unsafe fn initialize_vmcontext_globals(instance: &Instance) {
crates/runtime/src/instance.rs+11 −1 modified@@ -92,6 +92,10 @@ pub(crate) struct Instance { /// allocation, but some host-defined objects will store their state here. host_state: Box<dyn Any + Send + Sync>, + /// Flag to track when the vmctx has been initialized. + /// The pooling allocator may drop an instance before `vmctx` is initialized. + vmctx_initialized: bool, + /// Additional context used by compiled wasm code. This field is last, and /// represents a dynamically-sized array that extends beyond the nominal /// end of the struct (similar to a flexible array member). @@ -119,6 +123,7 @@ impl Instance { dropped_data: EntitySet::with_capacity(module.passive_data_map.len()), host_state, wasm_data, + vmctx_initialized: false, vmctx: VMContext { _marker: std::marker::PhantomPinned, }, @@ -733,13 +738,18 @@ impl Instance { } fn drop_globals(&mut self) { + // Dropping globals requires that the vmctx be fully initialized + if !self.vmctx_initialized { + return; + } + for (idx, global) in self.module.globals.iter() { let idx = match self.module.defined_global_index(idx) { Some(idx) => idx, None => continue, }; match global.wasm_ty { - // For now only externref gloabls need to get destroyed + // For now only externref globals need to get destroyed WasmType::ExternRef => {} _ => continue, }
tests/all/pooling_allocator.rs+58 −0 modified@@ -511,3 +511,61 @@ fn preserve_data_segments() -> Result<()> { Ok(()) } + +#[test] +fn drop_externref_global_during_module_init() -> Result<()> { + struct Limiter; + + impl ResourceLimiter for Limiter { + fn memory_growing(&mut self, _: usize, _: usize, _: Option<usize>) -> bool { + false + } + + fn table_growing(&mut self, _: u32, _: u32, _: Option<u32>) -> bool { + false + } + } + + let mut config = Config::new(); + config.wasm_reference_types(true); + config.allocation_strategy(InstanceAllocationStrategy::Pooling { + strategy: PoolingAllocationStrategy::NextAvailable, + module_limits: Default::default(), + instance_limits: InstanceLimits { count: 1 }, + }); + + let engine = Engine::new(&config)?; + + let module = Module::new( + &engine, + r#" + (module + (global i32 (i32.const 1)) + (global i32 (i32.const 2)) + (global i32 (i32.const 3)) + (global i32 (i32.const 4)) + (global i32 (i32.const 5)) + ) + "#, + )?; + + let mut store = Store::new(&engine, Limiter); + drop(Instance::new(&mut store, &module, &[])?); + drop(store); + + let module = Module::new( + &engine, + r#" + (module + (memory 1) + (global (mut externref) (ref.null extern)) + ) + "#, + )?; + + let mut store = Store::new(&engine, Limiter); + store.limiter(|s| s); + assert!(Instance::new(&mut store, &module, &[]).is_err()); + + Ok(()) +}
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-88xq-w8cq-xfg7ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-23636ghsaADVISORY
- github.com/bytecodealliance/wasmtime/commit/886ecc562040bef61faf19438c22285c2d62403aghsax_refsource_MISCWEB
- github.com/bytecodealliance/wasmtime/security/advisories/GHSA-88xq-w8cq-xfg7ghsax_refsource_CONFIRMWEB
- rustsec.org/advisories/RUSTSEC-2022-0096.htmlghsaWEB
News mentions
0No linked articles in our index yet.