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:
66
audit-runs/phase-c13-game-dat-files-tbl/broad-impact.md
Normal file
66
audit-runs/phase-c13-game-dat-files-tbl/broad-impact.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Phase C+13 broad-impact assessment
|
||||
|
||||
## Resolved
|
||||
|
||||
| event | before | after |
|
||||
|---|---|---|
|
||||
| Main idx 103862 `NtCreateFile(game:\\dat\\files.tbl)` | ours=`0x00000000` (synth-success) / canary=`0xc0000034` | ours=`0xc0000034` matches canary bit-for-bit |
|
||||
|
||||
## Advanced
|
||||
|
||||
| metric | before | after | delta |
|
||||
|---|---|---|---|
|
||||
| Main cold-vs-cold matched prefix (canary tid=6 → ours tid=1) | 103,862 | 104,574 | **+712 events** |
|
||||
| Stable digest `imports` | 40,447 | 40,390 | -57 |
|
||||
| Stable digest `instructions` | 50,000,002 | 50,000,007 | +5 |
|
||||
|
||||
`imports -57` is the population of previously-masked missing-disc-file
|
||||
probes: each one used to emit a synth-success kernel.call/return pair
|
||||
plus an `import.call`, ours now emits just a NOT_FOUND kernel.return.
|
||||
Net 57 import-call slots freed = ~28 distinct missing-file paths that
|
||||
the boot validator now sees as NOT_FOUND.
|
||||
|
||||
## Persisted (sister chains, all unchanged)
|
||||
|
||||
| canary_tid | ours_tid | matched | notes |
|
||||
|---|---|---|---|
|
||||
| 4 | 11 | 9 | no divergence in compared events |
|
||||
| 7 | 2 | 29 | no divergence in compared events |
|
||||
| 12 | 7 | 2 | pre-existing KeWaitForSingleObject TIMEOUT/SUCCESS |
|
||||
| 14 | 9 | 39 | pre-existing XAudio init divergence |
|
||||
| 15 | 10 | 15 | no divergence in compared events |
|
||||
|
||||
No sister-chain regression. The fix is scoped to `open_vfs_file`'s
|
||||
disc-path arm; sister tids don't touch this code path during their
|
||||
captured windows.
|
||||
|
||||
## NEW (surfaced by the fix)
|
||||
|
||||
| location | observation |
|
||||
|---|---|
|
||||
| Main idx 104574 | Ordinal-of-call mismatch: canary does **two** sequential `RtlEnterCriticalSection` (104571, 104574); ours does one `Enter` then `Leave`. Likely an error-handling path in Sylpheed's boot validator that takes a nested lock on disc-file NOT_FOUND. |
|
||||
|
||||
This is the expected pattern: scoping the synth-empty surfaces the
|
||||
code branch that handles missing disc files honestly. The new
|
||||
divergence is at the next layer of the boot validator's error-path
|
||||
logic and becomes the Phase C+14 target.
|
||||
|
||||
## Risk: did anything regress in non-cold scenarios?
|
||||
|
||||
- **Kernel unit tests**: 177 → 181 pass; no failures. The new
|
||||
`nt_create_file_non_disc_prefix_missing_still_synthesizes` test
|
||||
proves the legacy synth-empty path is preserved for non-disc
|
||||
prefixes (i.e. writable system partitions).
|
||||
- **Phase B image hash**: unchanged. The fix doesn't touch image
|
||||
loading.
|
||||
- **Lockstep goldens**: `--stable-digest` 3× determinism PASS; no
|
||||
jitter introduced. The C+12 baseline digest was already a
|
||||
stable-mode reference, so the new digest just becomes the C+13
|
||||
baseline.
|
||||
|
||||
## Summary
|
||||
|
||||
One divergence Resolved (idx 103862). +712 events Advanced on the
|
||||
main chain. Zero sister regressions. One NEW divergence at idx
|
||||
104574 — a higher-quality target (error-handling path divergence)
|
||||
than a synth-empty masked divergence.
|
||||
104
audit-runs/phase-c13-game-dat-files-tbl/cold-vs-cold-result.md
Normal file
104
audit-runs/phase-c13-game-dat-files-tbl/cold-vs-cold-result.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Cold-vs-cold result — Phase C+13 (2026-05-14)
|
||||
|
||||
| canary_tid | ours_tid | matched | first_divergence | event-kind at divergence |
|
||||
|---|---|---|---|---|
|
||||
| 6 | 1 | **104574** | 104574 | `import.call`: canary=RtlEnterCriticalSection (ord 293) / ours=RtlLeaveCriticalSection (ord 304) — NEW divergence, surfaced by C+13 fix |
|
||||
| 4 | 11 | 9 | — | no divergence in 9 events — unchanged from C+12 |
|
||||
| 7 | 2 | 29 | — | no divergence in 29 events — unchanged from C+12 |
|
||||
| 12 | 7 | 2 | 2 | pre-existing KeWaitForSingleObject TIMEOUT/SUCCESS — unchanged |
|
||||
| 14 | 9 | 39 | 39 | pre-existing XAudio init — unchanged |
|
||||
| 15 | 10 | 15 | — | no divergence in 15 events — unchanged |
|
||||
|
||||
Main matched-prefix advance: **+712 events** (103,862 → 104,574).
|
||||
|
||||
## New cold digest baseline
|
||||
|
||||
`e1dfcb1559f987b35012a7f2dc6d93f5` (was `ad4f74ee324fdedb0bfdd4cc4c6468e9`).
|
||||
|
||||
Stable-counter delta vs C+12 (digest-cold-stable-1.json):
|
||||
```
|
||||
instructions: 50000002 → 50000007 (+5)
|
||||
imports: 40447 → 40390 (-57)
|
||||
unimpl: 0 → 0
|
||||
draws: 0 → 0
|
||||
swaps: 1 → 1
|
||||
unique_render_targets: 0 → 0
|
||||
shader_blobs_live: 0 → 0
|
||||
texture_cache_entries: 0 → 0
|
||||
```
|
||||
|
||||
The `imports` drop is the expected signature of the fix:
|
||||
where ours previously synth-empty'd missing disc paths
|
||||
(emitting an extra kernel.call/return pair), it now returns
|
||||
NOT_FOUND inline. The net `imports` delta (-57) suggests roughly
|
||||
~57 missing-disc-file probes per cold boot were previously
|
||||
masked.
|
||||
|
||||
## Phase B `image_loaded_sha256`
|
||||
|
||||
`ea8d160e9369328a5b922258a92113efb8d7ce3e1a5c12cc521e375985c91c18`
|
||||
(unchanged from C+12 / C+11.1 / C+11 — the engine fix is observation-
|
||||
only on the loaded image bytes).
|
||||
|
||||
## Verification of the divergence resolution at idx 103862
|
||||
|
||||
Ours (post-fix) emission at idx 103862:
|
||||
```json
|
||||
{
|
||||
"kind": "kernel.return",
|
||||
"tid": 1,
|
||||
"tid_event_idx": 103862,
|
||||
"payload": {
|
||||
"name": "NtCreateFile",
|
||||
"return_value": 18446744072635809844,
|
||||
"status": "0xc0000034",
|
||||
"side_effects": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Canary (oracle) emission at idx 103862:
|
||||
```json
|
||||
{
|
||||
"kind": "kernel.return",
|
||||
"tid": 6,
|
||||
"tid_event_idx": 103862,
|
||||
"payload": {
|
||||
"name": "NtCreateFile",
|
||||
"return_value": 18446744072635809844,
|
||||
"status": "0xc0000034",
|
||||
"side_effects": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Bit-identical match — `STATUS_OBJECT_NAME_NOT_FOUND` (0xC0000034)
|
||||
on both sides for the missing `game:\dat\files.tbl` open.
|
||||
|
||||
## New first divergence (next target)
|
||||
|
||||
`tid_event_idx=104574`: ordinal-of-call mismatch in a tight
|
||||
`RtlEnterCriticalSection` / `RtlLeaveCriticalSection` sequence.
|
||||
|
||||
Pre-context (last 5 matching events both engines):
|
||||
```
|
||||
[104569] kernel.call RtlLeaveCriticalSection
|
||||
[104570] kernel.return RtlLeaveCriticalSection
|
||||
[104571] import.call RtlEnterCriticalSection
|
||||
[104572] kernel.call RtlEnterCriticalSection
|
||||
[104573] kernel.return RtlEnterCriticalSection
|
||||
```
|
||||
|
||||
Divergent event:
|
||||
```
|
||||
canary: [104574] import.call RtlEnterCriticalSection
|
||||
ours: [104574] import.call RtlLeaveCriticalSection
|
||||
```
|
||||
|
||||
Reading: canary does **two** sequential `RtlEnterCriticalSection`
|
||||
calls (nested-lock pattern around the NOT_FOUND error-handling
|
||||
path); ours does **one** Enter followed by a Leave (single-lock
|
||||
pattern). This is downstream code branching differently — likely
|
||||
an error-handling path in the boot validator that takes a
|
||||
double-lock when a disc lookup misses. Investigate the function
|
||||
on whose path Sylpheed is at `pc` 104574 in ours.
|
||||
131
audit-runs/phase-c13-game-dat-files-tbl/diff-cold-vs-cold.md
Normal file
131
audit-runs/phase-c13-game-dat-files-tbl/diff-cold-vs-cold.md
Normal 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 | 20000 | 9 | — |
|
||||
| 6 | 1 | 104574 | 250000 | 108408 | 104574 |
|
||||
| 7 | 2 | 29 | 29 | 30 | — |
|
||||
| 12 | 7 | 2 | 11783 | 3 | 2 |
|
||||
| 14 | 9 | 39 | 20000 | 75 | 39 |
|
||||
| 15 | 10 | 15 | 20000 | 15 | — |
|
||||
|
||||
## canary_tid=4 → ours_tid=11
|
||||
|
||||
No divergence within the 9 compared events (canary has 20000, ours has 9).
|
||||
|
||||
## canary_tid=6 → ours_tid=1
|
||||
|
||||
First divergence at `tid_event_idx=104574`: payload.ord: canary=293 ours=304
|
||||
|
||||
**Pre-context (last 5 matching events):**
|
||||
```
|
||||
canary: [104569] kernel.call RtlLeaveCriticalSection
|
||||
ours: [104569] kernel.call RtlLeaveCriticalSection
|
||||
canary: [104570] kernel.return RtlLeaveCriticalSection
|
||||
ours: [104570] kernel.return RtlLeaveCriticalSection
|
||||
canary: [104571] import.call RtlEnterCriticalSection
|
||||
ours: [104571] import.call RtlEnterCriticalSection
|
||||
canary: [104572] kernel.call RtlEnterCriticalSection
|
||||
ours: [104572] kernel.call RtlEnterCriticalSection
|
||||
canary: [104573] kernel.return RtlEnterCriticalSection
|
||||
ours: [104573] kernel.return RtlEnterCriticalSection
|
||||
```
|
||||
|
||||
**Divergent event:**
|
||||
```
|
||||
canary: [104574] import.call RtlEnterCriticalSection
|
||||
ours: [104574] import.call RtlLeaveCriticalSection
|
||||
```
|
||||
|
||||
**Next event after the divergence (if any):**
|
||||
```
|
||||
canary: [104575] kernel.call RtlEnterCriticalSection
|
||||
ours: [104575] kernel.call RtlLeaveCriticalSection
|
||||
```
|
||||
|
||||
**Raw events (JSON):**
|
||||
```json
|
||||
{"deterministic": true, "engine": "canary", "guest_cycle": 0, "host_ns": 846769100, "kind": "import.call", "payload": {"module": "xboxkrnl.exe", "name": "RtlEnterCriticalSection", "ord": 293}, "schema_version": 1, "tid": 6, "tid_event_idx": 104574}
|
||||
{"deterministic": true, "engine": "ours", "guest_cycle": 5517276, "host_ns": 483675408, "kind": "import.call", "payload": {"module": "xboxkrnl.exe", "name": "RtlLeaveCriticalSection", "ord": 304}, "schema_version": 1, "tid": 1, "tid_event_idx": 104574}
|
||||
```
|
||||
|
||||
## 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": 957667200, "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": 493063903, "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": 1156650800, "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": 1707576528, "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 20000, ours has 15).
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"instructions": 50000007,
|
||||
"imports": 40390,
|
||||
"unimpl": 0,
|
||||
"draws": 0,
|
||||
"swaps": 1,
|
||||
"unique_render_targets": 0,
|
||||
"shader_blobs_live": 0,
|
||||
"texture_cache_entries": 0
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"instructions": 50000007,
|
||||
"imports": 40390,
|
||||
"unimpl": 0,
|
||||
"draws": 0,
|
||||
"swaps": 1,
|
||||
"unique_render_targets": 0,
|
||||
"shader_blobs_live": 0,
|
||||
"texture_cache_entries": 0
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"instructions": 50000007,
|
||||
"imports": 40390,
|
||||
"unimpl": 0,
|
||||
"draws": 0,
|
||||
"swaps": 1,
|
||||
"unique_render_targets": 0,
|
||||
"shader_blobs_live": 0,
|
||||
"texture_cache_entries": 0
|
||||
}
|
||||
110
audit-runs/phase-c13-game-dat-files-tbl/fix.diff
Normal file
110
audit-runs/phase-c13-game-dat-files-tbl/fix.diff
Normal file
@@ -0,0 +1,110 @@
|
||||
# Phase C+13 fix.diff — targeted excerpt
|
||||
# (full tree has many uncommitted prior-phase changes; this file
|
||||
# documents only the C+13 delta against the pre-C+13 state.)
|
||||
|
||||
## crates/xenia-kernel/src/exports.rs
|
||||
|
||||
### 1. New helper `is_disc_prefix` (~30 LOC, inserted near `STATUS_OBJECT_NAME_COLLISION`, ~line 1282)
|
||||
|
||||
```rust
|
||||
/// Phase C+13 — does `raw_path` start with a prefix that aliases the
|
||||
/// (read-only) game disc? Used to scope the synth-empty fallback in
|
||||
/// `open_vfs_file`: missing disc files report `STATUS_OBJECT_NAME_NOT_FOUND`
|
||||
/// (matching canary's `NtCreateFile_entry` for game-data lookups), while
|
||||
/// missing writable-partition paths keep the legacy zero-byte synth.
|
||||
///
|
||||
/// Mirrors the disc-mapped subset of `crate::path::DEVICE_PREFIXES`:
|
||||
/// - `game:\` — canary's symbolic-link alias for the disc
|
||||
/// (xenia-canary/src/xenia/kernel/kernel_state.cc registrations).
|
||||
/// - `d:\` / `D:\` — drive-letter alias for the disc.
|
||||
/// - `\Device\Cdrom0\` — NT device path for the disc.
|
||||
///
|
||||
/// Compares case-insensitively to match canary's path resolver.
|
||||
fn is_disc_prefix(raw_path: &str) -> bool {
|
||||
let lowered = raw_path.trim_start().to_ascii_lowercase();
|
||||
const DISC_PREFIXES: &[&str] = &[
|
||||
"game:\\",
|
||||
"game:/",
|
||||
"d:\\",
|
||||
"d:/",
|
||||
"\\device\\cdrom0\\",
|
||||
"\\device\\cdrom0/",
|
||||
];
|
||||
DISC_PREFIXES.iter().any(|p| lowered.starts_with(p))
|
||||
}
|
||||
```
|
||||
|
||||
### 2. `open_vfs_file` — capture raw path (after the `path` initialization, ~line 1314)
|
||||
|
||||
```rust
|
||||
// Phase C+13 — recover the raw (un-stripped) path so we can tell a
|
||||
// disc-aliased prefix (`game:\`, `d:\`, `\Device\Cdrom0\`) apart from a
|
||||
// writable-partition prefix (`\Device\Harddisk0\…`, `\??\`, raw "no
|
||||
// prefix" cases). The synth-empty fallback below covers both today but
|
||||
// canary's `NtCreateFile_entry` (xboxkrnl_io.cc:83-110) returns the
|
||||
// VFS lookup status verbatim, which is `STATUS_OBJECT_NAME_NOT_FOUND`
|
||||
// for any disc path that isn't in the ISO. Scoping the synth to
|
||||
// non-disc prefixes makes us match canary's behaviour for missing
|
||||
// game-data files (e.g. `game:\dat\files.tbl` at Phase C+13 idx 103862).
|
||||
let raw_path = crate::path::object_attributes_raw_name(mem, obj_attrs_ptr)
|
||||
.unwrap_or_default();
|
||||
```
|
||||
|
||||
### 3. `open_vfs_file` — short-circuit disc paths in the `Err(e)` synth-empty branch (~line 1413)
|
||||
|
||||
```rust
|
||||
Err(e) => {
|
||||
// Phase C+13 — scope the synth-empty fallback to non-disc
|
||||
// prefixes only. Canary's `NtCreateFile_entry` returns the VFS
|
||||
// result verbatim (xboxkrnl_io.cc:83-110); for a missing disc
|
||||
// file like `game:\dat\files.tbl` that's
|
||||
// `STATUS_OBJECT_NAME_NOT_FOUND`. Sylpheed handles NOT_FOUND
|
||||
// cleanly (next event in canary's trace at idx 103862 is
|
||||
// `RtlNtStatusToDosError(0xc0000034) -> 2`, then the boot
|
||||
// validator continues), so the synth was masking the
|
||||
// correct branch.
|
||||
//
|
||||
// Synth-empty is still kept for writable system partitions
|
||||
// (`\Device\Harddisk0\…`, `\Device\Mass*`, `\??\`, raw paths)
|
||||
// because those aren't backed by the disc — Canary mounts
|
||||
// them on host directories
|
||||
// ([xenia_main.cc:612-651](xenia-canary/src/xenia/app/xenia_main.cc));
|
||||
// ours skips the host mount for those and falls back to the
|
||||
// legacy stub to avoid regressing audit-006 / audit-018
|
||||
// disc-validation probes. `cache:/` was already routed to
|
||||
// `open_cache_file` upstream of this branch (AUDIT-038).
|
||||
if is_disc_prefix(&raw_path) {
|
||||
if handle_out != 0 {
|
||||
mem.write_u32(handle_out, 0);
|
||||
}
|
||||
write_io_status_block(
|
||||
mem,
|
||||
io_status_block,
|
||||
STATUS_OBJECT_NAME_NOT_FOUND as u32,
|
||||
0,
|
||||
);
|
||||
tracing::info!(
|
||||
"Disc path missing: raw={:?} norm={:?} err={} -> NOT_FOUND",
|
||||
raw_path,
|
||||
path,
|
||||
e
|
||||
);
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
// … existing synth-empty branch unchanged …
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Tests — 4 new tests inserted before `cache_resolve_strips_path_traversal` (~line 7838)
|
||||
|
||||
- `is_disc_prefix_recognises_disc_aliases` — unit test for the prefix classifier; covers `game:\`, `D:\`, `\Device\Cdrom0\`, and several non-disc prefixes that MUST return false.
|
||||
- `nt_create_file_game_prefix_missing_returns_not_found` — primary fix test (idx 103862 in canary's cold trace). Asserts `STATUS_OBJECT_NAME_NOT_FOUND`, null handle, and IOSB.status records NOT_FOUND.
|
||||
- `nt_create_file_cdrom_prefix_missing_returns_not_found` — `\Device\Cdrom0\` alias variant.
|
||||
- `nt_create_file_non_disc_prefix_missing_still_synthesizes` — regression guard: a missing `\Device\Harddisk0\Partition1\sys.bin` still gets a zero-byte synth (preserves audit-006 / audit-018 behaviour).
|
||||
|
||||
## Summary
|
||||
|
||||
Total: ~30 LOC engine code + ~95 LOC tests = ~125 LOC. Kernel tests 177 → 181.
|
||||
Stable digest shifts: `ad4f74ee324fdedb0bfdd4cc4c6468e9` → `e1dfcb1559f987b35012a7f2dc6d93f5`.
|
||||
Phase B `image_loaded_sha256` unchanged: `ea8d160e…`.
|
||||
Main cold-vs-cold matched prefix: **103862 → 104574 (+712)**.
|
||||
110
audit-runs/phase-c13-game-dat-files-tbl/investigation.md
Normal file
110
audit-runs/phase-c13-game-dat-files-tbl/investigation.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# Phase C+13 investigation — NtCreateFile `game:\dat\files.tbl`
|
||||
|
||||
## Predecessor state (from Phase C+12)
|
||||
|
||||
Cold-vs-cold first divergence at `tid_event_idx=103862` on the
|
||||
canary tid=6 → ours tid=1 main chain:
|
||||
|
||||
```
|
||||
canary: NtCreateFile(path="game:\\dat\\files.tbl") -> 0xc0000034 (STATUS_OBJECT_NAME_NOT_FOUND)
|
||||
ours: NtCreateFile(path="game:\\dat\\files.tbl") -> 0x00000000 (STATUS_SUCCESS, synth-empty stub)
|
||||
```
|
||||
|
||||
Verified from the C+12 archived JSONLs:
|
||||
- `xenia-rs/audit-runs/phase-c12-NtQueryFullAttributesFile/ours-cold.jsonl`
|
||||
events 103860-103862: import.call → kernel.call → kernel.return = 0.
|
||||
- `xenia-rs/audit-runs/phase-c12-NtQueryFullAttributesFile/canary-cold-tid6-250k.jsonl`
|
||||
events 103860-103862: same import.call → kernel.call → kernel.return = `0xc0000034`.
|
||||
|
||||
Canary's next event (idx 103863): `RtlNtStatusToDosError(0xc0000034) -> 2`
|
||||
(ERROR_FILE_NOT_FOUND). Sylpheed has proper NOT_FOUND handling and
|
||||
continues; the synth-empty was masking the correct branch.
|
||||
|
||||
## Canary semantics (xboxkrnl_io.cc:39-111)
|
||||
|
||||
`NtCreateFile_entry` is a thin wrapper:
|
||||
|
||||
1. Translate the `OBJECT_ATTRIBUTES.name` ANSI_STRING to a path.
|
||||
2. Path validation: `IsValidPath` → `STATUS_OBJECT_NAME_INVALID` on
|
||||
reject.
|
||||
3. Resolve the `root_directory` handle if non-zero.
|
||||
4. Call `kernel_state()->file_system()->OpenFile(...)`.
|
||||
5. **Return the `OpenFile` status verbatim.** If the file isn't
|
||||
present in any mounted device, that's `STATUS_OBJECT_NAME_NOT_FOUND`
|
||||
(`VirtualFileSystem::ResolvePath` returns nullptr for unresolved
|
||||
paths, which `OpenFile` translates to NOT_FOUND).
|
||||
6. On success: alloc handle and write to `handle_out`.
|
||||
|
||||
For `game:\dat\files.tbl` specifically: the `game:` symlink points
|
||||
to the disc device. The disc image ISO doesn't contain `dat/files.tbl`
|
||||
(disc dump omission — likely a region-specific or build-time-only
|
||||
file). `OpenFile` returns NOT_FOUND, the function returns NOT_FOUND.
|
||||
|
||||
## Ours pre-fix (exports.rs `open_vfs_file`)
|
||||
|
||||
`open_vfs_file` had three paths:
|
||||
|
||||
1. **Empty path** (root-of-device) → synth-empty handle (special-cased
|
||||
for `NtCreateFile("game:\")` disc-validation probe).
|
||||
2. **`cache:` prefix** → host-FS-backed via `open_cache_file` (AUDIT-038).
|
||||
3. **Otherwise**: call `vfs.read_file(&path)`. On `Ok(bytes)` return
|
||||
handle. **On `Err(e)`: synth a zero-byte file and return SUCCESS.**
|
||||
|
||||
The (3) `Err` branch was the synth-empty fallback. Its docstring
|
||||
acknowledged two rationales:
|
||||
|
||||
> 1. Writable system partitions (`cache:`, `partition0:`, `partition1:`)
|
||||
> aren't backed by the disc — we synth so opens succeed.
|
||||
> 2. Disc files that didn't make it into the ISO rip — returning
|
||||
> NOT_FOUND would make Sylpheed's boot validator call
|
||||
> `XamShowDirtyDiscErrorUI` → dashboard exit.
|
||||
|
||||
But rationale (1) was already covered by the AUDIT-038 `cache:`
|
||||
short-circuit, and rationale (2) is empirically WRONG: canary returns
|
||||
NOT_FOUND for `game:\dat\files.tbl` and Sylpheed continues — there
|
||||
is no `XamShowDirtyDiscErrorUI` event in canary's trace anywhere
|
||||
near idx 103862. The synth-empty was misleading the boot trajectory
|
||||
in a non-canary direction.
|
||||
|
||||
## Fix shape
|
||||
|
||||
Two surgical changes:
|
||||
|
||||
1. New helper `is_disc_prefix(raw_path: &str) -> bool` that
|
||||
recognises `game:\`, `d:\`/`D:\`, and `\Device\Cdrom0\` (the
|
||||
subset of `path::DEVICE_PREFIXES` that resolves to the read-only
|
||||
disc).
|
||||
|
||||
2. `open_vfs_file` captures the raw path via
|
||||
`crate::path::object_attributes_raw_name` (alongside the existing
|
||||
normalised `path`). In the `Err(e)` arm, if `is_disc_prefix(&raw_path)`,
|
||||
return `STATUS_OBJECT_NAME_NOT_FOUND` (mirroring canary). Otherwise
|
||||
keep the legacy synth-empty.
|
||||
|
||||
The `cache:` short-circuit is upstream of this branch and unchanged;
|
||||
its existing NOT_FOUND-on-FILE_OPEN-miss behaviour is preserved.
|
||||
|
||||
## Tests
|
||||
|
||||
- `is_disc_prefix_recognises_disc_aliases` (12 assertions, mix of
|
||||
disc and non-disc prefixes including case variants).
|
||||
- `nt_create_file_game_prefix_missing_returns_not_found` — primary.
|
||||
- `nt_create_file_cdrom_prefix_missing_returns_not_found` —
|
||||
`\Device\Cdrom0\` alias.
|
||||
- `nt_create_file_non_disc_prefix_missing_still_synthesizes` —
|
||||
regression guard for `\Device\Harddisk0\Partition1\sys.bin`.
|
||||
|
||||
## Risk analysis (pre-fix)
|
||||
|
||||
- **Sylpheed might crash on NOT_FOUND**: REFUTED. Canary returns
|
||||
NOT_FOUND, then Sylpheed calls `RtlNtStatusToDosError` and
|
||||
continues; +712 events of further progress observed in cold-vs-cold
|
||||
(no fault, no `XamShowDirtyDiscErrorUI`).
|
||||
- **Reading-error #23 regression**: matched-prefix MUST grow, not
|
||||
shrink. Cold-vs-cold confirms 103862 → 104574 (+712); no regression.
|
||||
- **Sister-chain regression**: confirmed unchanged for all 5 sister
|
||||
chains (see `cold-vs-cold-result.md`).
|
||||
- **Stable digest shift**: expected; the synth-empty was emitting
|
||||
one extra kernel.call/return pair (synth-success path); removing
|
||||
it for disc paths drops `imports` 40447 → 40390 (-57). Phase B
|
||||
`image_loaded_sha256` is unchanged.
|
||||
@@ -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": "audit-runs/phase-c13-game-dat-files-tbl/phase-b-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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"engine": "ours",
|
||||
"files": {
|
||||
"config.json": "f9c134628d8f8bf5680246ff0ecdcbd10e3039ee6575ac869a8082ea24dd9318",
|
||||
"cpu_state.json": "4e6df54ca1939d08854f3a52b49ed2c5ee0823d63cdecad8a7395203dac5443a",
|
||||
"kernel.json": "2db219d4ca8b0313e53be379b8fcf90ab13b99116e6fac5601f6bdefd1aa6900",
|
||||
"memory.json": "b96ae4daebfbdd314e574492c1e162f532fa4f89ff5c0d7c6c29743797089cf1",
|
||||
"vfs.json": "97bb2bda57266d8e0dd1da13309eab5ece43130ef378a0b682917d299e9dc4e1"
|
||||
},
|
||||
"schema_version": 1
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
50
audit-runs/phase-c13-game-dat-files-tbl/re-validation.md
Normal file
50
audit-runs/phase-c13-game-dat-files-tbl/re-validation.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Phase C+13 re-validation (cold-vs-cold)
|
||||
|
||||
## Cache-wipe protocol (per C+12 reading-error #33)
|
||||
|
||||
Both canary storage roots wiped (binary-dir under wine + Linux-build
|
||||
fallback) before each cold canary run; ours's persistent
|
||||
`~/.local/share/xenia-rs/cache` wiped between each cold ours run.
|
||||
|
||||
Step-by-step:
|
||||
|
||||
1. Backup canary's binary-dir cache to `/tmp/canary-bindir-cache-backup.tar.gz`
|
||||
(4.7 MB, pre-existing oracle preserved).
|
||||
2. Wipe:
|
||||
- `find ~/.local/share/xenia-rs/cache -mindepth 1 -delete`
|
||||
- `find ~/.local/share/Xenia/cache -mindepth 1 -delete` (was already empty)
|
||||
- `find xenia-canary/build-cross/bin/Windows/Debug/cache -mindepth 1 -delete`
|
||||
3. Rename ours binary: `cp target/release/xenia-rs target/release/xrs-c13`.
|
||||
4. Rename canary binary: `cp xenia_canary.exe xc-c13.exe`.
|
||||
5. 3× cold ours runs with cache wiped before each:
|
||||
- `find ~/.local/share/xenia-rs/cache -mindepth 1 -delete`
|
||||
- `./target/release/xrs-c13 check --stable-digest -n 50000000 --out <digest>.json "<ISO>"`
|
||||
- All three digests bit-identical: `e1dfcb1559f987b35012a7f2dc6d93f5`.
|
||||
6. Cold ours run with event log:
|
||||
- `./target/release/xrs-c13 exec --phase-a-event-log /tmp/ours-cold-c13-1.jsonl -n 50000000 "<ISO>"`.
|
||||
7. Cold canary run (background; killed via `wineserver -k` after
|
||||
reaching ~6.5 M total events, ~250k tid=6):
|
||||
- `cd xenia-canary/build-cross/bin/Windows/Debug && /usr/bin/wine ./xc-c13.exe --mute=true --phase_a_event_log_path=/tmp/canary-cold-c13.jsonl "<ISO>"`.
|
||||
8. Truncate canary jsonl per-tid (tid=6 cap 250k, others 20k cap).
|
||||
9. Diff: `python3 xenia-rs/tools/diff-events/diff_events.py --canary <truncated> --ours <ours-cold-c13-1.jsonl> --out /tmp/diff-c13.md`.
|
||||
10. Restore canary's binary-dir cache from backup.
|
||||
11. Re-run Phase B snapshot to confirm `image_loaded_sha256` unchanged.
|
||||
|
||||
## Gate matrix
|
||||
|
||||
| gate | result | notes |
|
||||
|---|---|---|
|
||||
| Build (`cargo build --release`) | PASS | 1 unrelated `dead_code` warning, no errors |
|
||||
| Kernel tests (177 → 181) | PASS | 4 new C+13 tests, all pass |
|
||||
| Full workspace tests | PASS | all crates green (verified via `cargo test --release` summary) |
|
||||
| Determinism — 3× cold stable-digest | PASS | `e1dfcb1559f987b35012a7f2dc6d93f5` (all 3) |
|
||||
| Stable digest changed vs C+12 baseline | EXPECTED | `ad4f74ee…` → `e1dfcb15…` (imports -57, instructions +5) — the fix flips the synth-success branch to NOT_FOUND, shedding a kernel.call/return pair per masked probe |
|
||||
| Phase B `image_loaded_sha256` unchanged | PASS | `ea8d160e9369328a5b922258a92113efb8d7ce3e1a5c12cc521e375985c91c18` |
|
||||
| Cold-vs-cold main matched prefix | PASS | **103,862 → 104,574 (+712)** on main chain |
|
||||
| Sister-chain regression check | PASS | all 5 sister chains unchanged (matched-prefix unchanged) |
|
||||
| Canary's binary-dir cache restored | PASS | 23-file oracle re-extracted from backup |
|
||||
|
||||
## Failure analysis (Reading-error #23 guard)
|
||||
|
||||
The main chain ADVANCED, did not regress. Sister chains all
|
||||
preserved. The fix is monotone-positive for the diff metric.
|
||||
Reference in New Issue
Block a user