From f094752f2194087eedaac22de2e6e63a1ad11d40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 31 Aug 2026 12:52:35 +0200 Subject: [PATCH] MemoryUtil: fall back to far memory when the near reservation fails The x86-64 path searched for free memory near the code, and if it found some, committed to it - if that VirtualAlloc failed, ptr was left null and we returned null, never reaching the else branch that exists precisely to say "can still run, thanks to RipAccessible". Finding a free region isn't the same as being able to reserve it. VirtualAlloc rounds a non-null lpAddress down to the 64K allocation granularity while SearchForFreeMem only guarantees page alignment, so the rounded-down base can land back inside a committed region; a concurrent allocation between the VirtualQuery and the VirtualAlloc does it too. Callers don't check the result - AllocCodeSpace stores it unchecked and the emitters write from there - so this turned into a wild write rather than a clean JIT-unavailable fallback. --- Common/MemoryUtil.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Common/MemoryUtil.cpp b/Common/MemoryUtil.cpp index be2a14ddbe..d4de2cf131 100644 --- a/Common/MemoryUtil.cpp +++ b/Common/MemoryUtil.cpp @@ -145,8 +145,17 @@ void *AllocateExecutableMemory(size_t size) { #endif if (ptr) { ptr = VirtualAlloc(ptr, aligned_size, MEM_RESERVE | MEM_COMMIT, prot); + if (!ptr) { + // Finding a free region isn't the same as being able to reserve it: VirtualAlloc + // rounds a non-null address down to the 64K allocation granularity, and + // SearchForFreeMem only returns page alignment, so the rounded-down base can land + // back inside something committed. Another thread allocating in between does it too. + WARN_LOG(Log::Common, "Could not reserve the nearby executable memory found for jit. Proceeding with far memory."); + } } else { WARN_LOG(Log::Common, "Unable to find nearby executable memory for jit. Proceeding with far memory."); + } + if (!ptr) { // Can still run, thanks to "RipAccessible". ptr = VirtualAlloc(nullptr, aligned_size, MEM_RESERVE | MEM_COMMIT, prot); }