memory: remove per-page host protection bookkeeping

This commit is contained in:
nmzik
2026-09-11 12:49:30 +04:00
parent 0cd7682012
commit 73363f1272
6 changed files with 21 additions and 115 deletions
+9 -76
View File
@@ -31,8 +31,7 @@
namespace Common { namespace Common {
static pthread_mutex_t g_virtual_mutex {}; static pthread_mutex_t g_virtual_mutex {};
static std::map<uintptr_t, size_t>* g_allocs = nullptr; static std::map<uintptr_t, size_t>* g_allocs = nullptr;
static std::map<uintptr_t, int>* g_protects = nullptr;
void SysVirtualInit() { void SysVirtualInit() {
pthread_mutexattr_t attr {}; pthread_mutexattr_t attr {};
@@ -46,8 +45,7 @@ void SysVirtualInit() {
pthread_mutex_init(&g_virtual_mutex, &attr); pthread_mutex_init(&g_virtual_mutex, &attr);
pthread_mutexattr_destroy(&attr); pthread_mutexattr_destroy(&attr);
g_allocs = new std::map<uintptr_t, size_t>; g_allocs = new std::map<uintptr_t, size_t>;
g_protects = new std::map<uintptr_t, int>;
} }
static int get_protection_flag(VirtualMemory::Mode mode) { static int get_protection_flag(VirtualMemory::Mode mode) {
@@ -68,21 +66,6 @@ static int get_protection_flag(VirtualMemory::Mode mode) {
return protect; return protect;
} }
static VirtualMemory::Mode get_protection_flag(int mode) {
switch (mode) {
case PROT_NONE: return VirtualMemory::Mode::NoAccess;
case PROT_READ: return VirtualMemory::Mode::Read;
case PROT_WRITE: return VirtualMemory::Mode::Write;
case PROT_READ | PROT_WRITE: return VirtualMemory::Mode::ReadWrite; // NOLINT
case PROT_EXEC: return VirtualMemory::Mode::Execute;
case PROT_EXEC | PROT_WRITE: return VirtualMemory::Mode::ExecuteWrite; // NOLINT
case PROT_EXEC | PROT_READ: return VirtualMemory::Mode::ExecuteRead; // NOLINT
case PROT_EXEC | PROT_WRITE | PROT_READ:
return VirtualMemory::Mode::ExecuteReadWrite; // NOLINT
default: return VirtualMemory::Mode::NoAccess;
}
}
// Keep automatic mappings inside the guest and GPU-addressable low window. // Keep automatic mappings inside the guest and GPU-addressable low window.
#ifdef KYTY_FIXED_NOREPLACE #ifdef KYTY_FIXED_NOREPLACE
static constexpr uintptr_t LOW_ARENA_LIMIT = 0x000000FC00000000ULL; // libc mspace window ceiling static constexpr uintptr_t LOW_ARENA_LIMIT = 0x000000FC00000000ULL; // libc mspace window ceiling
@@ -161,11 +144,6 @@ uint64_t SysVirtualAlloc(uint64_t address, uint64_t size, VirtualMemory::Mode mo
if (ptr != MAP_FAILED) { if (ptr != MAP_FAILED) {
pthread_mutex_lock(&g_virtual_mutex); pthread_mutex_lock(&g_virtual_mutex);
record_alloc(ret_addr, size); record_alloc(ret_addr, size);
uintptr_t page_start = ret_addr >> 12u;
uintptr_t page_end = (ret_addr + size - 1) >> 12u;
for (uintptr_t page = page_start; page <= page_end; page++) {
(*g_protects)[page] = protect;
}
pthread_mutex_unlock(&g_virtual_mutex); pthread_mutex_unlock(&g_virtual_mutex);
} }
@@ -251,11 +229,6 @@ uint64_t SysVirtualAllocAligned(uint64_t address, uint64_t size, VirtualMemory::
pthread_mutex_lock(&g_virtual_mutex); pthread_mutex_lock(&g_virtual_mutex);
record_alloc(ret_addr, size); record_alloc(ret_addr, size);
uintptr_t page_start = ret_addr >> 12u;
uintptr_t page_end = (ret_addr + size - 1) >> 12u;
for (uintptr_t page = page_start; page <= page_end; page++) {
(*g_protects)[page] = protect;
}
pthread_mutex_unlock(&g_virtual_mutex); pthread_mutex_unlock(&g_virtual_mutex);
return ret_addr; return ret_addr;
@@ -337,11 +310,6 @@ bool SysVirtualAllocFixed(uint64_t address, uint64_t size, VirtualMemory::Mode m
if (ptr != MAP_FAILED) { if (ptr != MAP_FAILED) {
pthread_mutex_lock(&g_virtual_mutex); pthread_mutex_lock(&g_virtual_mutex);
record_alloc(ret_addr, size); record_alloc(ret_addr, size);
uintptr_t page_start = ret_addr >> 12u;
uintptr_t page_end = (ret_addr + size - 1) >> 12u;
for (uintptr_t page = page_start; page <= page_end; page++) {
(*g_protects)[page] = protect;
}
pthread_mutex_unlock(&g_virtual_mutex); pthread_mutex_unlock(&g_virtual_mutex);
return true; return true;
@@ -516,18 +484,7 @@ bool SysVirtualFree(uint64_t address) {
return false; return false;
} }
if (munmap(reinterpret_cast<void*>(addr), size) == 0) { return munmap(reinterpret_cast<void*>(addr), size) == 0;
uintptr_t page_start = addr >> 12u;
uintptr_t page_end = (addr + size - 1) >> 12u;
pthread_mutex_lock(&g_virtual_mutex);
for (uintptr_t page = page_start; page <= page_end; page++) {
g_protects->erase(page);
}
pthread_mutex_unlock(&g_virtual_mutex);
return true;
}
return false;
} }
bool SysVirtualFreeRange(uint64_t address, uint64_t size) { bool SysVirtualFreeRange(uint64_t address, uint64_t size) {
@@ -582,40 +539,16 @@ bool SysVirtualFreeRange(uint64_t address, uint64_t size) {
if (end < alloc_end) { if (end < alloc_end) {
(*g_allocs)[end] = alloc_end - end; (*g_allocs)[end] = alloc_end - end;
} }
for (uintptr_t page = addr >> 12u; page <= (end - 1u) >> 12u; page++) {
g_protects->erase(page);
}
pthread_mutex_unlock(&g_virtual_mutex); pthread_mutex_unlock(&g_virtual_mutex);
return true; return true;
} }
bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode, bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
VirtualMemory::Mode* old_mode) { const auto addr = static_cast<uintptr_t>(address);
auto addr = static_cast<uintptr_t>(address); const auto page_start = addr >> 12u;
const auto page_end = (addr + size - 1) >> 12u;
pthread_mutex_lock(&g_virtual_mutex); return mprotect(reinterpret_cast<void*>(page_start << 12u), (page_end - page_start + 1) << 12u,
if (old_mode != nullptr) { get_protection_flag(mode)) == 0;
if (auto s = g_protects->find(addr >> 12u); s != g_protects->end()) {
*old_mode = get_protection_flag(s->second);
} else {
*old_mode = VirtualMemory::Mode::NoAccess;
}
}
pthread_mutex_unlock(&g_virtual_mutex);
uintptr_t page_start = addr >> 12u;
uintptr_t page_end = (addr + size - 1) >> 12u;
if (mprotect(reinterpret_cast<void*>(page_start << 12u), (page_end - page_start + 1) << 12u,
get_protection_flag(mode)) == 0) {
pthread_mutex_lock(&g_virtual_mutex);
for (uintptr_t page = page_start; page <= page_end; page++) {
(*g_protects)[page] = get_protection_flag(mode);
}
pthread_mutex_unlock(&g_virtual_mutex);
return true;
}
return false;
} }
bool SysVirtualFlushInstructionCache(uint64_t /*address*/, uint64_t /*size*/) { bool SysVirtualFlushInstructionCache(uint64_t /*address*/, uint64_t /*size*/) {
+1 -2
View File
@@ -19,8 +19,7 @@ bool SysVirtualReserveFixed(uint64_t address, uint64_t size);
bool SysVirtualDecommit(uint64_t address, uint64_t size); bool SysVirtualDecommit(uint64_t address, uint64_t size);
bool SysVirtualFree(uint64_t address); bool SysVirtualFree(uint64_t address);
bool SysVirtualFreeRange(uint64_t address, uint64_t size); bool SysVirtualFreeRange(uint64_t address, uint64_t size);
bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode, bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode);
VirtualMemory::Mode* old_mode = nullptr);
bool SysVirtualFlushInstructionCache(uint64_t address, uint64_t size); bool SysVirtualFlushInstructionCache(uint64_t address, uint64_t size);
} // namespace Common } // namespace Common
+1 -17
View File
@@ -45,18 +45,6 @@ static DWORD GetProtectionFlag(VirtualMemory::Mode mode) {
return protect; return protect;
} }
static VirtualMemory::Mode GetProtectionFlag(DWORD mode) {
switch (mode) {
case PAGE_NOACCESS: return VirtualMemory::Mode::NoAccess;
case PAGE_READONLY: return VirtualMemory::Mode::Read;
case PAGE_READWRITE: return VirtualMemory::Mode::ReadWrite;
case PAGE_EXECUTE: return VirtualMemory::Mode::Execute;
case PAGE_EXECUTE_READ: return VirtualMemory::Mode::ExecuteRead;
case PAGE_EXECUTE_READWRITE: return VirtualMemory::Mode::ExecuteReadWrite;
default: return VirtualMemory::Mode::NoAccess;
}
}
void SysVirtualInit() {} void SysVirtualInit() {}
uint64_t SysVirtualAlloc(uint64_t address, uint64_t size, VirtualMemory::Mode mode) { uint64_t SysVirtualAlloc(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
@@ -328,17 +316,13 @@ bool SysVirtualFreeRange(uint64_t address, uint64_t size) {
return current - address == size && SysVirtualFree(address); return current - address == size && SysVirtualFree(address);
} }
bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode, bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
VirtualMemory::Mode* old_mode) {
DWORD old_protect = 0; DWORD old_protect = 0;
if (VirtualProtect(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size, if (VirtualProtect(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
GetProtectionFlag(mode), &old_protect) == 0) { GetProtectionFlag(mode), &old_protect) == 0) {
printf("VirtualProtect() failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError())); printf("VirtualProtect() failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
return false; return false;
} }
if (old_mode != nullptr) {
*old_mode = GetProtectionFlag(old_protect);
}
return true; return true;
} }
+2 -2
View File
@@ -50,8 +50,8 @@ bool FreeRange(uint64_t address, uint64_t size) {
return SysVirtualFreeRange(address, size); return SysVirtualFreeRange(address, size);
} }
bool Protect(uint64_t address, uint64_t size, Mode mode, Mode* old_mode) { bool Protect(uint64_t address, uint64_t size, Mode mode) {
return SysVirtualProtect(address, size, mode, old_mode); return SysVirtualProtect(address, size, mode);
} }
bool FlushInstructionCache(uint64_t address, uint64_t size) { bool FlushInstructionCache(uint64_t address, uint64_t size) {
+1 -1
View File
@@ -35,7 +35,7 @@ bool ReserveFixed(uint64_t address, uint64_t size);
bool Decommit(uint64_t address, uint64_t size); bool Decommit(uint64_t address, uint64_t size);
bool Free(uint64_t address); bool Free(uint64_t address);
bool FreeRange(uint64_t address, uint64_t size); bool FreeRange(uint64_t address, uint64_t size);
bool Protect(uint64_t address, uint64_t size, Mode mode, Mode* old_mode = nullptr); bool Protect(uint64_t address, uint64_t size, Mode mode);
bool FlushInstructionCache(uint64_t address, uint64_t size); bool FlushInstructionCache(uint64_t address, uint64_t size);
} // namespace VirtualMemory } // namespace VirtualMemory
+7 -17
View File
@@ -1147,23 +1147,13 @@ void TestDirectPartialProtectUnmapPreservesNeighbors() {
Libs::LibKernel::Memory::KernelMunmap(base + SceKernelPageSize, SceKernelPageSize), Libs::LibKernel::Memory::KernelMunmap(base + SceKernelPageSize, SceKernelPageSize),
"KernelMunmap(middle)"); "KernelMunmap(middle)");
Common::VirtualMemory::Mode old_left {}; // Access the neighbors without changing their host permissions first.
Common::VirtualMemory::Mode old_right {}; auto* left = reinterpret_cast<volatile uint64_t*>(base);
Check(test, auto* right = reinterpret_cast<volatile uint64_t*>(base + SceKernelPageSize * 2);
Common::VirtualMemory::Protect(base, SceKernelPageSize, *left = 0x4c45465450524f54ull; // "LEFTPROT"
Common::VirtualMemory::Mode::ReadWrite, &old_left), *right = 0x5247485450524f54ull; // "RGHTPROT"
"could not inspect left-page protection"); Check(test, *left == 0x4c45465450524f54ull, "partial unmap changed the left neighbor access");
Check(test, Check(test, *right == 0x5247485450524f54ull, "partial unmap changed the right neighbor access");
Common::VirtualMemory::Protect(base + SceKernelPageSize * 2, SceKernelPageSize,
Common::VirtualMemory::Mode::ReadWrite, &old_right),
"could not inspect right-page protection");
Check(test, old_left == Common::VirtualMemory::Mode::ReadWrite,
"partial unmap changed the left neighbor protection");
Check(test, old_right == Common::VirtualMemory::Mode::ReadWrite,
"partial unmap changed the right neighbor protection");
*reinterpret_cast<uint64_t*>(base) = 0x4c45465450524f54ull; // "LEFTPROT"
*reinterpret_cast<uint64_t*>(base + SceKernelPageSize * 2) =
0x5247485450524f54ull; // "RGHTPROT"
CheckOk(test, Libs::LibKernel::Memory::KernelReleaseDirectMemory(phys_addr, size), CheckOk(test, Libs::LibKernel::Memory::KernelReleaseDirectMemory(phys_addr, size),
"KernelReleaseDirectMemory"); "KernelReleaseDirectMemory");