Summary
benchmarks/packages/qs/parse_nested.ts (qs 6.16.0) holds a stale from-space array pointer across an evacuating minor. The stale use is js_dyn_index_set_strict → js_array_set_f64_extend_strict_impl, called from qs's lib/parse.js. On main it is latent: a plain run happens to print the right checksum. Under a seeded schedule with from-space protection it faults on 5 of 6 seeds. With any runtime change that shifts allocation timing, it becomes a wrong answer on a plain run: a different wrong checksum each run, with no crash.
Reproduce (Linux x86-64, main at c1d93bb, release build)
cd benchmarks/packages && npm ci
PERRY_NO_AUTO_OPTIMIZE=1 PERRY_KEEP_SYMBOLS=1 perry compile qs/parse_nested.ts -o qs_parse_nested
PERRY_GC_PROTECT_FROMSPACE=1 PERRY_GC_PROTECT_FROMSPACE_DEPTH=64 PERRY_GC_SCHEDULE_SEED=7 ./qs_parse_nested 3000 1000
[gc-fromspace-protect] FAULT: signal 11 at 0x3e9972d2dbc
This address is RETIRED FROM-SPACE. The evacuating minor moved or
freed the object here and the holder kept the pre-collection address.
block=0x3e9972c0000 +77244 retired_bytes=84960 retired_by_minor=#24
last-known object: user_ptr=0x3e9972d2db0 obj_type=3 size=32
#0 perry_runtime::array::indexing::js_array_set_f64_extend_strict_impl
#1 js_dyn_index_set_strict
#2 perry_closure_node_modules_qs_lib_parse_js
#3 perry_closure_node_modules_qs_lib_parse_js
#4 perry_closure_node_modules_qs_lib_parse_js
#5 js_native_call_value
...
#10 perry_fn_qs_parse_nested_ts.op
Seeds 1, 7, 42, 99 and 123 fault; seed 555 completes (1,031 copying minors, 362,457 moved objects).
Wrong answer without the instrument
With a runtime whose allocation timing differs from main's, a plain ./qs_parse_nested 3000 1000 prints a wrong checksum in 9 of 12 runs, and a different one each time. Node 26.5.1 prints checksum fa2bbd09. The branch that showed this is wip/protochain-keyconv. It also fails with its only cache disabled (PERRY_INHERITED_IC=0), and the stale use above is on main. Base main was correct 12 of 12 on the plain run. Any qs instruction-count A/B is untrustworthy until this is fixed, because a timing change can flip the output.
Found while measuring the prototype-chain lane (#10495). The obj_type 3 target and the extend-store frame point at the dynamic-index store path (#10513 / #10694 neighbourhood), not the property path.
Summary
benchmarks/packages/qs/parse_nested.ts(qs 6.16.0) holds a stale from-space array pointer across an evacuating minor. The stale use isjs_dyn_index_set_strict→js_array_set_f64_extend_strict_impl, called from qs'slib/parse.js. Onmainit is latent: a plain run happens to print the right checksum. Under a seeded schedule with from-space protection it faults on 5 of 6 seeds. With any runtime change that shifts allocation timing, it becomes a wrong answer on a plain run: a different wrong checksum each run, with no crash.Reproduce (Linux x86-64,
mainat c1d93bb, release build)Seeds 1, 7, 42, 99 and 123 fault; seed 555 completes (1,031 copying minors, 362,457 moved objects).
Wrong answer without the instrument
With a runtime whose allocation timing differs from
main's, a plain./qs_parse_nested 3000 1000prints a wrong checksum in 9 of 12 runs, and a different one each time. Node 26.5.1 printschecksum fa2bbd09. The branch that showed this iswip/protochain-keyconv. It also fails with its only cache disabled (PERRY_INHERITED_IC=0), and the stale use above is onmain. Basemainwas correct 12 of 12 on the plain run. Anyqsinstruction-count A/B is untrustworthy until this is fixed, because a timing change can flip the output.Found while measuring the prototype-chain lane (#10495). The obj_type 3 target and the extend-store frame point at the dynamic-index store path (#10513 / #10694 neighbourhood), not the property path.