diff --git a/src/xenia/cpu/function.cc b/src/xenia/cpu/function.cc index ebd8c5ba1..50ec3d178 100644 --- a/src/xenia/cpu/function.cc +++ b/src/xenia/cpu/function.cc @@ -44,6 +44,12 @@ bool BuiltinFunction::Call(ThreadState* thread_state, uint32_t return_address) { } assert_not_null(handler_); + // Detect corruption of builtin argument pointers (e.g., global mutex + // accidentally overwritten by guest code). A very low non-null address is + // almost certainly invalid here and has led to crashes in unlock(). + if (arg0_ && reinterpret_cast(arg0_) < 0x1000) { + XELOGE("BuiltinFunction '{}' arg0 pointer appears corrupt: {:p}", name(), arg0_); + } handler_(thread_state->context(), arg0_, arg1_); if (original_thread_state != thread_state) { @@ -129,6 +135,16 @@ bool GuestFunction::Call(ThreadState* thread_state, uint32_t return_address) { ThreadState::Bind(thread_state); } + // Validate the global mutex pointer before executing guest code to help + // diagnose crashes where std::recursive_mutex::unlock() sees an invalid + // 'this' (e.g., 0x1). + auto ctx = thread_state->context(); + auto& expected_global_mutex = xe::global_critical_region::mutex(); + if (ctx->global_mutex != &expected_global_mutex) { + XELOGE("GuestFunction '{}' executing with corrupted global_mutex {:p}; restoring", name(), ctx->global_mutex); + ctx->global_mutex = &expected_global_mutex; + } + bool result = CallImpl(thread_state, return_address); if (original_thread_state != thread_state) { diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index eb63a1abf..999cffaec 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -337,6 +337,16 @@ bool Processor::Execute(ThreadState* thread_state, uint32_t address) { auto context = thread_state->context(); + // Defensive: ensure the context's global mutex pointer hasn't been clobbered + // by guest code scribbling over the red zone. A corrupt pointer (like 0x1) + // leads to a crash when unlock() is invoked by translated code paths. + auto& expected_global_mutex = xe::global_critical_region::mutex(); + if (context->global_mutex != &expected_global_mutex) { + uintptr_t raw_ptr = reinterpret_cast(context->global_mutex); + XELOGE("PPCContext global_mutex pointer corrupted (was {:p} / 0x{:X}), restoring", context->global_mutex, raw_ptr); + context->global_mutex = &expected_global_mutex; + } + // Pad out stack a bit, as some games seem to overwrite the caller by about // 16 to 32b. context->r[1] -= 64 + 112;