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
gap: test_gap_eval_as_value segfaults on Linux (SIGSEGV) — last remaining gap regression #8498
test_gap_eval_as_valuesegfaults on Linux, and is the only remaining gap regression on main (sweep 32422185436 on 8d1cca158, parity rate 98.4%):
CRASH test_gap_eval_as_value (SIGSEGV (exit 139))
Perry died after printing 4 line(s); Node exited 0.
The fixture only readseval as a value — it never calls it (this is the get-intrinsic pattern, '%eval%': eval):
constintrinsics: Record<string,unknown>={"%Array%": Array,"%eval%": eval,"%Math%": Math};console.log("has eval key:","%eval%"inintrinsics);conste=eval;console.log("got eval value without throwing:",e!==Symbol.for("unreachable"));
Not in gap_snapshot.json, so this is a regression rather than accepted debt.
Distinct from #8479's unwind class. That family aborted with SIGABRT and panic in a function that cannot unwind; this is a genuine SIGSEGV, so it is a wild/null dereference rather than an abort guard. It is also unaffected by the extern "C-unwind" removals in #8488 — it still crashes on 8d1cca158, which contains them.
Does not reproduce on macOS: compiled and run locally against a current perry-dev build, it exits 0 and its output matches the Node oracle byte-for-byte. So Linux CI is the only place it can be diagnosed, as with the rest of this crash family.
Suggested starting point: eval is classified as a global function (typeof === "function") in perry-codegen/src/expr/helpers.rs, but reading it as a value has to materialise something callable. A plausible shape — and it matches a pattern already seen this week in #8465, where net.connect reached a null JS_NATIVE_HTTP_DISPATCH and silently returned undefined — is that the eval value resolves to a function pointer that is null in the Linux build and gets dereferenced rather than checked. Worth dumping what the %eval% slot actually holds before the crash point.
Fixed by #8500 — same root cause as #8495, which is why this reproduced only on Linux and never on macOS.
test_gap_eval_as_value now passes on the Linux gap runner:
[46/96] (00:06:09) test_gap_eval_as_value … PASS
...
Gap snapshot OK — 96 tests match test-parity/gap_snapshot.json (5 known non-passing).
All six shards are green on that run (this shard had failed on main in the three preceding sweeps).
Why a stale this presented as a SIGSEGV here rather than a wrong value.#8500 fixes twenty-two receiver save/restore pairs that wrote the IMPLICIT_THIS cell directly, leaving the displaced receiver in a bare local across a user call; an evacuating collection moved the object and the restore published the pre-move address back into the cell. Most reads through such a pointer return garbage quietly — Object.keys comes back []. eval-as-value dereferences the receiver on a path where garbage is not survivable, so it faulted instead. That also explains the platform split: this is a moving-collection bug, and the crash class needs the Linux runner's heap behaviour to land on an unmapped page rather than reusable memory.
Closing as fixed. Verified on the merge commit's CI run, not inferred.
Activity
proggeramlug commentedon Aug 21, 2026
Fixed by #8500 — same root cause as #8495, which is why this reproduced only on Linux and never on macOS.
test_gap_eval_as_valuenow passes on the Linux gap runner:All six shards are green on that run (this shard had failed on
mainin the three preceding sweeps).Why a stale
thispresented as a SIGSEGV here rather than a wrong value. #8500 fixes twenty-two receiver save/restore pairs that wrote theIMPLICIT_THIScell directly, leaving the displaced receiver in a bare local across a user call; an evacuating collection moved the object and the restore published the pre-move address back into the cell. Most reads through such a pointer return garbage quietly —Object.keyscomes back[].eval-as-value dereferences the receiver on a path where garbage is not survivable, so it faulted instead. That also explains the platform split: this is a moving-collection bug, and the crash class needs the Linux runner's heap behaviour to land on an unmapped page rather than reusable memory.Closing as fixed. Verified on the merge commit's CI run, not inferred.