Summary
Calling a const arrow function directly (show(a, b) where const show = (…) => …) leaves an earlier, already-evaluated heap argument unrooted while a later argument is evaluated. If the later argument collects (an evacuating minor moves the earlier argument), the call receives the pre-collection from-space address.
Under from-space protection this is an immediate SIGSEGV. Without protection it is silent wrong output: the label prints as an empty string.
The same program with function show(…) {…} passes.
Minimal repro
function work(n: number): number {
let s = 0;
for (let i = 0; i < n; i++) s += String(i * 7919).length;
return s;
}
const show = (label: string, v: number) => console.log(label, v);
const x: any = 4294967301;
show(String(x), work(3));
Build with GC instruments (PERRY_GC_INSTRUMENTS=1 at compile time).
| run |
result |
| plain |
4294967301 10 (matches node) |
PERRY_GC_SCHEDULE_SEED=1 PERRY_GC_SCHEDULE_RATE=1 |
10 (label is lost, rc=0) |
PERRY_GC_SCHEDULE_SEED=<1..10> PERRY_GC_SCHEDULE_RATE=1 PERRY_GC_PROTECT_FROMSPACE=1 |
SIGSEGV, every seed |
same with function show(...) {...} |
passes, all seeds |
PERRY_GC_PROTECT_FROMSPACE_DEPTH makes no difference (1, 4 and 100 all fault). The fault is fully deterministic.
Reporter (seed 1):
[gc-fromspace-protect] FAULT: signal 11 at 0x3faf0a40034
This address is RETIRED FROM-SPACE. ...
block=0x3faf0a40000 +52 retired_bytes=72 retired_by_minor=#0
last-known object: user_ptr=0x3faf0a40028 obj_type=3 size=40
obj_type=3 is GC_TYPE_STRING, the String(x) argument.
Where it was found
test-files/test_gap_10511_number_local_loop.ts (added by #11788) faults under the same knobs on its show("mix " + …, mix(seed, 3)) line, and does so identically on main with or without #11788's loop tier. This bug is independent of #11788.
Reproduced on main 69de7f3 (and ebc858c).
Summary
Calling a
constarrow function directly (show(a, b)whereconst show = (…) => …) leaves an earlier, already-evaluated heap argument unrooted while a later argument is evaluated. If the later argument collects (an evacuating minor moves the earlier argument), the call receives the pre-collection from-space address.Under from-space protection this is an immediate SIGSEGV. Without protection it is silent wrong output: the label prints as an empty string.
The same program with
function show(…) {…}passes.Minimal repro
Build with GC instruments (
PERRY_GC_INSTRUMENTS=1at compile time).4294967301 10(matches node)PERRY_GC_SCHEDULE_SEED=1 PERRY_GC_SCHEDULE_RATE=110(label is lost, rc=0)PERRY_GC_SCHEDULE_SEED=<1..10> PERRY_GC_SCHEDULE_RATE=1 PERRY_GC_PROTECT_FROMSPACE=1function show(...) {...}PERRY_GC_PROTECT_FROMSPACE_DEPTHmakes no difference (1, 4 and 100 all fault). The fault is fully deterministic.Reporter (seed 1):
obj_type=3isGC_TYPE_STRING, theString(x)argument.Where it was found
test-files/test_gap_10511_number_local_loop.ts(added by #11788) faults under the same knobs on itsshow("mix " + …, mix(seed, 3))line, and does so identically on main with or without #11788's loop tier. This bug is independent of #11788.Reproduced on main 69de7f3 (and ebc858c).