This commit is contained in:
I'm Matheus 2025-11-20 15:03:25 +00:00 committed by GitHub
commit 3c59473619
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 92 additions and 2 deletions

View file

@ -44,6 +44,22 @@ bool BuiltinFunction::Call(ThreadState* thread_state, uint32_t return_address) {
}
assert_not_null(handler_);
// Detect corrupted builtin argument pointers before calling the handler.
// A very low non-null address (< 0x10000) is almost certainly invalid and
// indicates memory corruption, likely from guest code buffer overflow.
// This check helps identify the problem before it causes a crash in the
// mutex operations within builtin handlers.
if (arg0_ && reinterpret_cast<uintptr_t>(arg0_) < 0x10000) {
XELOGE(
"BuiltinFunction '{}' detected corrupted arg0 pointer: {:p}. "
"This likely indicates memory corruption from guest code. "
"The emulation cannot continue safely.",
name(), arg0_);
assert_always("BuiltinFunction arg0 corrupted - guest code memory corruption detected");
return false;
}
handler_(thread_state->context(), arg0_, arg1_);
if (original_thread_state != thread_state) {
@ -129,8 +145,40 @@ bool GuestFunction::Call(ThreadState* thread_state, uint32_t return_address) {
ThreadState::Bind(thread_state);
}
// Validate PPCContext critical pointers before executing guest code.
// This detects corruption that may have occurred from a previous function.
auto ctx = thread_state->context();
auto& expected_global_mutex = xe::global_critical_region::mutex();
if (ctx->global_mutex != &expected_global_mutex) {
uintptr_t corrupt_ptr = reinterpret_cast<uintptr_t>(ctx->global_mutex);
XELOGE(
"GuestFunction '{}' at 0x{:08X} called with corrupted PPCContext. "
"global_mutex pointer is {:p} / 0x{:X} (expected {:p}). "
"Corruption likely occurred in a previous function call.",
name(), address(), ctx->global_mutex, corrupt_ptr,
static_cast<void*>(&expected_global_mutex));
assert_always(
"PPCContext already corrupted before function execution. Previous "
"guest function likely has buffer overflow.");
return false;
}
bool result = CallImpl(thread_state, return_address);
// Validate context after execution to catch corruption during this function.
if (ctx->global_mutex != &expected_global_mutex) {
uintptr_t corrupt_ptr = reinterpret_cast<uintptr_t>(ctx->global_mutex);
XELOGE(
"GuestFunction '{}' at 0x{:08X} CORRUPTED PPCContext during "
"execution. global_mutex changed to {:p} / 0x{:X}. "
"This function has a buffer overflow or invalid memory write.",
name(), address(), ctx->global_mutex, corrupt_ptr);
assert_always(
"Memory corruption detected in guest function execution. "
"The function has a buffer overflow bug.");
return false;
}
if (original_thread_state != thread_state) {
ThreadState::Bind(original_thread_state);
}
@ -139,4 +187,4 @@ bool GuestFunction::Call(ThreadState* thread_state, uint32_t return_address) {
}
} // namespace cpu
} // namespace xe
} // namespace xe

View file

@ -337,6 +337,31 @@ bool Processor::Execute(ThreadState* thread_state, uint32_t address) {
auto context = thread_state->context();
// Validate critical PPCContext pointers before executing guest code.
// The global_mutex pointer is particularly susceptible to corruption from
// guest code writing beyond array bounds (e.g., VMX register array overflow).
// Detecting corruption here helps identify the source before crashes occur.
auto& expected_global_mutex = xe::global_critical_region::mutex();
if (context->global_mutex != &expected_global_mutex) {
uintptr_t corrupt_ptr = reinterpret_cast<uintptr_t>(context->global_mutex);
XELOGE(
"PPCContext global_mutex pointer corrupted (expected {:p}, got {:p} / "
"0x{:X}). This indicates guest code is writing beyond allocated "
"boundaries. Common causes: VMX register overflow, stack corruption, or "
"invalid memory access in translated code. Thread ID: {}",
static_cast<void*>(&expected_global_mutex), context->global_mutex,
corrupt_ptr, thread_state->thread_id());
// Log additional context for debugging
XELOGE(" Function address: 0x{:08X}", address);
XELOGE(" Stack pointer (r1): 0x{:08X}", context->r[1]);
assert_always(
"PPCContext corruption detected - cannot continue safely. Check for "
"guest code buffer overflows or emulator bugs in array bound checks.");
return false;
}
// Pad out stack a bit, as some games seem to overwrite the caller by about
// 16 to 32b.
context->r[1] -= 64 + 112;
@ -349,6 +374,23 @@ bool Processor::Execute(ThreadState* thread_state, uint32_t address) {
// Execute the function.
auto result = function->Call(thread_state, uint32_t(context->lr));
// Validate context integrity after execution to detect corruption during
// the function call. This helps narrow down which guest functions cause
// memory corruption.
if (context->global_mutex != &expected_global_mutex) {
uintptr_t corrupt_ptr = reinterpret_cast<uintptr_t>(context->global_mutex);
XELOGE(
"PPCContext global_mutex corrupted DURING function execution at "
"0x{:08X}. Pointer changed from {:p} to {:p} / 0x{:X}. This "
"indicates the executed function wrote beyond its allocated memory.",
address, static_cast<void*>(&expected_global_mutex),
context->global_mutex, corrupt_ptr);
assert_always(
"Memory corruption detected during function execution. The executed "
"guest code has a buffer overflow or invalid memory write.");
return false;
}
context->lr = previous_lr;
context->r[1] += 64 + 112;
@ -1302,4 +1344,4 @@ uint32_t Processor::CalculateNextGuestInstruction(ThreadDebugInfo* thread_info,
}
} // namespace cpu
} // namespace xe
} // namespace xe