[Audit] handle-lifecycle: include guest LR + one-frame stack-walk on waits
Extend the AUDIT-HLC probes with guest LR capture:
- NtCreateEvent, NtSetEvent, KeSetEvent: log immediate guest LR
(cpu::ThreadState::Get()->context()->lr) so callers can be
attributed to specific guest functions.
- NtWaitForSingleObjectEx entry + _done: also walk one PPC stack
frame up to recover the guest_lr of the wait wrapper's caller
(Xbox 360 EABI: saved LR at [prev_sp - 8], see xenia-rs's
walk_guest_back_chain). lr_enter alone gives only the kernel
wait wrapper return address (e.g. 0x824AC578); guest_lr surfaces
the actual call site.
Still cvar-gated on audit_handle_lifecycle. Used in audit-059 round 4
to map canary handles F8000xxx to ours' 0x12xx wedges via guest LR
matching against silph::UImpl/GamePart cluster.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -584,8 +584,9 @@ uint32_t xeKeSetEvent(X_KEVENT* event_ptr, uint32_t increment, uint32_t wait) {
|
||||
dword_result_t KeSetEvent_entry(pointer_t<X_KEVENT> event_ptr,
|
||||
dword_t increment, dword_t wait) {
|
||||
if (cvars::audit_handle_lifecycle) {
|
||||
XELOGKERNEL("AUDIT-HLC KeSetEvent guest_ptr={:08X}",
|
||||
uint32_t(event_ptr.guest_address()));
|
||||
uint32_t lr = static_cast<uint32_t>(cpu::ThreadState::Get()->context()->lr);
|
||||
XELOGKERNEL("AUDIT-HLC KeSetEvent guest_ptr={:08X} lr={:08X}",
|
||||
uint32_t(event_ptr.guest_address()), lr);
|
||||
}
|
||||
return xeKeSetEvent(event_ptr, increment, wait);
|
||||
}
|
||||
@@ -645,9 +646,12 @@ dword_result_t NtCreateEvent_entry(
|
||||
*handle_ptr = ev->handle();
|
||||
}
|
||||
if (cvars::audit_handle_lifecycle) {
|
||||
XELOGKERNEL("AUDIT-HLC NtCreateEvent handle={:08X} type={} initial_state={}",
|
||||
handle_ptr ? uint32_t(*handle_ptr) : 0u,
|
||||
uint32_t(event_type), uint32_t(initial_state));
|
||||
uint32_t lr = static_cast<uint32_t>(cpu::ThreadState::Get()->context()->lr);
|
||||
XELOGKERNEL(
|
||||
"AUDIT-HLC NtCreateEvent handle={:08X} type={} initial_state={} "
|
||||
"lr={:08X}",
|
||||
handle_ptr ? uint32_t(*handle_ptr) : 0u, uint32_t(event_type),
|
||||
uint32_t(initial_state), lr);
|
||||
}
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -675,7 +679,9 @@ uint32_t xeNtSetEvent(uint32_t handle, xe::be<uint32_t>* previous_state_ptr) {
|
||||
|
||||
dword_result_t NtSetEvent_entry(dword_t handle, lpdword_t previous_state_ptr) {
|
||||
if (cvars::audit_handle_lifecycle) {
|
||||
XELOGKERNEL("AUDIT-HLC NtSetEvent handle={:08X}", uint32_t(handle));
|
||||
uint32_t lr = static_cast<uint32_t>(cpu::ThreadState::Get()->context()->lr);
|
||||
XELOGKERNEL("AUDIT-HLC NtSetEvent handle={:08X} lr={:08X}",
|
||||
uint32_t(handle), lr);
|
||||
}
|
||||
return xeNtSetEvent(handle, previous_state_ptr);
|
||||
}
|
||||
@@ -1051,17 +1057,41 @@ dword_result_t NtWaitForSingleObjectEx_entry(dword_t object_handle,
|
||||
dword_t wait_mode,
|
||||
dword_t alertable,
|
||||
lpqword_t timeout_ptr) {
|
||||
uint32_t lr_enter = 0;
|
||||
uint32_t guest_lr = 0;
|
||||
if (cvars::audit_handle_lifecycle) {
|
||||
XELOGKERNEL("AUDIT-HLC NtWaitForSingleObjectEx handle={:08X} alertable={}",
|
||||
uint32_t(object_handle), uint32_t(alertable));
|
||||
auto* ctx = cpu::ThreadState::Get()->context();
|
||||
lr_enter = static_cast<uint32_t>(ctx->lr);
|
||||
// Walk one PPC stack frame up from the wait wrapper's frame to recover
|
||||
// the GUEST caller's LR (lr_enter is just the kernel-internal wait
|
||||
// wrapper's return address; we want the function that called *it*).
|
||||
// Xbox 360 PPC convention: prologue stores caller's LR at [old_sp - 8]
|
||||
// *before* bumping r1 to the new frame, so from any frame, the LR saved
|
||||
// by that frame's prologue lives at (back_chain - 8). See xenia-rs
|
||||
// walk_guest_back_chain in crates/xenia-kernel/src/state.rs.
|
||||
uint32_t sp = static_cast<uint32_t>(ctx->r[1]);
|
||||
uint32_t back1 = xe::load_and_swap<uint32_t>(ctx->TranslateVirtual(sp));
|
||||
if (back1) {
|
||||
uint32_t back2 =
|
||||
xe::load_and_swap<uint32_t>(ctx->TranslateVirtual(back1));
|
||||
if (back2) {
|
||||
guest_lr = xe::load_and_swap<uint32_t>(
|
||||
ctx->TranslateVirtual(back2 - 8));
|
||||
}
|
||||
}
|
||||
XELOGKERNEL(
|
||||
"AUDIT-HLC NtWaitForSingleObjectEx handle={:08X} alertable={} "
|
||||
"lr={:08X} guest_lr={:08X}",
|
||||
uint32_t(object_handle), uint32_t(alertable), lr_enter, guest_lr);
|
||||
}
|
||||
uint64_t timeout = timeout_ptr ? static_cast<uint64_t>(*timeout_ptr) : 0u;
|
||||
uint32_t result = NtWaitForSingleObjectEx(object_handle, wait_mode, alertable,
|
||||
timeout_ptr ? &timeout : nullptr);
|
||||
if (cvars::audit_handle_lifecycle) {
|
||||
XELOGKERNEL(
|
||||
"AUDIT-HLC NtWaitForSingleObjectEx_done handle={:08X} result={:08X}",
|
||||
uint32_t(object_handle), result);
|
||||
"AUDIT-HLC NtWaitForSingleObjectEx_done handle={:08X} result={:08X} "
|
||||
"lr={:08X} guest_lr={:08X}",
|
||||
uint32_t(object_handle), result, lr_enter, guest_lr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user