You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
gc: PERRY_GC_FROMSPACE_SCAN_ABORT=1 aborts deterministically on two corpus files — an Old-space slot points at an unevacuated NurseryEden object #7677
PERRY_GC_FROMSPACE_SCAN_ABORT=1 aborts 100% reproducibly on two corpus files, reporting an Old-space slot that points into NurseryEden at a target that was never evacuated — while the slot is dirty and the owner is marked.
Found while investigating #7018 (which does not reproduce); this is a separate, unrelated finding.
It is SIGABRT, not SIGSEGV (the instrument's own panic, working as designed), and it is independent of PERRY_GC_TRACE, of the rest of the #7018 env (the knob alone suffices), and of PERRY_RS4GC — 0 and 1 both abort.
The interesting part is what is not wrong. missing_rewrites=0, so no evacuated object went un-rewritten. The slot is in the remembered set (ever_dirty=true), and its owner is marked. The target simply was never evacuated, which is why the rewrite pass had nothing to do — dirty_but_missed=1.
Why it looks like a table rather than a register
Per CLAUDE.md's own tell: "an unrooted register goes bad only when a collection lands in its window, so it is intermittent; an unrooted cache goes bad at collection #0 and stays bad, so a perfectly reproducible GC bug means a table, not a register." This is perfectly reproducible on two files and absent on three, which points at a runtime-side cache of a raw heap pointer — the class of changelog.d/7219-registry-gc-unrooted-caches.md and changelog.d/7239-gc-unrooted-runtime-caches.md — rather than a codegen rooting miss, which scripts/gc_root_dominance_check.py would see and which is currently allowlist-empty.
gc_register_mutable_root_scanner in gc/mod.rs (~55 entries) is where such a cache has to be registered, and the static checker is structurally blind to one that is not.
Why it matters beyond the knob
PERRY_GC_FROMSPACE_SCAN_ABORT is one of the #7154-family instruments, and its whole job is to turn a stale from-space reference into an immediate, precise report. If it fires deterministically on two corpus files today, then either
there is a live unrooted heap-pointer cache reachable from those two workloads, or
the instrument has a false-positive shape,
and the two are worth telling apart before the next rooting investigation trusts a clean run of it. It is not covered by #7254 (that is the different ZEAL + VERIFY_EVACUATION pairing).
Not investigated here
The owner's type=3 and the +24 slot offset are recorded but not decoded; whoever picks this up should start by identifying the owning object type and which side table (if any) holds that address, rather than by reading codegen output.
An earlier pass at this found the repro inert rather than clean: PERRY_GC_TRACE=1 PERRY_GC_DIAG=1 showed eligible=false fallback=conservative_stack on every collection and zero[gc-fromspace-scan] lines. The scan code never ran, so exit 0 meant nothing at all — a run with no copying minor scans nothing and aborts on nothing.
That is now reversed: hundreds of scan lines, zero conservative fallbacks, tens of thousands of objects examined per run, no abort.
What changed is #7721, which flipped PERRY_GC_MOVING_LOOP_POLLS to default ON. Reaching a copying minor by the alloc-point→safepoint route needed loop polls, and they were default-OFF from #7161 until #7721. So the configuration this issue reported was, for most of its life, one in which the instrument could not fire — and whatever the original abort was, the code path that produced it is now exercised heavily and comes back clean.
Reopen if it returns, but please include the [gc-fromspace-scan] count and the eligible= state in the report — without those a clean exit and an inert run are indistinguishable, which is what made this issue hard to settle for so long.
Activity
proggeramlug commentedon Aug 10, 2026
Closing — re-tested on current
main(v0.5.1432) with the instrument verifiably ARMED, and it no longer aborts.The issue's literal repro, unchanged:
[gc-fromspace-scan]lineseligible=false/ conservative fallbacktest_gap_repsel_scalar_replaced_localstest_gap_repsel_gc_stressWhy the arming evidence is the whole point
An earlier pass at this found the repro inert rather than clean:
PERRY_GC_TRACE=1 PERRY_GC_DIAG=1showedeligible=false fallback=conservative_stackon every collection and zero[gc-fromspace-scan]lines. The scan code never ran, so exit 0 meant nothing at all — a run with no copying minor scans nothing and aborts on nothing.That is now reversed: hundreds of scan lines, zero conservative fallbacks, tens of thousands of objects examined per run, no abort.
What changed is #7721, which flipped
PERRY_GC_MOVING_LOOP_POLLSto default ON. Reaching a copying minor by the alloc-point→safepoint route needed loop polls, and they were default-OFF from #7161 until #7721. So the configuration this issue reported was, for most of its life, one in which the instrument could not fire — and whatever the original abort was, the code path that produced it is now exercised heavily and comes back clean.Reopen if it returns, but please include the
[gc-fromspace-scan]count and theeligible=state in the report — without those a clean exit and an inert run are indistinguishable, which is what made this issue hard to settle for so long.