Skip to content

gc: test_gap_repsel_gc_stress SIGSEGVs intermittently in a copying minor with the conservative stack scan ON (PERRY_GC_INCREMENTAL=0) #6982

Description

@proggeramlug

Separate from the precise-roots family: this one crashes with the conservative native-stack scan ON, so it is not an unrooted-temporary bug.

Repro

Release build, macOS arm64, origin/main + #6977 (the fix in #6977 is not on this path — PERRY_GC_INCREMENTAL=0 bypasses the budgeted stepper entirely, so this reproduces on plain main given the same escape hatch).

$ perry test-files/test_gap_repsel_gc_stress.ts -o gc_stress
$ for i in 1 2; do PERRY_GC_HEAP_LIMIT=8 PERRY_GC_INCREMENTAL=0 ./gc_stress >/dev/null; echo "rc=$?"; done
rc=139
rc=139

Intermittent — roughly 2 of 3 runs. With PERRY_GC_TRACE=1 PERRY_GC_DIAG=1 added:

run1 rc=139  output mismatch
run2 rc=0    output matches the Node oracle
run3 rc=139  output mismatch

PERRY_GC_INCREMENTAL=0 is a documented escape hatch, and it is what makes gc_check_trigger take the #5476 direct-collection arm instead of handing the trigger to the budgeted stepper. On this path the collection is a real copying minor: the passing runs report [gc-copy-minor] ran copied_objects=... in the thousands.

Why it is a distinct defect

Every other corpus file passes on this exact configuration while relocating thousands of objects (see the table in the precise-roots issue — 19/20 OK in the conservative-scan column). test_gap_repsel_gc_stress is the one corpus member deliberately built to be GC-live, i.e. the one that drives the collector hardest, and it is the only one that crashes with conservative pinning still in force. So this is not "precise roots lost a value"; something in the copying minor itself is unsound under sustained churn.

The nondeterminism (same binary, same env, different outcome per run) points at a timing/ordering window rather than a fixed miscompile.

Why nobody saw it

test_gap_repsel_gc_stress is green in the matrix's shipped-configuration arms because in the shipped configuration it never takes this path: with incremental mode on, the budgeted cycle handles the trigger. #6950 documents that the shipped automatic paths never produce a copying minor at all. So the only way to reach this crash is the escape hatch, and nothing was running the escape hatch against the corpus until #6977 added the arm.

Suggested next step

Bisect against the copying-minor phases with PERRY_GC_VERIFY_EVACUATION=1 and PERRY_GC_VERIFY_MARK=1, and check whether the crash survives PERRY_GC_FORCE_EVACUATE=0 — i.e. whether it needs actual relocation or only the copying-minor code path. A core dump / lldb backtrace on a repro run should localise it quickly given how reliably it fires.

Related: #6950, #6977, #6892 (minor-sweep finalization), #6910.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions