handoff: VSync/event-wedge fixes + iterate 2.A–2.BC research notes

Source changes (dormant parity infra, retained from iterate 2.AI/2.AO):
- xenia-kernel/exports.rs: nt_create_event manual_reset polarity +
  related event wiring
- xenia-gpu/mmio_region.rs: D1MODE_VBLANK_VLINE_STATUS hardcode parity

Also lands the audit-runs/ analysis notes (.md/.txt/.json digests) for the
iterate 2.x VSync/0x10e8/0x1004 wedge investigation. Raw trace dumps
(.jsonl/.gz/.csv/.stdout) and agent worktrees (.claude/) are gitignored as
regenerable local artifacts — see memory + HANDOFF for the running findings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-05 07:19:08 +02:00
parent acd1656753
commit ef93a4fa14
620 changed files with 108303 additions and 1 deletions

View File

@@ -0,0 +1,70 @@
# Phase C+9 — broad impact
## Resolved divergences (C+8 → C+9)
| idx | name | C+8 ours | C+9 ours | canary | status |
|---|---|---|---|---|---|
| 102197 | `XamContentCreateEnumerator` return_value | 0 | **1317 (0x525)** | 1317 | ✅ MATCH |
## New first divergence
| idx | name | canary | ours | hypothesis |
|---|---|---|---|---|
| 102404 | `NtQueryFullAttributesFile` return_value | 0 (STATUS_SUCCESS) | 3221225524 (0xC0000034 STATUS_OBJECT_NAME_NOT_FOUND) | VFS path lookup miss — file/dir exists on canary's pre-populated cache/HDD root but not ours's fresh VFS. AUDIT-052/054 sibling class. |
## Persisting divergences (different bug, hold)
* tid=12 → 7 idx=2: `KeWaitForSingleObject` ret 258 vs 0 (held)
* tid=14 → 9 idx=39: XAudio vs `RtlEnterCriticalSection` (held)
## Coarse-counter movement
This is the first non-zero coarse digest delta since C+5
(`imports: 40470 → 40465`, -5). Interpretation: the return_value flip
at idx=102197 routes the game into a different control-flow branch
that calls 5 fewer import functions within 50M instructions. This is
exactly the bull's-eye behavior we want — the fix has visible
downstream effect.
`draws=0`/`swaps=1` stable: still in the pre-renderer boot phase.
## XAM call coverage delta
Pre-C+9: `XamContentCreateEnumerator` fired 1× with stub return.
Post-C+9: `XamContentCreateEnumerator` fires 1× with X_ERROR_NO_SUCH_USER.
Total XAM-content callsite count UNCHANGED (1 invocation in 50M).
## Reading-error class
NO new class introduced. **#28 (canary source supersedes NT-doc /
plan annotations) re-applied** with one extra rigor step: the plan's
inline constant identity (`0x525 = X_ERROR_NO_CONTENT`) was WRONG —
`0x525` is `X_ERROR_NO_SUCH_USER`. Lesson: **verify constants from
canary's xbox.h, not from the plan's natural-language descriptions**.
This is the same class of "trust the source not the framing" that #28
captures; logged here as a fresh data point.
## Deferred-item interaction check
| item | touched? |
|---|---|
| Heap region (C+2) | NO |
| Clock (Stage 2) | NO |
| Audio host-pump (AUDIT-032/048) | NO |
| KeSetEvent / KeResetEvent semantics | NO |
| VFS cache persistence (AUDIT-052/054) | NO (Phase C+10 may need to revisit) |
| `xam_user_get_signin_state` user0=1 model | FLAGGED as internal inconsistency — separate fix, not landed |
| profile-manager model | FLAGGED for future XAM-user-subsystem session |
## Internal inconsistency surfaced (NOT FIXED)
Ours's `xam_user_get_signin_state` (xam.rs:380-384) returns 1 for
`user_index == 0`, but the C+9 fix says "no profile installed → 0x525
for user 0." When the game eventually calls `XamUserGetSigninState(0)`,
canary returns 0 (idx 107996 confirmed in canary.jsonl) while ours
returns 1. This will become a Phase A first divergence in a future
Phase C session — fix shape will be: `xam_user_get_signin_state`
returns 0 unconditionally (mirror canary's "no profile manager"
default).
Single-fix discipline: NOT landed in C+9.

View File

@@ -0,0 +1,131 @@
# Phase A diff report
**This report is the output of Phase A's diff harness. Divergences
shown here are INPUT for Phase B (first-divergence localization),
not findings of Phase A.** Phase A's job is to make the harness
itself correct, not to analyze what it surfaces.
## Summary
| canary_tid | ours_tid | matched | canary_total | ours_total | first_divergence_at |
|---|---|---|---|---|---|
| 4 | 11 | 9 | 47573 | 9 | — |
| 6 | 1 | 102404 | 329948 | 108471 | 102404 |
| 7 | 2 | 29 | 29 | 30 | — |
| 12 | 7 | 2 | 6689 | 3 | 2 |
| 14 | 9 | 39 | 1371603 | 75 | 39 |
| 15 | 10 | 15 | 863209 | 15 | — |
## canary_tid=4 → ours_tid=11
No divergence within the 9 compared events (canary has 47573, ours has 9).
## canary_tid=6 → ours_tid=1
First divergence at `tid_event_idx=102404`: payload.return_value: canary=0 ours=3221225524
**Pre-context (last 5 matching events):**
```
canary: [102399] import.call RtlInitAnsiString
ours: [102399] import.call RtlInitAnsiString
canary: [102400] kernel.call RtlInitAnsiString
ours: [102400] kernel.call RtlInitAnsiString
canary: [102401] kernel.return RtlInitAnsiString
ours: [102401] kernel.return RtlInitAnsiString
canary: [102402] import.call NtQueryFullAttributesFile
ours: [102402] import.call NtQueryFullAttributesFile
canary: [102403] kernel.call NtQueryFullAttributesFile
ours: [102403] kernel.call NtQueryFullAttributesFile
```
**Divergent event:**
```
canary: [102404] kernel.return NtQueryFullAttributesFile
ours: [102404] kernel.return NtQueryFullAttributesFile
```
**Next event after the divergence (if any):**
```
canary: [102405] import.call RtlEnterCriticalSection
ours: [102405] import.call RtlNtStatusToDosError
```
**Raw events (JSON):**
```json
{"deterministic": true, "engine": "canary", "guest_cycle": 0, "host_ns": 755569000, "kind": "kernel.return", "payload": {"name": "NtQueryFullAttributesFile", "return_value": 0, "side_effects": [], "status": "0x00000000"}, "schema_version": 1, "tid": 6, "tid_event_idx": 102404}
{"deterministic": true, "engine": "ours", "guest_cycle": 5391947, "host_ns": 474074711, "kind": "kernel.return", "payload": {"name": "NtQueryFullAttributesFile", "return_value": 3221225524, "side_effects": [], "status": "0xc0000034"}, "schema_version": 1, "tid": 1, "tid_event_idx": 102404}
```
## canary_tid=7 → ours_tid=2
No divergence within the 29 compared events (canary has 29, ours has 30).
## canary_tid=12 → ours_tid=7
First divergence at `tid_event_idx=2`: payload.return_value: canary=258 ours=0
**Pre-context (last 5 matching events):**
```
canary: [0] import.call KeWaitForSingleObject
ours: [0] import.call KeWaitForSingleObject
canary: [1] kernel.call KeWaitForSingleObject
ours: [1] kernel.call KeWaitForSingleObject
```
**Divergent event:**
```
canary: [2] kernel.return KeWaitForSingleObject
ours: [2] kernel.return KeWaitForSingleObject
```
**Next event after the divergence (if any):**
```
canary: [3] import.call RtlEnterCriticalSection
ours: <end of stream>
```
**Raw events (JSON):**
```json
{"deterministic": true, "engine": "canary", "guest_cycle": 0, "host_ns": 904485700, "kind": "kernel.return", "payload": {"name": "KeWaitForSingleObject", "return_value": 258, "side_effects": [], "status": "0x00000102"}, "schema_version": 1, "tid": 12, "tid_event_idx": 2}
{"deterministic": true, "engine": "ours", "guest_cycle": 30, "host_ns": 499172677, "kind": "kernel.return", "payload": {"name": "KeWaitForSingleObject", "return_value": 0, "side_effects": [], "status": "0x00000000"}, "schema_version": 1, "tid": 7, "tid_event_idx": 2}
```
## canary_tid=14 → ours_tid=9
First divergence at `tid_event_idx=39`: payload.ord: canary=503 ours=293
**Pre-context (last 5 matching events):**
```
canary: [34] kernel.call KeReleaseSpinLockFromRaisedIrql
ours: [34] kernel.call KeReleaseSpinLockFromRaisedIrql
canary: [35] kernel.return KeReleaseSpinLockFromRaisedIrql
ours: [35] kernel.return KeReleaseSpinLockFromRaisedIrql
canary: [36] import.call KfLowerIrql
ours: [36] import.call KfLowerIrql
canary: [37] kernel.call KfLowerIrql
ours: [37] kernel.call KfLowerIrql
canary: [38] kernel.return KfLowerIrql
ours: [38] kernel.return KfLowerIrql
```
**Divergent event:**
```
canary: [39] import.call XAudioGetVoiceCategoryVolumeChangeMask
ours: [39] import.call RtlEnterCriticalSection
```
**Next event after the divergence (if any):**
```
canary: [40] kernel.call XAudioGetVoiceCategoryVolumeChangeMask
ours: [40] kernel.call RtlEnterCriticalSection
```
**Raw events (JSON):**
```json
{"deterministic": true, "engine": "canary", "guest_cycle": 0, "host_ns": 1082563200, "kind": "import.call", "payload": {"module": "xboxkrnl.exe", "name": "XAudioGetVoiceCategoryVolumeChangeMask", "ord": 503}, "schema_version": 1, "tid": 14, "tid_event_idx": 39}
{"deterministic": true, "engine": "ours", "guest_cycle": 417, "host_ns": 1685593303, "kind": "import.call", "payload": {"module": "xboxkrnl.exe", "name": "RtlEnterCriticalSection", "ord": 293}, "schema_version": 1, "tid": 9, "tid_event_idx": 39}
```
## canary_tid=15 → ours_tid=10
No divergence within the 15 compared events (canary has 863209, ours has 15).

View File

@@ -0,0 +1,10 @@
{
"instructions": 50000002,
"imports": 40465,
"unimpl": 0,
"draws": 0,
"swaps": 1,
"unique_render_targets": 0,
"shader_blobs_live": 0,
"texture_cache_entries": 0
}

View File

@@ -0,0 +1,10 @@
{
"instructions": 50000002,
"imports": 40465,
"unimpl": 0,
"draws": 0,
"swaps": 1,
"unique_render_targets": 0,
"shader_blobs_live": 0,
"texture_cache_entries": 0
}

View File

@@ -0,0 +1,10 @@
{
"instructions": 50000002,
"imports": 40465,
"unimpl": 0,
"draws": 0,
"swaps": 1,
"unique_render_targets": 0,
"shader_blobs_live": 0,
"texture_cache_entries": 0
}

View File

@@ -0,0 +1,103 @@
Phase C+9 — XamContentCreateEnumerator canary-parity X_ERROR_NO_SUCH_USER
=========================================================================
Scope: `crates/xenia-kernel/src/xam.rs`
LOC: ~85 body + ~125 test = ~210 net additive
Registration change — xam.rs:59:
--------------------------------
- state.register_export(Xam, 0x025C, "XamContentCreateEnumerator", stub_success);
+ state.register_export(Xam, 0x025C, "XamContentCreateEnumerator", xam_content_create_enumerator);
Body — new function after `xam_user_get_signin_state` (xam.rs:384):
-------------------------------------------------------------------
+ // ===== Content =====
+
+ /// `XamContentCreateEnumerator(user_index, device_id, content_type,
+ /// content_flags, items_per_enumerate, buffer_size_ptr, handle_out)`.
+ /// Mirrors xenia-canary `XamContentCreateEnumerator_entry`
+ /// (xam_content.cc:129-220). Reading-error #28 discipline applied: body
+ /// shape verified against canary source.
+ ///
+ /// Canary's normal-path success returns `X_ERROR_SUCCESS` (0) with a
+ /// fresh enumerator handle in `*handle_out`. The Phase A oracle at
+ /// `tid_event_idx=102197` shows canary returning `X_ERROR_NO_SUCH_USER`
+ /// (`0x525`, 1317) with empty side_effects — the call hit the
+ /// `if (!user) return X_ERROR_NO_SUCH_USER;` early-return at
+ /// xam_content.cc:153-155 because no profile is installed in canary's
+ /// default `--mute=true` config (no `--profile_slot_*` flags).
+ ///
+ /// Ours has no profile-manager state, so all `user_index != 0xFE`
+ /// queries miss. Mirror the early-return: write `*buffer_size_ptr` per
+ /// canary line 145-147 (which executes *before* the user check) and
+ /// return `X_ERROR_NO_SUCH_USER`. Implementing real content enumeration
+ /// is an XAM-content-subsystem session (escalation-tier scope), not
+ /// this fix.
+ ///
+ /// Side note on internal consistency: ours's `xam_user_get_signin_state`
+ /// returns 1 for `user_index == 0`, conflicting with the "no profile"
+ /// model used here. That divergence surfaces later in the Phase A trace
+ /// (idx 107996+) and is a separate fix — deferred per single-fix
+ /// discipline.
+ fn xam_content_create_enumerator(
+ ctx: &mut PpcContext,
+ mem: &GuestMemory,
+ _state: &mut KernelState,
+ ) {
+ const X_USER_INDEX_NONE: u32 = 0xFE;
+ const X_USER_INDEX_LATEST: u32 = 0xFD;
+ const X_USER_MAX_USER_COUNT: u32 = 4;
+ const X_E_INVALIDARG: u32 = 0x8007_0057;
+ const X_ERROR_NO_SUCH_USER: u32 = 0x0000_0525;
+ const X_ERROR_SUCCESS: u32 = 0;
+ const X_CONTENT_DATA_SIZE: u32 = 0x134;
+
+ let user_index = ctx.gpr[3] as u32;
+ let device_id = ctx.gpr[4] as u32;
+ let _content_type = ctx.gpr[5] as u32;
+ let _content_flags = ctx.gpr[6] as u32;
+ let items_per_enumerate = ctx.gpr[7] as u32;
+ let buffer_size_ptr = ctx.gpr[8] as u32;
+ let handle_out = ctx.gpr[9] as u32;
+
+ let device_unknown = device_id != 0 && device_id > 2;
+ if device_unknown || handle_out == 0 {
+ if buffer_size_ptr != 0 {
+ mem.write_u32(buffer_size_ptr, 0);
+ }
+ ctx.gpr[3] = X_E_INVALIDARG as u64;
+ return;
+ }
+
+ if buffer_size_ptr != 0 {
+ mem.write_u32(buffer_size_ptr, X_CONTENT_DATA_SIZE.wrapping_mul(items_per_enumerate));
+ }
+
+ if user_index != X_USER_INDEX_NONE {
+ let out_of_range = user_index >= X_USER_MAX_USER_COUNT
+ && user_index != X_USER_INDEX_LATEST;
+ let _ = out_of_range; // documentation only — both branches → no user
+ ctx.gpr[3] = X_ERROR_NO_SUCH_USER as u64;
+ return;
+ }
+
+ if handle_out != 0 {
+ mem.write_u32(handle_out, 0);
+ }
+ ctx.gpr[3] = X_ERROR_SUCCESS as u64;
+ }
Tests — 5 new in `mod tests`:
-----------------------------
+ xam_content_create_enumerator_returns_no_such_user_for_user0
+ xam_content_create_enumerator_invalid_handle_out_returns_invalidarg
+ xam_content_create_enumerator_unknown_device_returns_invalidarg
+ xam_content_create_enumerator_user_none_returns_success
+ xam_content_create_enumerator_out_of_range_user_returns_no_such_user
The first test is the THE Phase A oracle case: user_index=0,
device_id=1 (HDD), items_per_enumerate=4 → asserts ret=0x525 and
buffer_size_ptr=0x134*4 (canary writes size BEFORE the user check).

