From 4a78428c6b51bf4420a2997a1efe0f064bea0fee Mon Sep 17 00:00:00 2001 From: "Herman S." <429230+has207@users.noreply.github.com> Date: Tue, 10 Mar 2026 21:51:58 +0900 Subject: [PATCH] [CPU] Skip QueryProtect in SIGSEGV handler on Linux std::ifstream read of /proc/self/maps is not async-signal-safe and runs on every access violation fault. Skip the race-condition check and go straight to the callback, which handles cleared watches correctly. Fixes severe performance issues seen in MFSMW, MCLA, etc. --- src/xenia/cpu/mmio_handler.cc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/xenia/cpu/mmio_handler.cc b/src/xenia/cpu/mmio_handler.cc index 4662f50ba..c7cc7815e 100644 --- a/src/xenia/cpu/mmio_handler.cc +++ b/src/xenia/cpu/mmio_handler.cc @@ -440,6 +440,22 @@ bool MMIOHandler::ExceptionCallback(Exception* ex) { // clears the watch we just hit). // Do this under the lock so we don't introduce another race condition. auto lock = global_critical_region_.Acquire(); +#if XE_PLATFORM_LINUX + // On Linux, exception handling runs inside a signal handler (SIGSEGV). + // QueryProtect uses std::ifstream to read /proc/self/maps, which is NOT + // async-signal-safe and can corrupt heap state or deadlock if the signal + // interrupted malloc or another non-reentrant function. + // Instead, skip the race-condition check and go straight to the callback. + // The callback will handle it correctly — if watches were already cleared, + // TriggerCallbacks will find no watches and the page will be unprotected + // by the time the instruction retries. + if (access_violation_callback_) { + return access_violation_callback_(std::move(lock), + access_violation_callback_context_, + fault_host_address, is_write); + } + return false; +#else memory::PageAccess cur_access; size_t page_length = memory::page_size(); memory::QueryProtect(fault_host_address, page_length, cur_access); @@ -457,6 +473,7 @@ bool MMIOHandler::ExceptionCallback(Exception* ex) { fault_host_address, is_write); } return false; +#endif } auto rip = ex->pc();