From ffe978c17c0bcdabc46c2fc7144fad840af6c06e Mon Sep 17 00:00:00 2001 From: angelnereira <176702755+angelnereira@users.noreply.github.com> Date: Sun, 24 May 2026 07:10:59 -0500 Subject: [PATCH 1/2] Avoid GC funcref type mismatch panics --- crates/wasmtime/src/runtime/vm/gc/func_ref.rs | 54 ++++++++++++++++--- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/crates/wasmtime/src/runtime/vm/gc/func_ref.rs b/crates/wasmtime/src/runtime/vm/gc/func_ref.rs index dab931d546a1..3e4fa4677828 100644 --- a/crates/wasmtime/src/runtime/vm/gc/func_ref.rs +++ b/crates/wasmtime/src/runtime/vm/gc/func_ref.rs @@ -79,12 +79,14 @@ impl FuncRefTable { // Ensure that the funcref actually is a subtype of the expected // type. This protects against GC heap corruption being leveraged in - // attacks: if the attacker has a write gadget inside the GC heap, they - // can overwrite a funcref ID to point to a different funcref, but this - // assertion ensures that any calls to that wrong funcref at least - // remain well-typed, which reduces the attack surface and maintains - // memory safety. - assert!(types.is_subtype(actual_ty, expected_ty)); + // attacks: if the attacker has a write gadget inside the GC heap, + // they can overwrite a funcref ID to point to a different funcref, + // but this check ensures that any calls to that wrong funcref at + // least remain well-typed, which reduces the attack surface and + // maintains memory safety. + if !types.is_subtype(actual_ty, expected_ty) { + bail_bug!("funcref table type mismatch") + } } Ok(f) @@ -103,3 +105,43 @@ impl FuncRefTable { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{Engine, FuncType, ValType}; + use alloc::boxed::Box; + use core::ptr::NonNull; + + #[cfg_attr( + debug_assertions, + should_panic(expected = "BUG: funcref table type mismatch") + )] + #[test] + fn typed_get_rejects_mismatched_func_type() { + let engine = Engine::default(); + let actual_ty = FuncType::new(&engine, [ValType::I32], []); + let expected_ty = FuncType::new(&engine, [], []); + assert!( + !engine + .signatures() + .is_subtype(actual_ty.type_index(), expected_ty.type_index()) + ); + + let mut func_ref = Box::new(VMFuncRef { + array_call: crate::runtime::vm::VmPtr::dangling(), + wasm_call: None, + type_index: actual_ty.type_index(), + vmctx: crate::runtime::vm::VmPtr::dangling(), + }); + + let mut table = FuncRefTable::default(); + let id = unsafe { table.intern(Some(SendSyncPtr::new(NonNull::from(&mut *func_ref)))) }; + + let result = table.get_typed(engine.signatures(), id, expected_ty.type_index()); + if cfg!(not(debug_assertions)) { + let err = result.unwrap_err(); + assert!(err.is::()); + } + } +} From f3cdfa75cc2e66fdb47ad0e995486ec643761c7b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 3 Jun 2026 07:50:17 -0700 Subject: [PATCH 2/2] Remove unnecessary test --- crates/wasmtime/src/runtime/vm/gc/func_ref.rs | 40 ------------------- 1 file changed, 40 deletions(-) diff --git a/crates/wasmtime/src/runtime/vm/gc/func_ref.rs b/crates/wasmtime/src/runtime/vm/gc/func_ref.rs index 3e4fa4677828..9276e9cdb188 100644 --- a/crates/wasmtime/src/runtime/vm/gc/func_ref.rs +++ b/crates/wasmtime/src/runtime/vm/gc/func_ref.rs @@ -105,43 +105,3 @@ impl FuncRefTable { } } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::{Engine, FuncType, ValType}; - use alloc::boxed::Box; - use core::ptr::NonNull; - - #[cfg_attr( - debug_assertions, - should_panic(expected = "BUG: funcref table type mismatch") - )] - #[test] - fn typed_get_rejects_mismatched_func_type() { - let engine = Engine::default(); - let actual_ty = FuncType::new(&engine, [ValType::I32], []); - let expected_ty = FuncType::new(&engine, [], []); - assert!( - !engine - .signatures() - .is_subtype(actual_ty.type_index(), expected_ty.type_index()) - ); - - let mut func_ref = Box::new(VMFuncRef { - array_call: crate::runtime::vm::VmPtr::dangling(), - wasm_call: None, - type_index: actual_ty.type_index(), - vmctx: crate::runtime::vm::VmPtr::dangling(), - }); - - let mut table = FuncRefTable::default(); - let id = unsafe { table.intern(Some(SendSyncPtr::new(NonNull::from(&mut *func_ref)))) }; - - let result = table.get_typed(engine.signatures(), id, expected_ty.type_index()); - if cfg!(not(debug_assertions)) { - let err = result.unwrap_err(); - assert!(err.is::()); - } - } -}