From da2fca25bf8ae50892eb9c85c194c692fdc84e87 Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Sun, 1 Mar 2026 11:15:29 +0000 Subject: [PATCH] fix(windows): use tryProtect in BufferMemoryHandle destructor to prevent mprotect crash On Windows, the BufferMemoryHandle destructor calls OSAllocator::protect() to restore page permissions before freeing memory. This calls VirtualAlloc(MEM_COMMIT) which fails with ERROR_INVALID_ADDRESS (487) if the underlying virtual memory reservation was already released by the allocator (libpas recycling virtual pages). This manifests as a flaky crash in Bun's buffer tests on Windows: mprotect failed: 487 (exit code 0xC0000409 / STATUS_STACK_BUFFER_OVERRUN) The protect-before-free pattern is unnecessary on Windows because VirtualFree(MEM_RELEASE) releases memory regardless of page protection state (unlike POSIX where some free paths use madvise which may require accessible pages). Replace OSAllocator::protect() with OSAllocator::tryProtect() in both the Signaling and Shared BoundsChecking destructor paths on Windows. Non-Windows platforms continue to use the fatal protect() call. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../runtime/BufferMemoryHandle.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Source/JavaScriptCore/runtime/BufferMemoryHandle.cpp b/Source/JavaScriptCore/runtime/BufferMemoryHandle.cpp index 83c694c61c7f..5fda4fb51c2d 100644 --- a/Source/JavaScriptCore/runtime/BufferMemoryHandle.cpp +++ b/Source/JavaScriptCore/runtime/BufferMemoryHandle.cpp @@ -250,7 +250,15 @@ BufferMemoryHandle::~BufferMemoryHandle() // nullBasePointer's zero-sized memory is not used for MemoryMode::Signaling. constexpr bool readable = true; constexpr bool writable = true; +#if OS(WINDOWS) + // On Windows, VirtualFree(MEM_RELEASE) releases memory regardless of page + // protection state, so the protect call is not strictly necessary. Use + // tryProtect to avoid crashing if the underlying reservation was already + // released by the allocator (e.g. libpas recycling the virtual pages). + OSAllocator::tryProtect(memory, BufferMemoryHandle::fastMappedBytes(), readable, writable); +#else OSAllocator::protect(memory, BufferMemoryHandle::fastMappedBytes(), readable, writable); +#endif BufferMemoryManager::singleton().freeFastMemory(memory); break; } @@ -269,7 +277,15 @@ BufferMemoryHandle::~BufferMemoryHandle() } constexpr bool readable = true; constexpr bool writable = true; +#if OS(WINDOWS) + // On Windows, VirtualFree(MEM_RELEASE) releases memory regardless of page + // protection state, so the protect call is not strictly necessary. Use + // tryProtect to avoid crashing if the underlying reservation was already + // released by the allocator (e.g. libpas recycling the virtual pages). + OSAllocator::tryProtect(memory, m_mappedCapacity, readable, writable); +#else OSAllocator::protect(memory, m_mappedCapacity, readable, writable); +#endif BufferMemoryManager::singleton().freeGrowableBoundsCheckingMemory(memory, m_mappedCapacity); break; }