Skip to content

gc: a deleted write barrier passes every runtime probe — only a static IR assertion can catch it #8185

Description

@proggeramlug

A deleted GC write barrier passes every runtime probe we have

Found while validating #8183. A release build with the write barrier deleted from the dynamic-key IC's new reference-store arm passes the entire behavioural matrix:

  • old→young edge fixtures
  • PERRY_GC_FORCE_EVACUATE=1 PERRY_GC_VERIFY_EVACUATION=1
  • PERRY_GEN_GC=0
  • PERRY_GC_ZEAL=1 + from-space protection at depth 400

Byte-identical output, exit 0, on both the gap fixture and a larger adversarial one. The static IR test was the only thing that said no.

Why this matters more than one PR

CLAUDE.md's rooting-invariant guidance says the runtime instruments are the detector for the class the static checker cannot see (a runtime-side cache of a heap pointer). This is the mirror image and it is not written down anywhere: for a missing write barrier, the runtime instruments are the ones that cannot see it, and the static IR assertion is the only detector.

The reason is structural. A missing barrier does not corrupt anything at the moment of the store. It leaves a remembered-set entry unwritten, so a later minor collection fails to trace an old→young edge and drops a live object. Whether that happens depends on the object surviving to old-gen, the child staying young, and a minor landing in that window — none of which a short fixture reliably produces, and FORCE_EVACUATE/VERIFY_EVACUATION do not manufacture it either because they verify rewriting, not remembering.

This is the same shape as CLAUDE.md's hazard 4 — "the gate runs but its subject never did" — except the subject here is a negative: the absence of a barrier cannot be observed by running the program.

Consequences worth acting on

  1. Any PR that adds or moves a store on a GC-managed slot needs a static IR assertion, not a behavioural test, as its barrier evidence. perf(codegen): store reference values inline in the dynamic-key write IC (#8108) #8183 has one (br i1 into the arm, plus barrier/layout-note/addref presence); it should be the template.
  2. A green GC-canary run is not evidence a barrier is present. Several PRs in this campaign have cited FORCE_EVACUATE + VERIFY_EVACUATION as barrier evidence. That inference is invalid — it is evidence about rewriting, which is a different property.
  3. Worth checking whether scripts/gc_store_site_inventory.py can be extended to assert barrier presence per store site, rather than only inventorying the sites. If it can, this becomes a gate instead of a review convention.

Provenance

Sabotage arms run against #8183, all four caught by its two IR tests: drop the write barrier; drop the layout note + string addref; route reference values back to put.dynic.slow; leak a barrier into the scalar arm. The third initially passed — the arm became dead IR — which is why that PR added a br i1-into-the-arm assertion. That near-miss is the same lesson twice in one PR: presence of code is not proof it is reached, and absence of a crash is not proof a barrier exists.

Refs #8183, #7511, #7154.

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