[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.
This commit is contained in:
Herman S.
2026-03-10 21:51:58 +09:00
parent 6f18c98502
commit 4a78428c6b

View File

@@ -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();