Skip to content

fix(gc): widen field_count before a by-index FFI field store (#7164) - #7705

Merged
proggeramlug merged 2 commits into
mainfrom
fix/7164-by-index-field-count
Aug 9, 2026
Merged

fix(gc): widen field_count before a by-index FFI field store (#7164)#7705
proggeramlug merged 2 commits into
mainfrom
fix/7164-by-index-field-count

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Closes #7164.

perry_ffi::alloc_object() allocates with field_count = 0 and INLINE_SLOT_FLOOR physical slots. The documented by-index writer js_object_set_field bounds-checks field_index against max(field_count, INLINE_SLOT_FLOOR) — so it accepts an index at or above field_count — but never widened field_count to cover it.

object::gc_field_slot_range bounds the collector's view of the payload by field_count, and heap_payload_slot_selection short-circuits to Empty on an empty payload before consulting the per-object pointer mask. So a pointer written through this setter was invisible to both tracing (never marked → swept while live) and to evacuation rewriting (stale from-space pointer left in a live slot).

The 0x9 slot in the issue's SIGSEGV is downstream wreckage, not the defect — which is why the test does not chase it.

This is the by-index counterpart of the "#7154 publication order" invariant field_set_by_name/tail.rs already enforces at two sites; only this path was missing it. Widening before the store is safe: the existing bounds check already holds field_index below the physical capacity, and every physical slot is undefined-initialized at allocation, so the widened range can only expose non-pointer sentinels ahead of the store about to fill this one.

On the test not being vacuous

It asserts the defect deterministically, without racing a collector: it checks that gc_child_slots — the enumerator every mark/scan/evacuation pass calls — reports the written slot at all. Without the fix it enumerates []. It then runs a real copying minor gated on copied_objects > 0, so a run that collected nothing cannot report success.

Verified by reverting only the two-line widen and re-running:

#7164: js_object_set_field(obj, 0, ..) on a field_count=0 object must widen
field_count so the collector's view covers the written slot; the collector
enumerated [] (a raw field_count=0 leaves the whole payload range empty, so
the mask is never consulted)
test result: FAILED. 0 passed; 1 failed

cargo test -p perry-runtime --lib: 1945 passed, 0 failed.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed indexed object writes so newly populated fields are correctly tracked by garbage collection.
    • Ensured referenced values remain preserved and valid after memory compaction.
  • Tests

    • Added regression coverage for indexed writes to previously empty objects and copying garbage collection.
  • Documentation

    • Added a changelog entry describing the fix and its impact.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@proggeramlug, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 2 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f2a68791-175d-4d63-8bad-7b3313926701

📥 Commits

Reviewing files that changed from the base of the PR and between 2f55ffa and fc1744a.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • CLAUDE.md
  • Cargo.toml
📝 Walkthrough

Walkthrough

The indexed field setter now widens field_count before storing a higher-index value. A copying-GC regression test verifies slot enumeration and pointer relocation. A changelog entry documents the fix and invariant.

Changes

Indexed field publication and GC coverage

Layer / File(s) Summary
Widen field count before indexed storage
crates/perry-runtime/src/object/field_get_set/field_ops.rs
js_object_set_field increases field_count before storing a value at an index beyond the current logical count.
Verify copying-GC behavior
crates/perry-runtime/src/gc/tests/copying/pointer_publish_7154.rs, changelog.d/7164-ffi-by-index-field-count.md
The regression test verifies child-slot enumeration, field_count widening, and string preservation through copying collection. The changelog documents the behavior and safety invariant.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • PerryTS/perry#7179: Updates field_count before publishing object fields and adds copying-GC coverage.
  • PerryTS/perry#7643: Adds copying-GC regression coverage for pointer-slot enumeration and relocation through a different code path.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the GC fix: widening field_count before by-index FFI field stores.
Description check ✅ Passed The description explains the defect, fix, related issue, regression test, and verification results, although it omits the template headings and checklist.
Linked Issues check ✅ Passed The fix widens field_count before by-index FFI stores and the regression test verifies GC slot enumeration and copying, addressing [#7164].
Out of Scope Changes check ✅ Passed The code fix, regression test, and changelog entry directly support the linked issue and stated pull request objectives.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/7164-by-index-field-count

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Ralph Küpper added 2 commits August 9, 2026 16:34
The collector's payload view is bounded by field_count, so a store at an
index the count did not cover was invisible to tracing AND to evacuation
rewriting. Same publication-order invariant field_set_by_name/tail.rs
already enforces; only the by-index path was missing it.

Claude-Session: https://claude.ai/code/session_01Y1QZ5wUP9gRSwpiweT4Wix
@proggeramlug
proggeramlug force-pushed the fix/7164-by-index-field-count branch from 2f55ffa to fc1744a Compare August 9, 2026 14:34
@proggeramlug
proggeramlug merged commit 498e3d7 into main Aug 9, 2026
1 of 13 checks passed
@proggeramlug
proggeramlug deleted the fix/7164-by-index-field-count branch August 9, 2026 14:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gc: perry_ffi::alloc_object + later copying minor SIGSEGVs in scan_slot on a bogus slot (0x9) — documented-FFI-only repro

1 participant