[Audit] round 9: generalize JIT-entry probe to PC-configurable cvar
Some checks failed
Orchestrator / Commit Message Validation (push) Has been skipped
Orchestrator / Lint (push) Failing after 1m18s
Orchestrator / Windows (x86-64) (push) Has been skipped
Orchestrator / Linux (x86-64) (push) Has been skipped
Orchestrator / Create Release (push) Has been skipped

Replace the round-7 hardcoded `current_guest_function_ == 0x824F7800`
gate with a runtime cvar `audit_jit_prolog_pc` (uint32, 0 = disabled).
The cvar names the guest entry PC at which the x64 emitter inserts a
CallNative to the prolog probe; the probe now dumps the active PC in
each log line so multiple instrumentation campaigns can share output.

Renames `audit_log_sub824F7800_args` (cvar) and `AuditLogSub824F7800Args`
(host function) — the round-7 sub_824F7800-specific cvar and function
are deleted, not left dormant. The `audit_handle_lifecycle` cvar and
its existing AUDIT-HLC probe sites in xboxkrnl_threading.cc /
kernel_state.cc are untouched.

Validated by audit-059 round 9: with `--audit_jit_prolog_pc=0x821B55D8`
the silph WorkerCtx-init chain entry fires 1× in canary at tid=6
lr=0x82172D8C (call site at sub_82172BA0+0x1E8 `bctrl`, the virtual
dispatch through vtable slot 6 that statically-blind xref-walkers
cannot enumerate).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-07 10:40:35 +02:00
parent fc89e68155
commit dde48f287b

View File

@@ -41,12 +41,13 @@
DEFINE_bool(debugprint_trap_log, false, DEFINE_bool(debugprint_trap_log, false,
"Log debugprint traps to the active debugger", "CPU"); "Log debugprint traps to the active debugger", "CPU");
DEFINE_bool(audit_log_sub824F7800_args, false, DEFINE_uint32(audit_jit_prolog_pc, 0,
"Emit one XELOGKERNEL line per JIT-compiled entry to guest function " "Guest function entry PC at which to emit one XELOGKERNEL line "
"0x824F7800 (silph::WorkerCtx ctor's immediate caller; per audit-058 " "per JIT-compiled entry. When non-zero, the x64 emitter inserts "
"fires exactly 1x after DiscImageDevice::ResolvePath). Dumps " "a CallNative to AuditLogJitPrologArgs at the start of the JIT "
"r3..r10, LR, and 64 bytes at host(r3). Audit-059 round 7 probe — " "body of the matching guest function. Dumps r3..r10, LR, and 64 "
"off by default.", "bytes at host(r3). Zero (default) disables the probe. Audit-059 "
"round 7+ JIT-prolog probe — generic PC-configurable variant.",
"Auditing"); "Auditing");
DEFINE_bool(ignore_undefined_externs, true, DEFINE_bool(ignore_undefined_externs, true,
"Don't exit when an undefined extern is called.", "CPU"); "Don't exit when an undefined extern is called.", "CPU");
@@ -277,14 +278,16 @@ bool X64Emitter::Emit(HIRBuilder* builder, EmitFunctionInfo& func_info) {
count on no other code modifying it. mov(GetMembaseReg(), count on no other code modifying it. mov(GetMembaseReg(),
qword[GetContextReg() + offsetof(ppc::PPCContext, virtual_membase)]); qword[GetContextReg() + offsetof(ppc::PPCContext, virtual_membase)]);
*/ */
// Audit-059 round 7: one-shot dump of r3..r10/LR at entry to sub_824F7800. // Audit-059: PC-configurable JIT-prolog probe. Runtime-gated on the cvar
// Compile-time gated on guest function address; runtime gated on the cvar. // audit_jit_prolog_pc (uint32; 0 disables). When non-zero and the current
// guest function's entry PC matches, emit a single CallNative to
// AuditLogJitPrologArgs that dumps r3..r10, LR, and 64 bytes at host(r3).
// Emits *before* any body instruction runs, so r3..r10 / LR in PPCContext // Emits *before* any body instruction runs, so r3..r10 / LR in PPCContext
// still reflect the caller's args (no LOAD/STORE_CONTEXT has executed yet). // still reflect the caller's args (no LOAD/STORE_CONTEXT has executed yet).
if (cvars::audit_log_sub824F7800_args && if (cvars::audit_jit_prolog_pc != 0u &&
current_guest_function_ == 0x824F7800u) { current_guest_function_ == cvars::audit_jit_prolog_pc) {
extern uint64_t AuditLogSub824F7800Args(void* raw_context, uint64_t arg0); extern uint64_t AuditLogJitPrologArgs(void* raw_context, uint64_t arg0);
CallNative(AuditLogSub824F7800Args, 0); CallNative(AuditLogJitPrologArgs, 0);
} }
// Body. // Body.
auto block = builder->first_block(); auto block = builder->first_block();
@@ -454,12 +457,14 @@ uint64_t TrapDebugBreak(void* raw_context, uint64_t address) {
return 0; return 0;
} }
// Audit-059 round 7 probe: dump r3..r10, LR, and 64 bytes at host(r3) when // Audit-059 JIT-prolog probe: dump r3..r10, LR, and 64 bytes at host(r3)
// the guest hits the JIT-compiled entry of sub_824F7800 (silph::WorkerCtx // when the guest hits the JIT-compiled entry of the function whose entry PC
// ctor's immediate caller). Per audit-058 this fires exactly once after the // matches cvars::audit_jit_prolog_pc. Generic PC-configurable variant of the
// DiscImageDevice::ResolvePath(\dat\movie) sequence on canary. // round-7 sub_824F7800 hook. The hook logs the current guest PC so multiple
uint64_t AuditLogSub824F7800Args(void* raw_context, uint64_t /*unused*/) { // instrumentation campaigns can share log output.
uint64_t AuditLogJitPrologArgs(void* raw_context, uint64_t /*unused*/) {
auto* ctx = reinterpret_cast<ppc::PPCContext_s*>(raw_context); auto* ctx = reinterpret_cast<ppc::PPCContext_s*>(raw_context);
uint32_t pc = static_cast<uint32_t>(cvars::audit_jit_prolog_pc);
uint32_t r3 = static_cast<uint32_t>(ctx->r[3]); uint32_t r3 = static_cast<uint32_t>(ctx->r[3]);
uint32_t r4 = static_cast<uint32_t>(ctx->r[4]); uint32_t r4 = static_cast<uint32_t>(ctx->r[4]);
uint32_t r5 = static_cast<uint32_t>(ctx->r[5]); uint32_t r5 = static_cast<uint32_t>(ctx->r[5]);
@@ -473,9 +478,9 @@ uint64_t AuditLogSub824F7800Args(void* raw_context, uint64_t /*unused*/) {
uint32_t tid = ctx->thread_state ? ctx->thread_state->thread_id() : 0u; uint32_t tid = ctx->thread_state ? ctx->thread_state->thread_id() : 0u;
XELOGKERNEL( XELOGKERNEL(
"AUDIT-HLC sub_824F7800 entry tid={:08X} r3={:08X} r4={:08X} r5={:08X} " "AUDIT-HLC JitProlog pc={:08X} tid={:08X} r3={:08X} r4={:08X} r5={:08X} "
"r6={:08X} r7={:08X} r8={:08X} r9={:08X} r10={:08X} lr={:08X}", "r6={:08X} r7={:08X} r8={:08X} r9={:08X} r10={:08X} lr={:08X}",
tid, r3, r4, r5, r6, r7, r8, r9, r10, lr); pc, tid, r3, r4, r5, r6, r7, r8, r9, r10, lr);
// Dump 64 bytes at host(r3) if r3 looks like a plausible guest VA. // Dump 64 bytes at host(r3) if r3 looks like a plausible guest VA.
if (r3 >= 0x10000 && r3 < 0xE0000000) { if (r3 >= 0x10000 && r3 < 0xE0000000) {
@@ -486,14 +491,17 @@ uint64_t AuditLogSub824F7800Args(void* raw_context, uint64_t /*unused*/) {
uint32_t d1 = xe::load_and_swap<uint32_t>(host + off + 4); uint32_t d1 = xe::load_and_swap<uint32_t>(host + off + 4);
uint32_t d2 = xe::load_and_swap<uint32_t>(host + off + 8); uint32_t d2 = xe::load_and_swap<uint32_t>(host + off + 8);
uint32_t d3 = xe::load_and_swap<uint32_t>(host + off + 12); uint32_t d3 = xe::load_and_swap<uint32_t>(host + off + 12);
XELOGKERNEL("AUDIT-HLC sub_824F7800 r3+{:02X}: {:08X} {:08X} {:08X} {:08X}", XELOGKERNEL(
off, d0, d1, d2, d3); "AUDIT-HLC JitProlog pc={:08X} r3+{:02X}: {:08X} {:08X} {:08X} "
"{:08X}",
pc, off, d0, d1, d2, d3);
} }
} else { } else {
XELOGKERNEL("AUDIT-HLC sub_824F7800 r3 translate failed"); XELOGKERNEL("AUDIT-HLC JitProlog pc={:08X} r3 translate failed", pc);
} }
} else { } else {
XELOGKERNEL("AUDIT-HLC sub_824F7800 r3 out of VA range, skipping dump"); XELOGKERNEL("AUDIT-HLC JitProlog pc={:08X} r3 out of VA range, skipping dump",
pc);
} }
return 0; return 0;
} }