From 4293dea57aa192e74e4772f7cc7af10ff57ae243 Mon Sep 17 00:00:00 2001 From: Gabe Rudy Date: Wed, 21 Oct 2026 19:43:18 -0400 Subject: [PATCH] Fix interior pointer resolution for medium blocks on 16K pages SmallHeapBlockT::GetRealAddressFromInterior narrowed the byte offset of an interior pointer within its block to ushort before shifting it down to an object index. A medium block is eight pages. With 4K pages that is 32K and every offset fits. On Apple arm64 the page is 16K, the block is 128K, and any offset past 64K lost its top bit and resolved to the object 64K lower in the same block. The marker reaches objects on the native stack through this function. HeapBlockMap32::MarkInterior sets the candidate's own mark bit first, then resolves and queues the object it believes the pointer belongs to. For a medium object in the upper half of its block that queued the wrong object, and the real one kept its mark bit without ever being scanned. When the same object was later reached through an exact heap pointer it was skipped as already marked, so everything referenced only from it was swept while still live. Async function and generator frames live on the heap as medium objects and the interpreter executes inside them, so a collection during an async function left its locals, including the await object, pointing at freed memory after the next resume. An embedder saw this as crashes in SetInlineSlot, StrictEqual, instanceof and JSON building on native Apple Silicon only; x64 and Rosetta use 4K pages and never truncate. The sibling GetAddressBitIndex was already fixed for this in the Apple Silicon page size change (015b05db5) by shifting before narrowing. Do the same here. Co-Authored-By: Claude Fable 5.1 --- lib/Common/Memory/Recycler.inl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Common/Memory/Recycler.inl b/lib/Common/Memory/Recycler.inl index df4dbf2300e..d2cb65bb722 100644 --- a/lib/Common/Memory/Recycler.inl +++ b/lib/Common/Memory/Recycler.inl @@ -617,8 +617,8 @@ SmallHeapBlockT::GetRealAddressFromInterior(void * interiorAdd const ValidPointers validPointers = HeapInfo::GetValidPointersMapForBucket(bucketIndex); size_t rawInteriorAddress = reinterpret_cast(interiorAddress); size_t baseAddress = rawInteriorAddress & ~(TBlockAttributes::PageCount * AutoSystemInfo::PageSize - 1); - ushort offset = (ushort)(rawInteriorAddress - baseAddress); - offset = validPointers.GetInteriorAddressIndex(offset >> HeapConstants::ObjectAllocationShift); + uint byteOffset = (uint)(rawInteriorAddress - baseAddress); + ushort offset = validPointers.GetInteriorAddressIndex(byteOffset >> HeapConstants::ObjectAllocationShift); if (offset == SmallHeapBlockT::InvalidAddressBit) {