feat(kernel): KRNBUG-XEX-001 — real XexCheckExecutablePrivilege from XEX header bitmap

Replace stub_return_zero with a canary-faithful implementation that
returns bit `priv` of the loaded XEX's XEX_HEADER_SYSTEM_FLAGS
(key 0x00030000) bitmap. Mirrors xenia-canary
xboxkrnl_modules.cc:22-39: `(flags >> priv) & 1` for priv < 32, else 0.

Plumbing:
- xenia-xex: header_keys::SYSTEM_FLAGS const + get_system_flags() accessor.
- xenia-kernel/state.rs: pub xex_system_flags: u32 + xex_priv_logged
  HashSet for one-shot per-priv tracing.
- xenia-app: kernel.xex_system_flags wired in cmd_exec_inner.
- xenia-kernel/exports.rs: real export body + unit test covering
  bits 10/11/0/64 + zero-flags case.

Sylpheed's bitmap is 0x00000400 (only XEX_SYSTEM_PAL50_INCOMPATIBLE,
bit 10). At -n 500M with the fix:
- XGetAVPack: 0 -> 1 (priv-10 gate at lr=0x824ab598 flipped).
- 10 other canary-only exports + 9 producer PCs + 3 parked handles
  unchanged. Priv-11 site at sub_824A9710 is downstream and still
  not reached — AV/crypto block aborts after XGetAVPack returns
  our placeholder 0x16 (canary returns 8/HDMI; Sylpheed accepts
  only 3/4/6/8 per xenia-canary xam_info.cc:250-251).

Tests 588 -> 589. Lockstep deterministic (3 reruns identical):
n50m goes 50000008 -> 50000005 instr / 407415 -> 407417 imp / swaps=2 /
draws=0. Goldens re-baselined (sylpheed_n50m, sylpheed_n2m); oracle
test green.

Full chain-of-effects + next-frontier hand-off in audit-findings.md
under KRNBUG-XEX-001.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-05-04 18:32:51 +02:00
parent 451b3b28fe
commit 1a892d4641
8 changed files with 163 additions and 11 deletions

View File

@@ -190,7 +190,7 @@ pub fn register_exports(state: &mut KernelState) {
state.register_export(Xboxkrnl, 0x0257, "XeKeysConsoleSignatureVerification", stub_success);
// Xex module
state.register_export(Xboxkrnl, 0x0194, "XexCheckExecutablePrivilege", stub_return_zero);
state.register_export(Xboxkrnl, 0x0194, "XexCheckExecutablePrivilege", xex_check_executable_privilege);
state.register_export(Xboxkrnl, 0x0195, "XexGetModuleHandle", xex_get_module_handle);
state.register_export(Xboxkrnl, 0x0197, "XexGetProcedureAddress", xex_get_procedure_address);
@@ -2696,6 +2696,31 @@ fn xma_create_context(ctx: &mut PpcContext, _mem: &GuestMemory, state: &mut Kern
// ===== Xex =====
/// Mirrors xenia-canary `XexCheckExecutablePrivilege_entry`
/// (xboxkrnl_modules.cc:22-39): returns whether bit `privilege` of the
/// loaded executable module's `XEX_HEADER_SYSTEM_FLAGS` (key 0x00030000)
/// is set. Privilege ≥ 32 returns 0 (matches `1 << priv` UB-via-overflow
/// behavior — canary's mask becomes 0 once the shift saturates the
/// uint32_t range, and `(flags & 0)` is always 0).
fn xex_check_executable_privilege(ctx: &mut PpcContext, _mem: &GuestMemory, state: &mut KernelState) {
let privilege = ctx.gpr[3] as u32;
let result = if privilege < 32 {
(state.xex_system_flags >> privilege) & 1
} else {
0
};
if state.xex_priv_logged.insert(privilege) {
tracing::info!(
priv = privilege,
flags = format_args!("{:#010x}", state.xex_system_flags),
result,
lr = format_args!("{:#010x}", ctx.lr),
"XexCheckExecutablePrivilege",
);
}
ctx.gpr[3] = result as u64;
}
fn xex_get_procedure_address(ctx: &mut PpcContext, mem: &GuestMemory, state: &mut KernelState) {
// Mirrors xenia-canary XexGetProcedureAddress_entry
// (xboxkrnl_modules.cc:195): r3 = hmodule, r4 = ordinal,
@@ -5242,6 +5267,39 @@ mod tests {
assert_ne!(h_krnl, h_xam, "xboxkrnl distinct from xam");
}
/// `XexCheckExecutablePrivilege` must return bit `priv` of the loaded
/// XEX's `XEX_HEADER_SYSTEM_FLAGS` bitmap. Mirrors canary
/// [xboxkrnl_modules.cc:22-39](../../../xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc#L22-L39).
#[test]
fn xex_check_executable_privilege_reads_system_flags_bitmap() {
let (mut ctx, mem, mut state) = fresh();
// bit 10 set, bit 11 clear (matches Sylpheed's actual bitmap value
// `0x00000400` / XEX_SYSTEM_PAL50_INCOMPATIBLE).
state.xex_system_flags = 0x0000_0400;
ctx.gpr[3] = 10;
xex_check_executable_privilege(&mut ctx, &mem, &mut state);
assert_eq!(ctx.gpr[3], 1, "priv 10 set in flags 0x0400");
ctx.gpr[3] = 11;
xex_check_executable_privilege(&mut ctx, &mem, &mut state);
assert_eq!(ctx.gpr[3], 0, "priv 11 clear in flags 0x0400");
ctx.gpr[3] = 0;
xex_check_executable_privilege(&mut ctx, &mem, &mut state);
assert_eq!(ctx.gpr[3], 0, "priv 0 clear in flags 0x0400");
ctx.gpr[3] = 64;
xex_check_executable_privilege(&mut ctx, &mem, &mut state);
assert_eq!(ctx.gpr[3], 0, "priv >= 32 returns 0");
// With no flags set, every priv reads 0.
state.xex_system_flags = 0;
ctx.gpr[3] = 10;
xex_check_executable_privilege(&mut ctx, &mem, &mut state);
assert_eq!(ctx.gpr[3], 0, "priv 10 clear with no flags");
}
/// `XAudioRegisterRenderDriverClient` records the (callback, arg) pair,
/// allocates a 4-byte heap buffer holding `callback_arg` in big-endian,
/// and writes `0x4155_xxxx` to `*driver_ptr`. Mirrors canary