From 19659d7f76ff081fea58781abe139dabe2d7d369 Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Mon, 4 May 2026 18:54:24 +0200 Subject: [PATCH] =?UTF-8?q?feat(kernel):=20KRNBUG-XAM-001=20=E2=80=94=20XG?= =?UTF-8?q?etAVPack=20returns=208=20(HDMI),=20not=200x16?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors canary's cvars::avpack default (xam_info.cc:35) and Sylpheed's accepted set {3,4,6,8} (xam_info.cc:250-251). With KRNBUG-XEX-001 having flipped the priv-10 gate, XGetAVPack now reaches its caller in sub_824AB578; returning 0x16 caused Sylpheed to abort the AV/crypto block before XeCryptSha. Cascade walks one step (canary-only export list 11 → 10); sub_824ABA98 is the next candidate. Tests: 589 → 590. Goldens re-baselined (n50m: 50000005→50000004, imports 407417→407416). Lockstep deterministic across 3 reruns at -n 100M (instructions=100000010, import_calls=987686 +2.4×, swaps=2). 9-PC producer probe still 0×; parked handles 0x1004/0x100c/0x15e0 still signal_attempts=0. Co-Authored-By: Claude Opus 4.7 (1M context) --- audit-findings.md | 108 ++++++++++++++++++ .../xenia-app/tests/golden/sylpheed_n2m.json | 4 +- .../xenia-app/tests/golden/sylpheed_n50m.json | 4 +- crates/xenia-kernel/src/xam.rs | 9 +- 4 files changed, 120 insertions(+), 5 deletions(-) diff --git a/audit-findings.md b/audit-findings.md index bd4a981..2e2dd74 100644 --- a/audit-findings.md +++ b/audit-findings.md @@ -4891,3 +4891,111 @@ divergence; otherwise the next gate is in `XeCryptSha` / **Trace artifacts:** `audit-runs/post-priv-fix/ours.log` (5.6M lines, 500M-instruction PC-probe + handle-focus run; full diagnostic dump in stdout). + +--- + +### KRNBUG-XAM-001 — `XGetAVPack` returned non-canary `0x16`; canary default is `8` (HDMI) + +**Status:** LANDED 2026-05-04. Closes the first follow-up of +KRNBUG-XEX-001 (the `XGetAVPack` arm flipped 0→1 by the priv-10 fix +exposed this as the next gate). + +**One-line fix.** `crates/xenia-kernel/src/xam.rs:382-384`: + +```rust +fn xget_avpack(ctx: &mut PpcContext, _mem: &GuestMemory, _state: &mut KernelState) { + ctx.gpr[3] = 8; +} +``` + +Was `0x16`. Canary's `XGetAVPack_entry` returns `cvars::avpack` +(`xam_info.cc:252`); the cvar is `DEFINE_int32(avpack, 8, ...)` +(`xam_info.cc:35`). Canary's inline comment at `xam_info.cc:250-251`: +*"Games seem to use this as a PAL check — if the result is not +3/4/6/8 they explode with errors if not in PAL mode."* `0x16` (=22) +is not in `{3, 4, 6, 8}`, so Sylpheed's caller treated the response +as invalid. + +**Tests.** 589 → 590. New unit test `xget_avpack_returns_hdmi` asserts +`r3 == 8`. Constant-return change; one assertion is enough. + +**Validation chain (Step 3 of the hand-off):** + +| step | outcome | +|---|---| +| (a) `cargo test --workspace --release` | 589 → 590; all green. | +| (b) `--stable-digest -n 100M` lockstep | `instructions=100000010, import_calls=987686, swaps=2`. 3-run identical (counter sets bit-equal). Was `100000013, 407417, 2`. The +2.4× import-call jump is the deterministic guest divergence into the canary-correct branch (the AV/crypto block now executes more thunks). NOT non-determinism. | +| (c) AUDIT-005 9-PC probe at -n 500M | All 9 producer probe sites still 0× (`grep -c CTOR-PROBE = 0`). | +| (d) `--trace-handles-focus=0x1004,0x100c,0x15e0` | All 3 handles still `signal_attempts=0`. The producers live deeper in the init flow than what `XGetAVPack` alone unlocks. | +| (e) Canary kernel-call diff (set-diff `audit-runs/post-fix/ours-500m.log` vs `audit-runs/audit-005/canary.log`) | 11 → **10** canary-only exports. The single match unlocked is `XGetAVPack` (canary=1, ours=1). Remaining absent: `ExTerminateThread ×2`, `KeReleaseSemaphore ×268`, `KeResetEvent ×1`, `NtDeviceIoControlFile ×2`, `ObCreateSymbolicLink ×1`, `XamTaskCloseHandle ×1`, `XamTaskSchedule ×1`, `XamUserReadProfileSettings ×2`, `XeCryptSha ×1`, `XeKeysConsolePrivateKeySign ×1`. | +| (f) `sylpheed_oracles` (n50m) | Re-baselined: `instructions=50000004, imports=407416, swaps=2, draws=0` (was `50000005, 407417, 2, 0`). 3 deterministic re-runs. Orphan golden `sylpheed_n2m.json` (no test refers to it) also re-baselined for hygiene. | + +**Decisive interpretation.** The fix is **correct but partial**. The +`XGetAVPack` value returns are now in the canary-accepted set, and +the call site at `0x824ab5a0` reaches it; the rest of the AV/crypto +block at `sub_824AB578` between `XGetAVPack` returning (`lr=0x824ab5a4`) +and `XeCryptSha` does not execute. The cascade walked exactly **one** +step. + +**Telemetry signal — `lr=0x824a97e4` post-fix.** A new `RtlNtStatusToDosError(r3=0xc0000011 ...)` (`STATUS_NOT_IMPLEMENTED`) +fires from `lr=0x824a97e4` immediately after `XGetAVPack` returns. +That PC is **inside** `sub_824A9710` (the priv-11 site), so the +priv-11-caller IS being entered (probably via fall-through from a +caller of `sub_824AB578`'s post-AV block), but the priv-11 query +itself never fires — there's a precondition between block entry and +priv-11 that fails. Almost certainly a downstream sub of the +AV/crypto block (one of `sub_824ABA98` and friends from AUDIT-005's +disasm) returns negative NTSTATUS, which routes here. + +**Next-frontier bug (the new gate identified by this fix).** Between +`XGetAVPack` (`lr=0x824ab5a4`) and `XeCryptSha`. Two candidates: + +1. **The 4 unreached siblings inside `sub_824AB578`** — + `XeCryptSha`, `XeKeysConsolePrivateKeySign`, `NtDeviceIoControlFile ×2`, + `ObCreateSymbolicLink`. Currently all stubs (`stub_success` for + the crypto, real for `NtDeviceIoControlFile` but the caller may + not be reached). Need to diff sub_824AB578 step-by-step from + `0x824ab5a4` onward to find the failing precondition. +2. **`sub_824ABA98` returning negative NTSTATUS** (the AUDIT-005 + call site referenced from `lr=0x824a97e4`). If the AV/crypto + block calls `sub_824ABA98` and gets a negative return, control + transfers to the error path that triggers the + `RtlNtStatusToDosError(c0000011)` we observe. That PC is the + tail signal — finding what's upstream of it is the next probe. + +**What did NOT change** (per the AUDIT-004 diagnosis chain): + +- The 9 producer-callsite PCs for handles `0x100c` (5 sites) and + `0x15e0` (4 sites): still 0× hits. +- The 3 parked-waiter handles `0x1004 / 0x100c / 0x15e0`: + still `signal_attempts=0`. +- `swaps=2` plateau, `draws=0`: unchanged. + +**Trace artifacts:** `audit-runs/post-fix/ours-500m.log` (5.6M lines, +500M-instruction PC-probe + handle-focus run, post-AV-pack-fix). +Same probe configuration as KRNBUG-AUDIT-005's `audit-runs/audit-005/ours.log`, +re-runnable with the command in that finding's "Reproduce" block. + +**Reproduce the canary set-diff:** + +```bash +python3 - <<'PY' +import re +from pathlib import Path +from collections import Counter +HERE = Path('audit-runs/audit-005') +CR = re.compile(r'^d>\s+[0-9A-Fa-f]+\s+([A-Z][A-Za-z0-9_]+)\(') +OR_ = re.compile(r'probe_calls.*?call=([A-Za-z_][A-Za-z0-9_]*)') +def extract(p, rx): + out = Counter() + with open(p, errors='replace') as f: + for line in f: + m = rx.search(line) + if m: out[m.group(1)] += 1 + return out +canary = extract(HERE/'canary.log', CR) +ours = extract('audit-runs/post-fix/ours-500m.log', OR_) +for n in sorted(set(canary) - set(ours)): + print(f' {canary[n]:>5} {n}') +PY +``` diff --git a/crates/xenia-app/tests/golden/sylpheed_n2m.json b/crates/xenia-app/tests/golden/sylpheed_n2m.json index 88a62f5..4beef5f 100644 --- a/crates/xenia-app/tests/golden/sylpheed_n2m.json +++ b/crates/xenia-app/tests/golden/sylpheed_n2m.json @@ -1,6 +1,6 @@ { - "instructions": 2000000, - "imports": 5636, + "instructions": 2000005, + "imports": 5635, "unimpl": 0, "draws": 0, "swaps": 0, diff --git a/crates/xenia-app/tests/golden/sylpheed_n50m.json b/crates/xenia-app/tests/golden/sylpheed_n50m.json index a37262d..e1ef545 100644 --- a/crates/xenia-app/tests/golden/sylpheed_n50m.json +++ b/crates/xenia-app/tests/golden/sylpheed_n50m.json @@ -1,6 +1,6 @@ { - "instructions": 50000005, - "imports": 407417, + "instructions": 50000004, + "imports": 407416, "unimpl": 0, "draws": 0, "swaps": 2, diff --git a/crates/xenia-kernel/src/xam.rs b/crates/xenia-kernel/src/xam.rs index 0de162e..f752d99 100644 --- a/crates/xenia-kernel/src/xam.rs +++ b/crates/xenia-kernel/src/xam.rs @@ -380,7 +380,7 @@ fn xam_session_create_handle(ctx: &mut PpcContext, mem: &GuestMemory, state: &mu // ===== Locale ===== fn xget_avpack(ctx: &mut PpcContext, _mem: &GuestMemory, _state: &mut KernelState) { - ctx.gpr[3] = 0x16; // HDMI + ctx.gpr[3] = 8; } fn xget_game_region(ctx: &mut PpcContext, _mem: &GuestMemory, _state: &mut KernelState) { @@ -465,4 +465,11 @@ mod tests { other => panic!("expected Thread object with hw_id set, got {:?}", other), } } + + #[test] + fn xget_avpack_returns_hdmi() { + let (mut ctx, mem, mut state) = fresh(); + xget_avpack(&mut ctx, &mem, &mut state); + assert_eq!(ctx.gpr[3], 8); + } }