Phase A's diagnosis (bit 28 of [0x40d09a40] gets set to exit sub_822F1AA8's loop) is falsified by direct probe + --dump-addr in 4 sub-rounds. Key evidence: - sub_821B55D8 candidate fn fires 0× in ours; sub_824AA858 (XamInputSetState wrapper) fires 0× in canary too — chain is dead code in both engines. - end-of-run dump shows [0x40d09a40+0] = 0x00000021, same as at entry — bit 28 is NEVER set. - bcctrl at PC 0x822F1B4C (sub_822F1AA8+0xA4) fires (LR=0x822F1B50) but the post-bcctrl BB head 0x822F1B50 fires 0× — bcctrl never returns. - sub_82173990 (vtable[0] of singleton at [0x828E1F08]) is the call target; tid=1 wedges inside this 768-byte function on a thread-join to handle 0x1070 (= tid=13's thread handle). - tid=13 (entry=sub_821748F0, ctx=0x4024a840, handle=0x1070) reaches sub_821C4EB0 (silph::UImpl@GamePart_Title) at cycle 1882 → audit-049 cluster IS reached, wedges on handle 0x1078 there. C.2 force-clear POC NOT EXECUTED — would be no-op since bit 28 is never set. Per plan stopping criterion, hand back instead of proceeding blind. Adds reading-error class #19: disasm-pattern-match without runtime verification (Phase A scanned 49 oris-0x1000 sites and declared one the setter without ever observing the bit get set). No xenia-rs source changes. Canary repo also unchanged (config edit reverted clean). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8.4 KiB
Phase C.1 — Validation refutes Phase A's bit-28 setter hypothesis
TL;DR
Phase A claimed: "bit 28 of [0x40d09a40] (controller word) gets set in ours, causing sub_822F1AA8's dispatcher loop to exit early; candidate setter is sub_821B55D8 at PC 0x821B5DA4."
Phase C.1 falsifies this in 4 sub-rounds:
sub_821B55D8is dead code in both engines — itsXamInputSetStatewrappersub_824AA858fires 0× in both.[0x40d09a40]is never set to anything with bit 28 —--dump-addrat end of run shows+0x00 = 0x00000021, the entry value. Bit 28 is NEVER set.- The actual wedge is at the
bcctrlat PC0x822F1B4C(inside sub_822F1AA8 setup, BEFORE the dispatcher loop). tid=1 never reaches the loop top-check. - The bcctrl calls
sub_82173990(vtable[0] of the dispatcher singleton at[0x828E1F08]), which eventually waits for tid=13 to terminate. tid=13 wedges in the audit-049 silph::UImpl@GamePart_Title chain on handle0x1078.
The C.2 force-clear POC (the planned next step) would have zero effect because bit 28 is never set. Skipped per plan stopping criterion.
Probe-fire counts (ours, 50M-instr parallel)
| PC | sub-round | fires | meaning |
|---|---|---|---|
0x821B55D8 (Phase A candidate fn entry) |
1 | 0 | function never reached → β/γ |
0x821B5D98,DA0,DAC,D48 (loop BB heads) |
1 | 0 | function never reached |
0x822F1AA8 (sub_822F1AA8 entry) |
2,3,4 | 2-3 | reached |
0x822F1B38 (post-bl 0x824AA8B0) |
4 | 2 | reached |
0x822F1B50 (post-bcctrl) |
4 | 0 | bcctrl never returns |
0x822F1B60,B78,B80,BBC (loop setup/top) |
3 | 0 | unreachable past bcctrl |
0x822F1E10 (loop exit cleanup) |
2 | 0 | loop never entered, never exited |
0x822F1E34 (post-thread-join) |
2 | 0 | never reached |
0x82173990 (vtable[0] target) |
4 | 2 | called via bcctrl, r3=singleton (LR=0x822F1B50) |
0x821748F0 (tid=13 entry) |
4 | 2 | tid=13 runs |
0x821C4EB0 (silph::UImpl@GamePart_Title) |
4 | 2 | audit-009/049 reached on tid=13 |
0x82457388,0x824574C0,0x82457408,0x82457490 (other oris candidates) |
2 | 0 | unreachable |
Canary probe results
| PC | fires | meaning |
|---|---|---|
0x824AA858 (XamInputSetState wrapper) |
0 | sub_821B55D8 chain is dead code in CANARY too |
0x822F1B50 (post-bcctrl, attempted) |
0 | canary's JitProlog only fires at function entries, so not directly testable; but per audit round-33 sub_821741C8 fires 471× in canary → bcctrl DOES return in canary |
Critical evidence: --dump-addr=0x40d09a40 at end of run
addr=0x40d09a40
+0x00: 00 00 00 21 00 00 00 01 42 44 df 00 40 54 1a 40
^^^^^^^^^^^ ^^^^^^^^^^^
+0x10: 40 54 1b 40 40 54 1b 80 40 54 1b c0 00 00 10 54
+0x20: 00 00 00 00 40 24 a8 20 00 00 00 08 00 00 00 00
[+0x00] = 0x00000021← bit 28 (mask 0x10000000) is NOT SET. Same value as at sub_822F1AA8 entry.[+0x1c] = 0x00001054← spawned init thread handle (= tid=8's thread handle, NOT 0x1070)- Thread state: tid=1 waits on handle
0x1070, tid=13 waits on handle0x1078.
Handle 0x1070 is tid=13's thread handle (per stderr: ExCreateThread: tid=13 handle=0x1070 entry=0x821748f0 ctx=0x4024a840 suspended=true). So tid=1's wait at the wedge point is a thread-join on tid=13, NOT a thread-join on the dispatcher init thread (tid=8, handle 0x1054).
Wedge path (corrected)
entry_point (sub_824AB748) [tid=1 main]
└─ sub_8216EA68
└─ sub_822F1AA8(controller=0x40d09a40) [LR=0x8216EE14]
├─ ExCreateThread(entry=sub_822F1EE0, ctx=controller) [PC 0x822F1B08]
│ ⇒ tid=8 spawn, handle=0x1054 (suspended)
├─ bl 0x824AA8B0 (no-op probe) [PC 0x822F1B34]
└─ bcctrl on vtable[+0] of [0x828E1F08] singleton [PC 0x822F1B4C]
│
└─ sub_82173990(r3=singleton) [r3=0x40ba9a80, vtable=0x40111910]
└─ ... (768-byte function with ≥18 calls; calls sub_82448AA0, sub_824AA7A0,
sub_82448BC8, sub_82448C50, sub_8216F218, sub_8217C850, sub_82178E50,
sub_821835E0, ...)
└─ ... → KeWaitForSingleObject INFINITE on handle 0x1070
(= tid=13's thread handle, thread-join)
⇒ WEDGE — tid=13 never exits
(Concurrently — spawned somewhere else, not from sub_822F1AA8:)
[tid=13, spawn-handle=0x1070, ctx=0x4024a840]
└─ sub_821748F0 (worker boilerplate, entry from ExCreateThread)
├─ sub_82172798, sub_82172818
└─ sub_821749C0
└─ sub_821CF3F0
└─ ... → sub_821C4EB0 (UImpl@GamePart_Title@silph) [audit-009/049!]
└─ ... → sub_821CB030 (creates KEVENT at +0x128)
⇒ KeWaitForSingleObject INFINITE on handle 0x1078
⇒ WEDGE — handle 0x1078 is never signaled in ours
Why Phase A's hypothesis is wrong
Phase A:
- Disassembled sub_822F1AA8's body, observed the bit-28 loop-exit check at
0x822F1BB8and end-of-iter check at0x822F1E0C. - Mem-watch on
0x40d09a40showed zero stores → inferred "the setter writes via some path mem-watch doesn't capture." - DB-scanned
oris ?, ?, 0x1000(49 sites), foundsub_821B55D8 + 0x821B5DA4with patternbl sub_824AA858 ; if r3 == 0xAA: oris r11, 0x1000 ; stw. - Concluded
sub_821B55D8was the setter.
What Phase A missed:
- Mem-watch's 0-stores result was correct: NO setter exists. Bit 28 is never set in either engine. The mem-watch null-result was a hint that the bit-28 hypothesis itself was wrong, but Phase A interpreted it as "mem-watch misses something."
- The disasm-based hypothesis was visually compelling (a loop iterating arrays and setting bit 28 when a kernel call returns 0xAA) but never verified runtime.
sub_821B55D8is itself dead code in both engines.
Reading-error class #19: disasm-pattern-match without runtime verification
When scanning for a hypothesized signal source via DB pattern-match (oris ?, ?, 0x1000), the analyst must run a probe to verify the suspected site is both reached and takes the suspected path before declaring it the cause. Phase A bypassed both checks. The single --dump-addr=0x40d09a40 flag in sub-round 2 (literally 4 keystrokes added to the existing probe command) revealed the central assumption was wrong.
Real divergence (handed to next session)
This is the same wedge as audit-049/058/059: tid=13 wedges in the silph::UImpl@GamePart_Title cluster on handle 0x1078. tid=1 wedges on tid=13's thread-handle (0x1070) inside sub_82173990's call chain.
sub_82173990 is vtable[0] of the dispatcher singleton at [0x828E1F08]. It's a 768-byte function with ≥18 calls; the actual wait site is somewhere down its tree. To localize where in sub_82173990 the wait happens, probe its BB heads + the KeWaitForSingleObject thunks (sub_824AA330, sub_824AA708).
The fix-shape is NOT "force-clear bit 28." The fix-shape is "signal handle 0x1078 in the audit-049 cluster, or short-circuit tid=13's wait." Round 22 (silph_synth.rs) attempted the cluster-A version of this. Cluster B (silph::UImpl) needs its own synthesis or a kernel-side signal of handle 0x1078.
Phase C verdict
- C.1: 4 sub-rounds executed (within budget).
- C.2: NOT EXECUTED — POC would be no-op since bit 28 is never set. Per plan stopping criterion, do not proceed to C.2 blind when C.1 refutes the diagnosis.
- C.3: not applicable.
- Branch state: no source changes. Audit artifacts only.
Files in this directory
ours-c1-probe.log/stderr— sub-round 1, probe at sub_821B55D8 BB heads (0 fires)ours-sr2-confirm-bit28.log/stderr— sub-round 2, probe loop top/exit + dump-addr (bit 28 NEVER SET)ours-sr3-wait-trace.log/stderr— sub-round 3, probe wait site + handle 0x1070 traceours-sr4-bcctrl-trace.log/stderr— sub-round 4, probe pre/post bcctrl + sub_82173990 entry + tid=13 entry (decisive)- canary side in
../round-C1-setter-validation-canary/:canary-824AA858.log— XamInputSetState wrapper fires 0× in canary toocanary-822F1B50.log— JitProlog can't probe at BB-internal PCs (function-entry-only)