Stream: git-wasmtime

Topic: wasmtime / issue #14563 Guest-debug GC root tracing treat...


view this post on Zulip Wasmtime GitHub notifications bot (Oct 06 2026 at 14:33):

fitzgen commented on issue #14563:

<details> <summary>Full LLM report</summary>

Guest-debug GC root tracing treats module_start's stack words as GC roots

Date 2026-10-05
Wasmtime commit 73b04cff3317d1e308866eb24359483ac6116669 (main)
Host macOS 15.8.1 (Darwin 24.6.0), aarch64-apple-darwin
Model Claude Opus 5.5 (claude-opus-5-5)
Features Config::guest_debug(true) / -D guest-debug=y, plus Wasm GC with the copying (default) or drc collector
Class Wrong GC roots: stack corruption (copying), bogus refcount traffic (DRC)

Summary

When guest debugging is enabled, root tracing asks the guest-debug frame table
which locals and operand-stack slots of each Wasm frame hold GC refs
(crates/wasmtime/src/runtime/store/gc.rs:796-803 ->
crate::debug::gc_refs_in_frame, crates/wasmtime/src/runtime/debug.rs:838).
That lookup goes through FrameTable::find_program_point
(crates/environ/src/frame_table.rs:178-193):

let index = match self.progpoint_pcs.binary_search_by_key(&key, ...) {
    Ok(idx) => idx,
    Err(idx) if idx > 0 => idx - 1,   // nearest preceding program point
    Err(_) => return None,
};

The Err(idx) => idx - 1 fallback never checks that the preceding program
point belongs to the same function as pc. The compiled module_start
function has no guest-debug program points of its own. It runs global
initializers, element-segment setup and the start function. So it inherits
the descriptor of whichever function precedes it in the text section.
wasmtime objdump --frame-tables shows this.

When a GC happens while module_start is on the stack, for example when an
array.new_default in a global initializer exhausts the heap,
gc_refs_in_frame decodes that unrelated descriptor. It then hands the
collector FP-relative slots in module_start's frame that hold arbitrary
words:

VirtualFrame::decode (debug.rs:684) uses the same lookup, so the debugger
API (FrameHandle locals) reports wrong frames and values for these frames
too.

No GC-heap corruption or unsafe API is needed. A valid module, a public
Config option, and Instance::new are enough.

Reproduction

repro.wast: three globals each initialized with a 100000-byte
array.new_default, and a preceding function with 32 externref locals so
the borrowed descriptor names many slots.

$ target/debug/wasmtime wast -W gc=y -C collector=copying -D guest-debug=y reports/014-guest-debug-stale-frame-roots/repro.wast
thread 'main' panicked at crates/wasmtime/src/runtime/vm/gc/enabled/copying.rs:1044:13:
assertion failed: self.heap.is_in_idle_space(old_index)

$ target/debug/wasmtime wast -W gc=y -C collector=drc -D guest-debug=y reports/014-guest-debug-stale-frame-roots/repro.wast
thread 'main' panicked at crates/wasmtime/src/runtime/vm/gc/enabled/drc.rs:567:13:
every on-stack gc ref inside a Wasm frame should have be in our over-approximated
stack roots set, but 0xa8000000 is not in the set

Rust API (crate/, binary guest_debug_roots), which shows the release-build
behaviour:

$ cd reports/014-guest-debug-stale-frame-roots/crate
$ cargo run --release --bin guest_debug_roots copying
collector=Copying guest_debug=false: Ok(Ok(()))
collector=Copying guest_debug=true: Ok(Err(BUG: gc object and/or heap misaligned ...))
$ cargo run --release --bin guest_debug_roots drc
thread 'main' panicked at crates/wasmtime/src/runtime/vm/gc/gc_runtime.rs:517:9:
collector=DeferredReferenceCounting guest_debug=true: Err(Any { .. })

The release results depend on which stray words happen to be on the stack.
These two were stable here. Before the collector notices anything, the
copying collector has already written to the stray stack slots it accepted
as roots.

observed.txt records the full matrix.

Suggested fix

Make the frame-table lookup function-boundary aware:

Run the GC misc_testsuite with guest_debug enabled in CI to catch this
class of bug.

</details>

view this post on Zulip Wasmtime GitHub notifications bot (Oct 06 2026 at 14:35):

fitzgen opened issue #14563:

With -D guest-debug=y, GC root tracing looks up frame descriptors via
FrameTable::find_program_point. When a PC has no exact match, that lookup
falls back to the previous program point without checking whether it belongs
to the same function. Functions with no program points of their own, such as
the compiled module_start, inherit a neighbour's descriptor. A GC during
global initialization then hands arbitrary stack words to the collector as
roots, and the copying collector rewrites those stack slots.

Test Case

(module
  (type $a (array (mut i8)))
  (global $g1 (ref $a) (array.new_default $a (i32.const 100000)))
  (global $g2 (ref $a) (array.new_default $a (i32.const 100000)))
  (global $g3 (ref $a) (array.new_default $a (i32.const 100000)))
  (func (local externref externref externref externref externref externref externref externref
               externref externref externref externref externref externref externref externref
               externref externref externref externref externref externref externref externref
               externref externref externref externref externref externref externref externref))
)

Steps to Reproduce

wasmtime wast -W gc=y -C collector=copying -D guest-debug=y test.wast

Expected Results

The module instantiates successfully, just as it does without
-D guest-debug=y.

Actual Results

thread 'main' panicked at crates/wasmtime/src/runtime/vm/gc/enabled/copying.rs:1044:13:
assertion failed: self.heap.is_in_idle_space(old_index)

With -C collector=drc:

thread 'main' panicked at crates/wasmtime/src/runtime/vm/gc/enabled/drc.rs:567:13:
every on-stack gc ref inside a Wasm frame should have be in our over-approximated stack roots set, but 0xa8000000 is not in the set

Release builds give BUG: gc object and/or heap misaligned with the copying
collector, and panic in GcHeap::index_mut with DRC.

Versions and Environment

Wasmtime version or commit: 73b04cff33

Operating system: macOS 15.8.1

Architecture: aarch64

view this post on Zulip Wasmtime GitHub notifications bot (Oct 06 2026 at 14:35):

fitzgen added the wasmtime:debugging label to Issue #14563.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 06 2026 at 14:35):

fitzgen added the wasm-proposal:gc label to Issue #14563.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 09 2026 at 01:02):

alexcrichton added the bug label to Issue #14563.


Last updated: Oct 11 2026 at 04:10 UTC