From 323eb72b7c3175ad71df91cdd59da309ffa38687 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 28 Aug 2016 13:35:27 +0200 Subject: [PATCH] Write-protect the dispatcher on all platforms. --- Common/MemoryUtil.cpp | 25 ++++++++++++++++++++----- Common/MemoryUtil.h | 15 +++++++-------- Common/x64Emitter.cpp | 6 ++++-- Core/MIPS/ARM/ArmAsm.cpp | 6 +++++- Core/MIPS/ARM64/Arm64Asm.cpp | 5 ++++- Core/MIPS/x86/Asm.cpp | 7 +++++-- 6 files changed, 45 insertions(+), 19 deletions(-) diff --git a/Common/MemoryUtil.cpp b/Common/MemoryUtil.cpp index 74bc8bdc4b..98a263bd1d 100644 --- a/Common/MemoryUtil.cpp +++ b/Common/MemoryUtil.cpp @@ -291,6 +291,10 @@ bool PlatformIsWXExclusive() { } void ProtectMemoryPages(void* ptr, size_t size, uint32_t memProtFlags) { + if (PlatformIsWXExclusive()) { + if ((memProtFlags & (MEM_PROT_WRITE | MEM_PROT_EXEC)) == (MEM_PROT_WRITE | MEM_PROT_EXEC)) + PanicAlert("Bad memory protect : W^X is in effect, can't both write and exec"); + } #ifdef _WIN32 uint32_t protect = ConvertProtFlagsWin32(memProtFlags); DWORD oldValue; @@ -300,10 +304,21 @@ void ProtectMemoryPages(void* ptr, size_t size, uint32_t memProtFlags) { // Do nothing #else uint32_t protect = ConvertProtFlagsUnix(memProtFlags); - if (PlatformIsWXExclusive()) { - if ((flags & (MEM_PROT_WRITE | MEM_PROT_EXEC)) == (MEM_PROT_WRITE | MEM_PROT_EXEC)) - PanicAlert("Bad memory protect : W^X is in effect, can't both write and exec"); - } mprotect(ptr, size, protect); #endif -} \ No newline at end of file +} + +// Hardcoded in the header. This is a way to check it dynamically in case it ever becomes necessary. +/* +#ifdef _WIN32 + +// 4k on most archs, 8k on Itanium (but who cares). +// Should not be confused with the allocation granularity which is 65k and is how precise +// VirtualAlloc allocations can be located in memory. We don't really care about that much though. +int GetMemoryProtectPageSize() { + if (sys_info.dwPageSize == 0) + GetSystemInfo(&sys_info); + return sys_info.dwPageSize; +} +#endif +*/ \ No newline at end of file diff --git a/Common/MemoryUtil.h b/Common/MemoryUtil.h index 6d63590adc..eb2f5404d9 100644 --- a/Common/MemoryUtil.h +++ b/Common/MemoryUtil.h @@ -29,18 +29,21 @@ using std::size_t; // Returns true if we need to avoid setting both writable and executable at the same time (W^X) bool PlatformIsWXExclusive(); - #define MEM_PROT_READ 1 #define MEM_PROT_WRITE 2 #define MEM_PROT_EXEC 4 // Note that some platforms go through special contortions to allocate executable memory. So for memory // that's intended for execution, allocate it first using AllocateExecutableMemory, then modify protection as desired. -// AllocateMemoryPages is simpler and more generic. +// AllocateMemoryPages is simpler and more generic. Note that on W^X platforms, this will return executable but not writable +// memory! void* AllocateExecutableMemory(size_t size); void* AllocateMemoryPages(size_t size, uint32_t memProtFlags); // Note that on platforms returning PlatformIsWXExclusive, you cannot set a page to be both readable and writable at the same time. void ProtectMemoryPages(void* ptr, size_t size, uint32_t memProtFlags); +inline void ProtectMemoryPages(const void *start, const void *end, uint32_t memProtFlags) { + ProtectMemoryPages((void *)start, (const uint8_t *)end - (const uint8_t *)start, memProtFlags); +} void FreeMemoryPages(void* ptr, size_t size); // Regular aligned memory. Don't try to apply memory protection willy-nilly to memory allocated this way as in-page alignment is unknown (though could be checked). @@ -52,12 +55,8 @@ void ResetExecutableMemory(void* ptr); #endif inline int GetMemoryProtectPageSize() { -#ifdef _WIN32 - // For memory protection purposes, Windows is still stuck at 64k except in WSL (linux on windows). - return 65536; -#else - return PAGE_SIZE; -#endif + // This is 4096 on all platforms we care about. 8k on Itanium but meh. + return 4096; } template diff --git a/Common/x64Emitter.cpp b/Common/x64Emitter.cpp index 489223895f..0b48c928b9 100644 --- a/Common/x64Emitter.cpp +++ b/Common/x64Emitter.cpp @@ -135,9 +135,11 @@ const u8 *XEmitter::AlignCode16() const u8 *XEmitter::AlignCodePage() { - int c = int((u64)code & 4095); + // Memory protection pages matter. + int page_size = GetMemoryProtectPageSize(); + int c = int((u64)code & (page_size - 1)); if (c) - ReserveCodeSpace(4096-c); + ReserveCodeSpace(page_size - c); return code; } diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index 8c277a803a..177c43c408 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -72,7 +72,7 @@ namespace MIPSComp { using namespace ArmJitConstants; void ArmJit::GenerateFixedCode() { - const u8 *start = GetCodePtr(); + const u8 *start = AlignCodePage(); // LR == SCRATCHREG2 on ARM32 so it needs to be pushed. restoreRoundingMode = AlignCode16(); { @@ -279,6 +279,10 @@ void ArmJit::GenerateFixedCode() { // Don't forget to zap the instruction cache! FlushLitPool(); FlushIcache(); + + // Freeze the dispatcher code + const void *end = AlignCodePage(); + ProtectMemoryPages(start, end, MEM_PROT_READ | MEM_PROT_EXEC); } } // namespace MIPSComp diff --git a/Core/MIPS/ARM64/Arm64Asm.cpp b/Core/MIPS/ARM64/Arm64Asm.cpp index 0986fd23ad..0eaee8682a 100644 --- a/Core/MIPS/ARM64/Arm64Asm.cpp +++ b/Core/MIPS/ARM64/Arm64Asm.cpp @@ -94,7 +94,7 @@ namespace MIPSComp { using namespace Arm64JitConstants; void Arm64Jit::GenerateFixedCode(const JitOptions &jo) { - const u8 *start = AlignCode16(); + const u8 *start = AlignCodePage(); if (jo.useStaticAlloc) { saveStaticRegisters = AlignCode16(); STR(INDEX_UNSIGNED, DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount)); @@ -316,6 +316,9 @@ void Arm64Jit::GenerateFixedCode(const JitOptions &jo) { // Don't forget to zap the instruction cache! This must stay at the end of this function. FlushIcache(); + // Freeze the dispatcher code + const void *end = AlignCodePage(); + ProtectMemoryPages(start, end, MEM_PROT_READ | MEM_PROT_EXEC); } } // namespace MIPSComp diff --git a/Core/MIPS/x86/Asm.cpp b/Core/MIPS/x86/Asm.cpp index 05eda2823d..6f699a5448 100644 --- a/Core/MIPS/x86/Asm.cpp +++ b/Core/MIPS/x86/Asm.cpp @@ -63,7 +63,7 @@ void ImHere() { } void Jit::GenerateFixedCode(JitOptions &jo) { - const u8 *start = AlignCode16(); + const u8 *start = AlignCodePage(); restoreRoundingMode = AlignCode16(); { STMXCSR(M(&mips_->temp)); @@ -217,7 +217,10 @@ void Jit::GenerateFixedCode(JitOptions &jo) { ABI_PopAllCalleeSavedRegsAndAdjustStack(); RET(); - endOfPregeneratedCode = GetCodePtr(); + endOfPregeneratedCode = AlignCodePage(); + + // Freeze the pre-generated code. + ProtectMemoryPages(start, endOfPregeneratedCode, MEM_PROT_READ | MEM_PROT_EXEC); } } // namespace \ No newline at end of file