Found by #7793's witness during its merge audit, in the sabotage arm that turns the from-space quarantine OFF — i.e., this is a latent defect on main (v0.5.1458 runtime), not a defect of that PR, and the shipped witness configuration (quarantine ON) runs the same workload clean.
Reproduce
cargo build --release -p perry -p perry-runtime-static -p perry-stdlib-static
npm ci --ignore-scripts --no-audit --no-fund
target/release/perry test-files/gc-dep-corpus/main.ts -o /tmp/w
PERRY_GC_SCHEDULE_SEED=1 PERRY_GC_SCHEDULE_RATE=1 \
PERRY_GC_PROTECT_FROMSPACE=0 PERRY_GC_DIAG=1 /tmp/w
Observed (M1 Pro, macOS, release build):
TypeError: Cannot read properties of undefined (reading 'toString')
at <anonymous>
[gc-schedule] done: seed=1 safepoints=3319 scheduled_collections=3319 polls_paced=23075 copying_minors=3319 moved_objects=385030 loop_polls=26394
exit 1, at safepoint 3,319 of the ~8,000 the full run performs. The workload is single-threaded, so the seeded schedule should replay exactly ((seed, counter) — one observation so far; the box was at load 20+ and a second confirmation run was cut off, so start by confirming determinism).
Why this smells like a #7154-class rooting bug and not an instrument bug
- The same binary, same seed, same rate with
PERRY_GC_PROTECT_FROMSPACE=1 (depth 800) passes: 8,022 copying minors, 852,411 objects moved, output byte-identical to the unscheduled run — verified twice.
- The failure shape is the classic surfacing form:
TypeError on an undefined that should be an object, cycles after the collection that lost it.
- The quarantine changes recycling — retired from-space page-sets are held (and PROT_NONE'd) instead of being recycled into Eden. Two readings, both worth chasing:
- the allocation/recycling dynamics under quarantine shift which safepoints land in the vulnerable window, so the bug's window simply never lines up in the protected arm (the protected arm also paced differently: 63,936 polls vs 26,394 at the failure point); or
- less likely, a stale read that the quarantine should be catching is landing in memory the quarantine does not cover (same-cycle, pre-retirement).
- If (1),
PERRY_GC_SCHEDULE_SEED sweeps over the PROTECTED arm (scripts/gc_schedule_fuzz.sh) should eventually find a seed whose protected run faults precisely — that would hand us the exact stale address and retiring minor.
Context
The zod dependency corpus is exactly the population #7280 showed the curated fixtures don't reach (js_object_assign_one, js_new_function_construct). #7793's shipped witness gates the protected arm in CI; this issue tracks the unprotected finding it flushed out on its first run.
Found by #7793's witness during its merge audit, in the sabotage arm that turns the from-space quarantine OFF — i.e., this is a latent defect on
main(v0.5.1458 runtime), not a defect of that PR, and the shipped witness configuration (quarantine ON) runs the same workload clean.Reproduce
Observed (M1 Pro, macOS, release build):
exit 1, at safepoint 3,319 of the ~8,000 the full run performs. The workload is single-threaded, so the seeded schedule should replay exactly (
(seed, counter)— one observation so far; the box was at load 20+ and a second confirmation run was cut off, so start by confirming determinism).Why this smells like a #7154-class rooting bug and not an instrument bug
PERRY_GC_PROTECT_FROMSPACE=1(depth 800) passes: 8,022 copying minors, 852,411 objects moved, output byte-identical to the unscheduled run — verified twice.TypeErroron anundefinedthat should be an object, cycles after the collection that lost it.PERRY_GC_SCHEDULE_SEEDsweeps over the PROTECTED arm (scripts/gc_schedule_fuzz.sh) should eventually find a seed whose protected run faults precisely — that would hand us the exact stale address and retiring minor.Context
The zod dependency corpus is exactly the population #7280 showed the curated fixtures don't reach (
js_object_assign_one,js_new_function_construct). #7793's shipped witness gates the protected arm in CI; this issue tracks the unprotected finding it flushed out on its first run.