From f90e282444d99595cfd030f85d80465d0571d7de Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 13 Jul 2026 00:43:46 +0000 Subject: [PATCH] speculationFromCell: reject misaligned StringImpl* from concurrent zap residue On weak-memory-order architectures (observed on Windows arm64 in Bun CI), the DFG compiler thread can read a JSString from a ValueProfile bucket before the main thread's m_fiber store becomes visible. With no release fence between construction and the bucket store (mutatorFence() is a no-op outside concurrent marking), the compiler thread observes the slot's previous bytes: HeapCell::zap(StopAllocating) residue. zap() writes cellWords[2] = ZapReason (= 2 for StopAllocating) and leaves cellWords[3] intact, so m_fiber reads as {low32=2, high32=prev-hi-bits}, e.g. 0x0000021700000002. isSanePointer() passes (below 48 bits, above page 0), tryGetValueImpl() sees bit 0 clear so returns it as a StringImpl*, and impl->isAtom() segfaults at [impl + 0x10]. StringImpl is at least 8-byte aligned (pointer member), so rejecting misaligned impl pointers catches the zap residue without touching real values. Extends the existing rdar://69036888 band-aid to cover the case isSanePointer lets through. --- Source/JavaScriptCore/bytecode/SpeculatedType.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/JavaScriptCore/bytecode/SpeculatedType.cpp b/Source/JavaScriptCore/bytecode/SpeculatedType.cpp index 8532f1c6df047..d74f71d9f270c 100644 --- a/Source/JavaScriptCore/bytecode/SpeculatedType.cpp +++ b/Source/JavaScriptCore/bytecode/SpeculatedType.cpp @@ -602,7 +602,9 @@ SpeculatedType speculationFromCell(JSCell* cell) if (cell->isString()) { JSString* string = uncheckedDowncast(cell); if (const StringImpl* impl = string->tryGetValueImpl()) { - if (!Integrity::isSanePointer(impl)) [[unlikely]] { + // Alignment check rejects HeapCell::zap residue in m_fiber seen on weak-memory-order + // CPUs when a freshly-allocated JSString is read before its m_fiber store is visible. + if (!Integrity::isSanePointer(impl) || (reinterpret_cast(impl) & (alignof(StringImpl) - 1))) [[unlikely]] { ASSERT_NOT_REACHED(); return SpecNone; }