diff --git a/crates/runtime/src/externref.rs b/crates/runtime/src/externref.rs index 9677b39afa..573dd4b24f 100644 --- a/crates/runtime/src/externref.rs +++ b/crates/runtime/src/externref.rs @@ -463,7 +463,7 @@ impl Deref for VMExternRef { /// /// We use this so that we can morally put `VMExternRef`s inside of `HashSet`s /// even though they don't implement `Eq` and `Hash` to avoid foot guns. -#[derive(Clone)] +#[derive(Clone, Debug)] struct VMExternRefWithTraits(VMExternRef); impl Hash for VMExternRefWithTraits { @@ -940,7 +940,9 @@ pub unsafe fn gc( debug_assert!( r.is_null() || activations_table_set.contains(&r), "every on-stack externref inside a Wasm frame should \ - have an entry in the VMExternRefActivationsTable" + have an entry in the VMExternRefActivationsTable; \ + {:?} is not in the table", + r ); if let Some(r) = NonNull::new(r) { VMExternRefActivationsTable::insert_precise_stack_root( diff --git a/crates/runtime/src/libcalls.rs b/crates/runtime/src/libcalls.rs index 2544e16739..841dfaa1a2 100644 --- a/crates/runtime/src/libcalls.rs +++ b/crates/runtime/src/libcalls.rs @@ -402,6 +402,13 @@ pub unsafe extern "C" fn wasmtime_activations_table_insert_with_gc( let externref = VMExternRef::clone_from_raw(externref); let instance = (*vmctx).instance(); let (activations_table, module_info_lookup) = (*instance.store()).externref_activations_table(); + + // Invariant: all `externref`s on the stack have an entry in the activations + // table. So we need to ensure that this `externref` is in the table + // *before* we GC, even though `insert_with_gc` will ensure that it is in + // the table *after* the GC. + activations_table.insert_without_gc(externref.clone()); + activations_table.insert_with_gc(externref, module_info_lookup); }