From 0836797d5f4dcb38778e5c8e41d8f0a160a4b74b Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Mon, 24 Aug 2026 00:59:32 -0400 Subject: [PATCH] fix(runtime): add acquire fence before dense subclass seqlock recheck 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/perry#8668. --- crates/perry-runtime/src/array/subclass.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/perry-runtime/src/array/subclass.rs b/crates/perry-runtime/src/array/subclass.rs index dd2eada043..dbf6b8e5d2 100644 --- a/crates/perry-runtime/src/array/subclass.rs +++ b/crates/perry-runtime/src/array/subclass.rs @@ -81,7 +81,11 @@ fn cached_dense_layout(key: u64) -> Option { let slots = entry.slots.load(Ordering::Relaxed); let bounds = entry.bounds.load(Ordering::Relaxed); // Recheck the seqlock before interpreting either word so readers never - // combine payloads from two colliding publishers. + // combine payloads from two colliding publishers. The fence stops the + // relaxed payload loads above from being reordered past this recheck on + // weakly ordered targets - an Acquire load alone only orders what comes + // after it, not what precedes it in program order. + std::sync::atomic::fence(Ordering::Acquire); if entry.sequence.load(Ordering::Acquire) != sequence { return None; }