Skip to content

fix(runtime): add acquire fence before dense subclass seqlock recheck - #8735

Closed
jdalton wants to merge 1 commit into
PerryTS:mainfrom
jdalton:fix/seqlock-acquire-fence-dense-layout
Closed

fix(runtime): add acquire fence before dense subclass seqlock recheck#8735
jdalton wants to merge 1 commit into
PerryTS:mainfrom
jdalton:fix/seqlock-acquire-fence-dense-layout

Conversation

@jdalton

@jdalton jdalton commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

This fixes a data race in the dense-array subclass layout cache where a reader could combine fields from two different cache publishers and return a wrong element value instead of crashing.

Root cause and fix - a relaxed payload read can be reordered past an acquire recheck on weakly-ordered hardware

cached_dense_layout reads two payload fields with relaxed ordering and then rechecks a sequence counter with an acquire load to make sure no writer touched the cache slot in between. An acquire load only holds back operations that come after it in the code, so it does nothing to stop the two payload reads that come before it from being reordered later by the CPU on weakly-ordered hardware like ARM. If a different cache key collides into the same slot and gets published while a reader is mid-read, the reader can end up combining one field from the old layout with the other field from the new layout. This adds an explicit acquire fence between the payload reads and the recheck, which is the standard fix for this kind of seqlock reader bug.

CodeRabbit originally flagged this exact issue on pull request #8668, which closed without the fix landing. This PR carries the same fix forward against current main.

cargo check -p perry-runtime --lib produces no diagnostics for array/subclass.rs. The crate currently fails to build for an unrelated, pre-existing reason: perf_histogram.rs calls algebraic_sub/algebraic_mul without the crate declaring #![feature(float_algebraic)]. I confirmed this is unrelated to this change by checking that every diagnostic file path points at perf_histogram.rs, never array/subclass.rs.

Summary by CodeRabbit

  • Bug Fixes
    • Improved array layout validation on systems with weaker memory-ordering guarantees.
    • Helps ensure reliable behavior when accessing cached array layout information.

cached_dense_layout loaded slots/bounds with Relaxed ordering then
rechecked sequence with Acquire, which only orders loads sequenced
after it, not the payload loads that precede it in program order. On
weakly ordered targets the payload loads could be reordered past the
recheck, letting a reader combine slots from one publisher with
bounds from a colliding publisher for a different cache key.

Flagged by coderabbitai on PerryTS#8668.
@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 568c25f5-950b-495a-af6f-27576262788d

📥 Commits

Reviewing files that changed from the base of the PR and between 0749bd3 and 0836797.

📒 Files selected for processing (1)
  • crates/perry-runtime/src/array/subclass.rs

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

cached_dense_layout now uses an Acquire fence before seqlock sequence revalidation. This orders relaxed payload loads before validation on weakly ordered targets.

Changes

Dense layout cache

Layer / File(s) Summary
Acquire ordering in layout reads
crates/perry-runtime/src/array/subclass.rs
cached_dense_layout adds an Acquire fence before the sequence-number recheck.

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

Merge Risk: ⚪ Minimal · up to 08367

The PR adds an acquire fence to prevent inconsistent dense-layout cache reads. No actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers: proggeramlug

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the root cause, fix, and test limitation, but it omits the required Changes, Related issue, Test plan, and Checklist sections. Use the repository template and add the missing sections, including concrete changes, issue status, test commands or results, and checklist confirmations.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the runtime fix and the acquire fence added before the dense subclass seqlock recheck.
Docstring Coverage ✅ Passed Docstring check was indeterminate for this PR — some files could not be analyzed in time. Not blocking.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

proggeramlug added a commit that referenced this pull request Aug 24, 2026
…his-methods and proven Symbols (#8737)

Lands #8735, #8729 and #8731.

#8735 fixes a real data race in the dense-array subclass layout cache.
`cached_dense_layout` read two payload fields `Relaxed` and then rechecked
the sequence counter with an `Acquire` LOAD -- but an acquire load only
constrains what follows it, so on weakly ordered hardware the preceding
payload reads could sink past the recheck. A reader racing a colliding
publisher could then combine one field from the old layout with one from
the new and return a WRONG ELEMENT rather than faulting. A standalone
`fence(Acquire)` now sits between the payload reads and the recheck, which
is the canonical seqlock-reader form.

#8729 lowers strict equality against a proven Symbol to raw NaN-boxed
identity, keeping loose equality, reassigned locals and erased TypeScript
annotation claims on the semantic runtime helpers.

#8731 (fixes #8693) publishes producer-authoritative proven-`this` method
capabilities through imports, aliases and re-exports, emitting guarded
direct imported clone calls while retaining generic dispatch fallbacks.

A changelog fragment was added for #8735, which had neither one nor a
skip-changelog label.

No version bump.

Co-authored-by: Ralph Küpper <ralph@skelpo.com>
@proggeramlug

Copy link
Copy Markdown
Contributor

Landed on main via #8737 (squash c203c772c), with #8729 and #8731.

Good catch, and the reasoning is exactly right — an Acquire load constrains what follows it, so it does nothing to stop the preceding Relaxed payload loads from sinking past the recheck on weakly-ordered hardware. A standalone fence(Acquire) between the reads and the recheck is the canonical seqlock-reader form. The failure shape you describe is the nasty one too: combining fields from two publishers returns a wrong element value rather than faulting, so it would surface as silent data corruption rather than a crash.

I added a changelog.d/ fragment — the PR had neither one nor a skip-changelog label.

Validated on the merged result: all 30 lint checkers, runtime 2669/0 at RUST_TEST_THREADS=1, codegen 1222/0, all codegen integration suites clean.

@jdalton

jdalton commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

@proggeramlug Fast, before I could fix the lint issue :P

@jdalton
jdalton deleted the fix/seqlock-acquire-fence-dense-layout branch August 24, 2026 12:40
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.

2 participants