From 17eefbbbd9131ac8a4f7f7574c799e314a5fb597 Mon Sep 17 00:00:00 2001 From: Michon <231849@buas.nl> Date: Thu, 1 Oct 2026 18:50:15 +0200 Subject: [PATCH] fix(libc): reserve the guest arena at its fixed range around what Windows already placed in it The arena is always [0x200000000, 0x7200000000). Every free, allocation-granularity-aligned piece of the range is reserved as a placeholder; what lies between the pieces (thread stacks and other reservations Windows made before libc starts, unaligned slivers) and the system-reserved range are holes, used ranges a release never frees. A failed query or reservation of the range throws. Co-Authored-By: Claude Opus 5.5 (1M context) --- core/libs/prx/libc/src/GuestArena.cpp | 44 ++++++++++++++++----------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/core/libs/prx/libc/src/GuestArena.cpp b/core/libs/prx/libc/src/GuestArena.cpp index dc87e282e6..1041790e88 100644 --- a/core/libs/prx/libc/src/GuestArena.cpp +++ b/core/libs/prx/libc/src/GuestArena.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #ifdef _WIN32 #include @@ -16,12 +18,10 @@ namespace GuestArena { namespace { -constexpr std::uintptr_t PreferredBase = 0x0000000200000000ull; +constexpr std::uintptr_t ArenaStart = 0x0000000200000000ull; constexpr std::uintptr_t SystemReservedStart = 0x00000007FFFFC000ull; constexpr std::uintptr_t SystemReservedEnd = 0x0000001000000000ull; -constexpr std::size_t MaximumSize = 0x0000007000000000ull; -constexpr std::size_t MinimumSize = 0x0000001000000000ull; -constexpr std::uintptr_t MapAreaEnd = 0x000000FC00000000ull; +constexpr std::size_t ArenaSize = 0x0000007000000000ull; std::uintptr_t alignUp(std::uintptr_t value, std::size_t alignment) { return (value + alignment - 1) & ~(static_cast(alignment) - 1); @@ -83,30 +83,40 @@ public: if (rangeStart < start) _used.emplace(rangeStart, start); if (rangeEnd > end) _used.emplace(end, rangeEnd); } + for (const auto& [holeStart, holeEnd] : _holes) { + if (holeStart < end && start < holeEnd) _used.emplace(holeStart, holeEnd); + } } private: Arena() { #ifdef _WIN32 - // Windows places other reservations randomly, so take the lowest base and largest size that fit. - for (std::uintptr_t base = PreferredBase; base + MinimumSize <= MapAreaEnd; base += MinimumSize) { - for (std::size_t size = MaximumSize; size >= MinimumSize; size /= 2) { - if (base + size > MapAreaEnd) continue; - _writeWatched = std::getenv("APS5_NO_WRITE_WATCH") == nullptr; - void* reserved = WindowsMappings::Get().Reserve(reinterpret_cast(base), size); - if (!reserved) continue; - _base = reinterpret_cast(reserved); - _end = _base + size; - if (_base < SystemReservedEnd && SystemReservedStart < _end) _used.emplace(std::max(_base, SystemReservedStart), std::min(_end, SystemReservedEnd)); - return; - } + _writeWatched = std::getenv("APS5_NO_WRITE_WATCH") == nullptr; + SYSTEM_INFO system{}; + GetSystemInfo(&system); + const std::uintptr_t granularity = system.dwAllocationGranularity; + const std::uintptr_t end = ArenaStart + ArenaSize; + for (std::uintptr_t cursor = ArenaStart; cursor < end;) { + MEMORY_BASIC_INFORMATION info{}; + if (VirtualQuery(reinterpret_cast(cursor), &info, sizeof(info)) == 0) throw std::system_error(static_cast(GetLastError()), std::system_category(), "query the guest arena range"); + const auto regionEnd = std::min(end, reinterpret_cast(info.BaseAddress) + info.RegionSize); + const auto first = info.State == MEM_FREE ? std::min(regionEnd, alignUp(cursor, granularity)) : regionEnd; + const auto last = std::max(first, regionEnd & ~(granularity - 1)); + if (first < last && WindowsMappings::Get().Reserve(reinterpret_cast(first), last - first) == nullptr) throw std::system_error(static_cast(GetLastError()), std::system_category(), "reserve the guest arena range"); + if (cursor < first) _holes.emplace_back(cursor, first); + if (last < regionEnd) _holes.emplace_back(last, regionEnd); + cursor = regionEnd; } - std::fprintf(stderr, "[memory] guest arena unavailable below 0x%llx\n", static_cast(MapAreaEnd)); + _holes.emplace_back(SystemReservedStart, SystemReservedEnd); + for (const auto& [holeStart, holeEnd] : _holes) _used.emplace(holeStart, holeEnd); + _base = ArenaStart; + _end = end; #endif } std::mutex _lock; std::map _used; + std::vector> _holes; std::uintptr_t _base = 0; std::uintptr_t _end = 0; bool _writeWatched = false;