Skip to content

gc: stale from-space closure in js_register_function_prototype_method during moment init under seeded schedule (moment/parse_format 22/200 seeds) #11635

Description

@proggeramlug

Under a seeded GC schedule, the moment/parse_format package workload (moment 2.31.0) faults on a stale from-space closure pointer during moment's module init. The stale use is in js_register_function_prototype_method → synthetic_class_id_for_function, which is the Func.prototype.x = fn registration path.

main: 295473cd5c4f0eceed5c5cd04b180e7533195641. It contains #11610 (be39bbf), which fixed the qs crash with a similar shape (#11550). On the same build, qs/parse_nested is 0/200 clean, so this one is separate.

Failure rate

PERRY_GC_SCHEDULE_RATE=0.05 PERRY_GC_PROTECT_FROMSPACE=1, seeds 1–200, args 300 50, output compared to Node 26.5.1 (checksum cfca1856):

22/200 fail, all SIGSEGV (rc 139): 3 13 19 21 32 65 75 90 91 94 97 101 107 120 133 140 144 152 155 162 173 183

These are the same seeds PR #11634 reported as pre-existing on its older base (3, 13, 19, 21, 32 within 1–40; 22/200). It is deterministic: seed 3 faulted on 2 of 2 re-runs. Every failure has the same signature: obj_type=4 (GC_TYPE_CLOSURE), size=56, retired_by_minor=#1, around safepoint 14. That means the first copying minor, during module init.

Without PERRY_GC_PROTECT_FROMSPACE, seeds 3, 13 and 19 exit 0 (77, 70 and 64 copying minors). I did not diff their output against Node, so a silent wrong answer there is possible but unverified.

Repro

cargo build --release -p perry -p perry-runtime-static -p perry-stdlib-static
cd benchmarks/packages && npm ci --ignore-scripts
PERRY_KEEP_SYMBOLS=1 PERRY_NO_AUTO_OPTIMIZE=1 PERRY_RUNTIME_DIR=../../target/release \
  ../../target/release/perry compile moment/parse_format.ts -o /tmp/msym
PERRY_GC_SCHEDULE_SEED=3 PERRY_GC_SCHEDULE_RATE=0.05 PERRY_GC_PROTECT_FROMSPACE=1 /tmp/msym 300 50

Fault report (seed 3, symbolized via addr2line)

[gc-schedule] FAILURE (signal 11) under seed=3
[gc-schedule]   safepoints=14 scheduled_collections=2
[gc-fromspace-protect] FAULT: signal 11 at 0x50e74416f30
  This address is RETIRED FROM-SPACE. The evacuating minor moved or
  freed the object here and the holder kept the pre-collection address.
  block=... +94000 retired_bytes=518824 retired_by_minor=#1
  last-known object: user_ptr=0x50e74416f38 obj_type=4 size=56
  The faulting instruction IS the stale use. Backtrace:
perry_runtime::arena::quarantine::fromspace_fault_handler
perry_runtime::gc::schedule::schedule_fault_handler
??   (inlined; likely is_callable_function_value reading the closure header)
perry_runtime::object::class_registry::prototype_methods::synthetic_class_id_for_function
js_register_function_prototype_method
perry_closure_node_modules_moment_moment_js__11
perry_closure_node_modules_moment_moment_js__433
perry_closure_node_modules_moment_moment_js__1
perry_closure_node_modules_moment_moment_js__0
node_modules_moment_moment_js__init_body
...run_path_initializer / js_run_module_init_catching / main

Notes (not verified)

  • The func_value passed to js_register_function_prototype_method from moment closure __11 still holds the closure's pre-minor address. So the codegen-side holder of Func across the Func.prototype.x = fn store is not rooted, or its root is not rewritten, across a preceding collection point. --trace hir --focus / --trace llvm on closure __11 should show which local it is.
  • There is a related hazard in synthetic_class_id_for_function itself: FUNCTION_CLASS_IDS is keyed by the closure's NaN-boxed bits. A closure that moves in a copying minor after its first registration would look up under a new key and get a fresh synthetic class id. That would split F.prototype methods from new F() instances. This is not the fault above, but it is the same path.

Not run

Only Linux x86_64 (perrymaster), release build with CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16, PERRY_NO_AUTO_OPTIMIZE=1. I did not check the other moment workload (diff_duration) or the macOS/arm64 path, and I did not dig into the root cause.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed defect or regression

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions