From af2c32635adb90ee89bd800cb7decc4b1a1895f8 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 31 Jul 2023 14:37:35 +0200 Subject: [PATCH 1/2] Jit: Add more error checking to ProtectStack --- Source/Core/Common/MemoryUtil.cpp | 34 ++++++++++++++++--- Source/Core/Common/MemoryUtil.h | 8 ++--- .../Core/Core/PowerPC/JitCommon/JitBase.cpp | 14 ++++++-- 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/Source/Core/Common/MemoryUtil.cpp b/Source/Core/Common/MemoryUtil.cpp index b473705411..2cd393d098 100644 --- a/Source/Core/Common/MemoryUtil.cpp +++ b/Source/Core/Common/MemoryUtil.cpp @@ -160,18 +160,25 @@ void* AllocateAlignedMemory(size_t size, size_t alignment) return ptr; } -void FreeMemoryPages(void* ptr, size_t size) +bool FreeMemoryPages(void* ptr, size_t size) { if (ptr) { #ifdef _WIN32 if (!VirtualFree(ptr, 0, MEM_RELEASE)) + { PanicAlertFmt("FreeMemoryPages failed!\nVirtualFree: {}", GetLastErrorString()); + return false; + } #else if (munmap(ptr, size) != 0) + { PanicAlertFmt("FreeMemoryPages failed!\nmunmap: {}", LastStrerrorString()); + return false; + } #endif } + return true; } void FreeAlignedMemory(void* ptr) @@ -186,39 +193,56 @@ void FreeAlignedMemory(void* ptr) } } -void ReadProtectMemory(void* ptr, size_t size) +bool ReadProtectMemory(void* ptr, size_t size) { #ifdef _WIN32 DWORD oldValue; if (!VirtualProtect(ptr, size, PAGE_NOACCESS, &oldValue)) + { PanicAlertFmt("ReadProtectMemory failed!\nVirtualProtect: {}", GetLastErrorString()); + return false; + } #else if (mprotect(ptr, size, PROT_NONE) != 0) + { PanicAlertFmt("ReadProtectMemory failed!\nmprotect: {}", LastStrerrorString()); + return false; + } #endif + return true; } -void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) +bool WriteProtectMemory(void* ptr, size_t size, bool allowExecute) { #ifdef _WIN32 DWORD oldValue; if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue)) + { PanicAlertFmt("WriteProtectMemory failed!\nVirtualProtect: {}", GetLastErrorString()); + return false; + } #elif !(defined(_M_ARM_64) && defined(__APPLE__)) // MacOS 11.2 on ARM does not allow for changing the access permissions of pages // that were marked executable, instead it uses the protections offered by MAP_JIT // for write protection. if (mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ) != 0) + { PanicAlertFmt("WriteProtectMemory failed!\nmprotect: {}", LastStrerrorString()); + return false; + } #endif + return true; } -void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) +bool UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) { #ifdef _WIN32 DWORD oldValue; if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldValue)) + { PanicAlertFmt("UnWriteProtectMemory failed!\nVirtualProtect: {}", GetLastErrorString()); + return false; + } #elif !(defined(_M_ARM_64) && defined(__APPLE__)) // MacOS 11.2 on ARM does not allow for changing the access permissions of pages // that were marked executable, instead it uses the protections offered by MAP_JIT @@ -227,8 +251,10 @@ void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ) != 0) { PanicAlertFmt("UnWriteProtectMemory failed!\nmprotect: {}", LastStrerrorString()); + return false; } #endif + return true; } size_t MemPhysical() diff --git a/Source/Core/Common/MemoryUtil.h b/Source/Core/Common/MemoryUtil.h index b6489e9546..11ee1c4cbb 100644 --- a/Source/Core/Common/MemoryUtil.h +++ b/Source/Core/Common/MemoryUtil.h @@ -27,12 +27,12 @@ struct ScopedJITPageWriteAndNoExecute ~ScopedJITPageWriteAndNoExecute() { JITPageWriteDisableExecuteEnable(); } }; void* AllocateMemoryPages(size_t size); -void FreeMemoryPages(void* ptr, size_t size); +bool FreeMemoryPages(void* ptr, size_t size); void* AllocateAlignedMemory(size_t size, size_t alignment); void FreeAlignedMemory(void* ptr); -void ReadProtectMemory(void* ptr, size_t size); -void WriteProtectMemory(void* ptr, size_t size, bool executable = false); -void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false); +bool ReadProtectMemory(void* ptr, size_t size); +bool WriteProtectMemory(void* ptr, size_t size, bool executable = false); +bool UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false); size_t MemPhysical(); } // namespace Common diff --git a/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp b/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp index 7c9c707fbc..7cba8c7dfb 100644 --- a/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp +++ b/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp @@ -151,7 +151,12 @@ void JitBase::ProtectStack() #ifdef _WIN32 ULONG reserveSize = SAFE_STACK_SIZE; - SetThreadStackGuarantee(&reserveSize); + if (!SetThreadStackGuarantee(&reserveSize)) + { + PanicAlertFmt("Failed to set thread stack guarantee"); + m_enable_blr_optimization = false; + return; + } #else auto [stack_addr, stack_size] = Common::GetCurrentThreadStack(); @@ -184,7 +189,12 @@ void JitBase::ProtectStack() } m_stack_guard = reinterpret_cast(stack_guard_addr); - Common::ReadProtectMemory(m_stack_guard, GUARD_SIZE); + if (!Common::ReadProtectMemory(m_stack_guard, GUARD_SIZE)) + { + m_stack_guard = nullptr; + m_enable_blr_optimization = false; + return; + } #endif } From 4131dffae92c22ed938118d196ffc94da63246e1 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 30 Jul 2023 17:35:22 +0200 Subject: [PATCH 2/2] Jit: Allow BLR optimization without fastmem While both fastmem and the BLR optimization depend on fault handling, the BLR optimization doesn't depend on fastmem, and there are cases where you might want the BLR optimization but not fastmem. For me personally, it's useful when I try to use a debugger on Android and have to disable fastmem so I don't get SIGSEGVs all the time, but it would be especially useful for iOS users. --- Source/Core/Core/Core.cpp | 9 +++++---- Source/Core/Core/PowerPC/JitCommon/JitBase.cpp | 7 +++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 9ca298a837..5396c8439b 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -391,9 +391,10 @@ static void CpuThread(const std::optional& savestate_path, bool del static_cast(IDCache::GetEnvForThread()); #endif - const bool fastmem_enabled = Config::Get(Config::MAIN_FASTMEM); - if (fastmem_enabled) - EMM::InstallExceptionHandler(); // Let's run under memory watch + // The JIT need to be able to intercept faults, both for fastmem and for the BLR optimization. + const bool exception_handler = EMM::IsExceptionHandlerSupported(); + if (exception_handler) + EMM::InstallExceptionHandler(); #ifdef USE_MEMORYWATCHER s_memory_watcher = std::make_unique(); @@ -441,7 +442,7 @@ static void CpuThread(const std::optional& savestate_path, bool del s_is_started = false; - if (fastmem_enabled) + if (exception_handler) EMM::UninstallExceptionHandler(); if (GDBStub::IsActive()) diff --git a/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp b/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp index 7cba8c7dfb..bcdafd9725 100644 --- a/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp +++ b/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp @@ -18,6 +18,7 @@ #include "Core/Core.h" #include "Core/CoreTiming.h" #include "Core/HW/CPU.h" +#include "Core/MemTools.h" #include "Core/PowerPC/PPCAnalyst.h" #include "Core/PowerPC/PowerPC.h" #include "Core/System.h" @@ -132,7 +133,8 @@ void JitBase::RefreshConfig() analyzer.SetDivByZeroExceptionsEnabled(m_enable_div_by_zero_exceptions); bool any_watchpoints = m_system.GetPowerPC().GetMemChecks().HasAny(); - jo.fastmem = m_fastmem_enabled && jo.fastmem_arena && (m_ppc_state.msr.DR || !any_watchpoints); + jo.fastmem = m_fastmem_enabled && jo.fastmem_arena && (m_ppc_state.msr.DR || !any_watchpoints) && + EMM::IsExceptionHandlerSupported(); jo.memcheck = m_system.IsMMUMode() || m_system.IsPauseOnPanicMode() || any_watchpoints; jo.fp_exceptions = m_enable_float_exceptions; jo.div_by_zero_exceptions = m_enable_div_by_zero_exceptions; @@ -140,7 +142,8 @@ void JitBase::RefreshConfig() void JitBase::InitBLROptimization() { - m_enable_blr_optimization = jo.enableBlocklink && m_fastmem_enabled && !m_enable_debugging; + m_enable_blr_optimization = + jo.enableBlocklink && !m_enable_debugging && EMM::IsExceptionHandlerSupported(); m_cleanup_after_stackfault = false; }