View File

@@ -0,0 +1,146 @@
# Phase C+9 — XamContentCreateEnumerator investigation
## Phase A oracle (predecessor C+8 baseline)
`canary tid=6 → ours tid=1` main chain, `tid_event_idx = 102197`:
```
canary: kernel.return XamContentCreateEnumerator return_value=1317 status=0x00000525 side_effects=[]
ours: kernel.return XamContentCreateEnumerator return_value=0 status=0x00000000 side_effects=[]
```
`args` and `args_resolved` are empty per the current Phase A emitter
scope (kernel.call args are not yet wired for dword-only signatures).
## Framing verification (reading-error #28 — canary source supersedes
NT-doc semantics or value-name guesses)
**The plan's framing was wrong on the constant identity**:
`0x525 = 1317` is `X_ERROR_NO_SUCH_USER`, NOT `X_ERROR_NO_CONTENT`
(`xbox.h:113`):
```cpp
#define X_ERROR_NO_SUCH_USER X_RESULT_FROM_WIN32(0x00000525L)
```
`X_ERROR_NO_CONTENT` is not even defined in canary's xbox.h. Always
verify constants against canary source, never the plan's inline
annotations.
## Canary body (`xenia-canary/src/xenia/kernel/xam/xam_content.cc:129-220`)
```cpp
dword_result_t XamContentCreateEnumerator_entry(
dword_t user_index, dword_t device_id, dword_t content_type,
dword_t content_flags, dword_t items_per_enumerate,
lpdword_t buffer_size_ptr, lpdword_t handle_out) {
assert_not_null(handle_out);
auto device_info = device_id == 0 ? nullptr : GetDummyDeviceInfo(device_id);
if ((device_id && device_info == nullptr) || !handle_out) {
if (buffer_size_ptr) *buffer_size_ptr = 0;
return X_E_INVALIDARG; // 0x80070057
}
if (buffer_size_ptr) {
*buffer_size_ptr = sizeof(XCONTENT_DATA) * items_per_enumerate;
} // sizeof(XCONTENT_DATA) = 0x134
uint64_t xuid = 0;
if (user_index != XUserIndexNone /* 0xFE */) {
const auto& user = kernel_state()->xam_state()->GetUserProfile(user_index);
if (!user) return X_ERROR_NO_SUCH_USER; // 0x525 ← THIS BRANCH
xuid = user->xuid();
}
// ... enumerator init, ListContent over HDD/ODD, fill items ...
*handle_out = e->handle();
return X_ERROR_SUCCESS; // 0
}
```
Only ONE control-flow path returns `0x525`:
1. `user_index != 0xFE` (i.e. a specific signed-in user is being queried)
2. `XamState::GetUserProfile(user_index)` returns `nullptr` — i.e. no
profile is installed at that slot.
Under canary's default `--mute=true` config (no `--profile_slot_*`
flags), `ProfileManager` has zero profiles, so `GetProfile(N)` returns
`nullptr` for every `N`.
## Side-effects check (rule out classification C)
Canary's `kernel.return` payload has `side_effects=[]`. Combined with
the body shape: the `*handle_out` write and the `*buffer_size_ptr`
write happen **inside the function** but are not handle-creation
side effects (no Phase A `handle.create` emit because no `XEnumerator`
was minted). The empty `side_effects` confirms canary reached the
`X_ERROR_NO_SUCH_USER` early-return at line 153-155, **before** the
`new ContentEnumerator(...)` allocation at line 160-161. So:
* No handle-table mutation to mirror.
* The `*buffer_size_ptr` write happens BEFORE the user check (line
145-147), so it does fire on the 0x525 path. Ours must mirror.
## Classification
**Class A — Unconditional (under ours's state model).**
Ours has no profile manager; all `user_index < 4` queries return
"no such user". This is effectively unconditional from Sylpheed's
perspective. Mirror canary's early-return.
**NOT class B** (state-dependent over real disk content) — we don't
reach the `ListContent()` enumeration path, so we don't need a
filesystem scan. No escalation.
**NOT class C** (side-effect mismatch) — both engines have empty
`side_effects` for this idx (above).
**NOT class D** (Phase A coverage asymmetry) — both engines emit the
same 3-event sequence (import.call, kernel.call, kernel.return).
## Ours pre-fix state
`crates/xenia-kernel/src/xam.rs:59`:
```rust
state.register_export(Xam, 0x025C, "XamContentCreateEnumerator", stub_success);
```
`stub_success` sets `r3 = 0`. Hence `return_value=0`, `status=0x00000000`,
no buffer_size_ptr write, no handle_out write.
## Fix shape
Replace the `stub_success` registration with a dedicated handler that:
1. Reads r3..r9 (user_index, device_id, content_type, content_flags,
items_per_enumerate, buffer_size_ptr, handle_out).
2. Returns `X_E_INVALIDARG` (0x80070057) when device_id is unknown
(>2; canary's GetDummyDeviceInfo accepts HDD=1, ODD=2) or
handle_out is NULL; writes `*buffer_size_ptr = 0` in that arm.
3. Otherwise writes `*buffer_size_ptr = 0x134 * items_per_enumerate`
(the sizeof(XCONTENT_DATA) computation).
4. If `user_index != 0xFE`: return `X_ERROR_NO_SUCH_USER` (0x525) —
ours has no profile manager.
5. Otherwise (user_index == 0xFE, XUserIndexNone): proceed to a
stubbed enumerator-success path — write `*handle_out = 0` and
return 0. Defensive; not exercised by current Phase A.
LOC: ~85 body + ~125 tests = ~210 additive in xam.rs only.
## Internal-consistency caveat (deferred)
Ours's `xam_user_get_signin_state` (xam.rs:380-384) returns 1 for
`user_index == 0`, claiming user 0 is signed in. This contradicts the
"no profile manager" model used by the C+9 fix. The conflict surfaces
at Phase A idx 107996 (canary returns 0 for `XamUserGetSigninState`)
and will be a separate fix in a future session. Single-fix discipline:
do not widen scope here.
## Internal-consistency caveat #2 (cosmetic)
The doc comment on `xam_user_get_signin_state` says "user0 signed in
locally" but canary returns 0 here. That's evidence of an older fix
that needs reconciliation. Out of scope for C+9.

View File

@@ -0,0 +1,77 @@
# Phase C+9 — XamContentCreateEnumerator re-validation
## Gate matrix
| # | gate | result |
|---|---|---|
| 1 | cvar-OFF determinism 50M (3 runs) | PASS — all 3 = `b8fa0e0460359a4f660adb7605e053de` (new baseline; coarse fields shifted: imports 40470→40465, ±5 reflecting downstream branch flip from the 0x525 return) |
| 2 | Phase B `image_loaded_sha256` | PASS — `ea8d160e9369328a5b922258a92113efb8d7ce3e1a5c12cc521e375985c91c18` (matches baseline; XEX loader untouched) |
| 3 | Phase A main chain matched-prefix > 102197 | PASS — **102404** (+207 from 102197) |
| 3b | tid=4 → 11 unchanged | PASS — 9 (no regression) |
| 3c | tid=7 → 2 unchanged | PASS — 29 (no regression) |
| 3d | tid=12 → 7 unchanged | PASS — 2 (no regression) |
| 3e | tid=14 → 9 unchanged | PASS — 39 (no regression) |
| 3f | tid=15 → 10 unchanged | PASS — 15 (no regression) |
| 4 | Build clean | PASS (1 unrelated `walk_committed_regions` dead-code warning, pre-existing) |
| 5 | Phase A emitter determinism (2 runs) | PASS — both det-fields md5 = `0b299c37b8fe9fd4ee1b1877a875543f` |
| 6 | Unit tests | PASS — 160 → **165** (+5 new, 0 regressions) |
## Stable-digest comparison
| field | C+8 baseline | C+9 post-fix | delta |
|---|---|---|---|
| instructions | 50000000 | 50000002 | +2 (engine ticks; jittery) |
| imports | 40470 | 40465 | -5 (game took the 0x525 branch, calls fewer of something) |
| unimpl | 0 | 0 | 0 |
| draws | 0 | 0 | 0 |
| swaps | 1 | 1 | 0 |
| unique_render_targets | 0 | 0 | 0 |
| shader_blobs_live | 0 | 0 | 0 |
| texture_cache_entries | 0 | 0 | 0 |
C+8 baseline `c6d895829b4611964978990ae1cb8a6a` → C+9 baseline
`b8fa0e0460359a4f660adb7605e053de`. First non-trivial digest movement
in 4 Phase C sub-sessions — the `0x525` return flips at least one
downstream branch decision, producing 5 fewer import calls within 50M
instructions.
## Phase A determinism
ours.jsonl (run 1) det-fields md5: `0b299c37b8fe9fd4ee1b1877a875543f`
ours-determ.jsonl (run 2) : `0b299c37b8fe9fd4ee1b1877a875543f`
Byte-identical on deterministic fields. New `--phase-a` det baseline
(`0b299c37…`), replacing C+8's `9135e369…`. Reflects the downstream
trajectory shift caused by the return-value flip at idx=102197.
## Per-chain summary
| chain | C+8 KeResetEvent | C+9 XamContentCreateEnumerator | delta |
|---|---|---|---|
| canary tid=6 → ours tid=1 (main) | 102197 | **102404** | **+207** |
| canary tid=4 → ours tid=11 | 9 | 9 | 0 (sister at end of ours window) |
| canary tid=7 → ours tid=2 | 29 | 29 | 0 (sister at end of ours window) |
| canary tid=12 → ours tid=7 | 2 | 2 | 0 (different bug: KeWaitForSingleObject 258 vs 0) |
| canary tid=14 → ours tid=9 | 39 | 39 | 0 (different bug: XAudio vs RtlEnterCS) |
| canary tid=15 → ours tid=10 | 15 | 15 | 0 |
All gates pass. No regressions.
## Next target
`canary tid=6 → ours tid=1` main, idx **102404**:
`NtQueryFullAttributesFile`
- canary return_value=0 (`STATUS_SUCCESS`) — file/path exists
- ours return_value=3221225524 (`0xC0000034 STATUS_OBJECT_NAME_NOT_FOUND`) — file/path missing
Likely a VFS/cache path-existence divergence: canary has a pre-populated
content directory or HDD-backing mount that responds SUCCESS to a
specific Sylpheed query path, while ours's fresh-boot VFS rejects it.
Sister of AUDIT-053/054 (VFS layout aliasing) and the AUDIT-052 cache
persistence question.
The args/path string is not captured in the Phase A event payload
(emitter gap — kernel.call args resolution for `lpstring_t` not wired).
First step in Phase C+10 will be capturing the path (either via the
existing emitter or a one-shot --trace-imports run with the same
ISO + binary).

View File

@@ -0,0 +1,25 @@
{
"build_id": "ours-phaseB",
"cvars": {
"phase_b_dump_section_content": false,
"phase_b_snapshot_and_exit": true,
"phase_b_snapshot_dir": "/home/fabi/RE - Project Sylpheed/xenia-rs/audit-runs/phase-c9-XamContentCreateEnumerator/snap"
},
"deterministic_skip": [
"host_ns_at_snapshot",
"wall_clock_iso8601",
"build_id",
"iso_path",
"cvars.phase_b_snapshot_dir"
],
"engine": "ours",
"host_ns_at_snapshot": 0,
"image_loaded_sha256": "ea8d160e9369328a5b922258a92113efb8d7ce3e1a5c12cc521e375985c91c18",
"iso_path": "",
"schema_version": 1,
"wall_clock_iso8601": "epoch:0",
"xex_entry_point": "0x824ab748",
"xex_header_sha256": "0000000000000000000000000000000000000000000000000000000000000000",
"xex_image_base": "0x82000000",
"xex_image_size": 9568256
}

View File

@@ -0,0 +1,234 @@
{
"cr": [
"0x0",
"0x0",
"0x0",
"0x0",
"0x0",
"0x0",
"0x0",
"0x0"
],
"ctr": "0x0000000000000000",
"deterministic_skip": [
"hw_id"
],
"engine": "ours",
"fpr": [
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000"
],
"fpscr": "0x00000000",
"gpr": [
"0x0000000000000000",
"0x00000000700fff00",
"0x0000000020000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x000000007fff0000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000"
],
"hw_id": 0,
"lr": "0x00000000bcbcbcbc",
"msr": "0x0000000000009030",
"pc": "0x824ab748",
"pcr_base": "0x7fff0000",
"schema_version": 1,
"stack_base": "0x00000000",
"stack_limit": "0x00000000",
"thread_id": 1,
"tls_base": "0x00000000",
"vr": [
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000"
],
"vrsave": "0xffffffff",
"vscr": "00000000000000000000000000010000",
"xer": {
"ca": 0,
"ov": 0,
"so": 0,
"tbc": 0
}
}

View File

@@ -0,0 +1,62 @@
{
"deterministic_skip": [
"raw_handle_id",
"exports_registered_count"
],
"engine": "ours",
"exports_registered_count": 199,
"exports_registered_sample": [
"xam.xex!NetDll_WSACleanup",
"xam.xex!NetDll_WSAStartup",
"xam.xex!XGetAVPack",
"xam.xex!XGetGameRegion",
"xam.xex!XGetLanguage",
"xam.xex!XGetVideoMode",
"xam.xex!XMsgInProcessCall",
"xam.xex!XMsgStartIORequest",
"xam.xex!XMsgStartIORequestEx",
"xam.xex!XNotifyGetNext",
"xam.xex!XNotifyPositionUI",
"xam.xex!XamAlloc",
"xam.xex!XamContentClose",
"xam.xex!XamContentCreate",
"xam.xex!XamContentCreateEnumerator",
"xam.xex!XamContentDelete",
"xam.xex!XamContentGetCreator",
"xam.xex!XamContentGetDeviceData",
"xam.xex!XamContentGetDeviceName",
"xam.xex!XamContentGetDeviceState",
"xam.xex!XamContentSetThumbnail",
"xam.xex!XamEnableInactivityProcessing",
"xam.xex!XamEnumerate",
"xam.xex!XamFree",
"xam.xex!XamGetExecutionId",
"xam.xex!XamGetSystemVersion",
"xam.xex!XamInputGetCapabilities",
"xam.xex!XamInputGetKeystrokeEx",
"xam.xex!XamInputGetState",
"xam.xex!XamInputSetState",
"xam.xex!XamLoaderLaunchTitle",
"xam.xex!XamLoaderTerminateTitle"
],
"exports_registered_sha256": "bca7668a2a76ce1d1cc4dba8a862a2f16ec6ee3b2aab8a71d8d8bc0ccc89a097",
"handle_name_table": [],
"notification_listeners": [],
"objects": [
{
"details": {
"entry_pc": "0x824ab748",
"exit_code": null,
"hw_id": 0,
"is_entry_thread": true,
"thread_id": 1
},
"handle_semantic_id": "9879c5053fedb1d0",
"name": null,
"raw_handle_id": "0x00001000",
"type": "Thread",
"type_code": 5
}
],
"schema_version": 1
}

View File

@@ -0,0 +1,11 @@
{
"engine": "ours",
"files": {
"config.json": "e513ea2b2b8c3ab9866e788b287416cf8f2803c50eb8102774242a811fac3b53",
"cpu_state.json": "4e6df54ca1939d08854f3a52b49ed2c5ee0823d63cdecad8a7395203dac5443a",
"kernel.json": "2db219d4ca8b0313e53be379b8fcf90ab13b99116e6fac5601f6bdefd1aa6900",
"memory.json": "b96ae4daebfbdd314e574492c1e162f532fa4f89ff5c0d7c6c29743797089cf1",
"vfs.json": "97bb2bda57266d8e0dd1da13309eab5ece43130ef378a0b682917d299e9dc4e1"
},
"schema_version": 1
}

View File

@@ -0,0 +1,84 @@
{
"committed_pages_total": 2594,
"deterministic_skip": [
"host_base_pointer"
],
"engine": "ours",
"guest_address_space_bytes": 4294967296,
"heaps": [
{
"base": "0x00000000",
"name": "v00000000",
"page_size": 4096,
"page_state_histogram": {
"committed": 0
},
"size": "0x40000000"
},
{
"base": "0x40000000",
"name": "v40000000",
"page_size": 4096,
"page_state_histogram": {
"committed": 266
},
"size": "0x40000000"
},
{
"base": "0x80000000",
"name": "v80000000",
"page_size": 4096,
"page_state_histogram": {
"committed": 2336
},
"size": "0x40000000"
},
{
"base": "0x90000000",
"name": "v90000000",
"page_size": 4096,
"page_state_histogram": {
"committed": 0
},
"size": "0x40000000"
}
],
"page_size": 4096,
"regions": [
{
"byte_count": 1048576,
"end": "0x70100000",
"protect": 0,
"section_kind": null,
"sha256": "30e14955ebf1352266dc2ff8067e68104607e750abb9d3b36582b8af909fcb58",
"start": "0x70000000"
},
{
"byte_count": 4096,
"end": "0x7ffe1000",
"protect": 0,
"section_kind": null,
"sha256": "ad7facb2586fc6e966c004d7d1d16b024f5805ff7cb47c7a85dabd8b48892ca7",
"start": "0x7ffe0000"
},
{
"byte_count": 4096,
"end": "0x7fff1000",
"protect": 0,
"section_kind": null,
"sha256": "e35cddaf9c210aed7505ec4cf1c599f58ac2b7ec25b0885db1ee49aba2db519a",
"start": "0x7fff0000"
},
{
"byte_count": 9568256,
"end": "0x82920000",
"protect": 0,
"section_kind": null,
"sha256": "ea8d160e9369328a5b922258a92113efb8d7ce3e1a5c12cc521e375985c91c18",
"start": "0x82000000"
}
],
"regions_walked": [],
"schema_version": 1,
"section_contents": null
}

View File

@@ -0,0 +1,71 @@
{
"cache_root_listing": [],
"deterministic_skip": [
"host_path_realpath"
],
"engine": "ours",
"mounted_devices_observed_count": 1,
"resolve_path_probes": [
{
"is_directory": true,
"path": "\\Device\\Cdrom0",
"resolved": true,
"size": null
},
{
"is_directory": true,
"path": "\\Device\\Cdrom0\\dat",
"resolved": true,
"size": 4096
},
{
"is_directory": null,
"path": "\\Device\\Cdrom0\\dat\\movie",
"resolved": false,
"size": null
},
{
"is_directory": null,
"path": "\\Device\\Cdrom0\\dat\\movie\\opening.bik",
"resolved": false,
"size": null
},
{
"is_directory": false,
"path": "\\Device\\Cdrom0\\default.xex",
"resolved": true,
"size": 3497984
},
{
"is_directory": null,
"path": "\\Device\\HardDisk0\\Partition1",
"resolved": false,
"size": null
},
{
"is_directory": true,
"path": "cache:\\",
"resolved": true,
"size": null
},
{
"is_directory": null,
"path": "cache:\\nonexistent_probe",
"resolved": false,
"size": null
},
{
"is_directory": true,
"path": "game:\\dat",
"resolved": true,
"size": 4096
},
{
"is_directory": false,
"path": "game:\\default.xex",
"resolved": true,
"size": 3497984
}
],
"schema_version": 1
}