diff --git a/docs/wasm-inflate-panic.md b/docs/wasm-inflate-panic.md index 75b8a4a..0cff6dc 100644 --- a/docs/wasm-inflate-panic.md +++ b/docs/wasm-inflate-panic.md @@ -312,3 +312,51 @@ from reset), not more per-TB work. The `?tcgdiff` harness is expensive (full DRAM + TLB snapshot and double execution per candidate; heap grows to ~1 GiB, frame gen drops) but is debug-only and does not affect the production store-TCI path. + +## E11: whole-execution differential (opt-in `?tcgdiff2`) — live compiled vs TCI shadow + +Mode 2 inverts E10: the **compiled** path runs LIVE and authoritative (the real +buggy path, so corruption *accumulates* across TBs exactly as in the crash), +while a per-TB **TCI shadow** — restored from the pre-snapshot — is the +known-good reference. Store-TCI is disabled under this mode so compiled stores +actually execute. Per eligible store TB: snapshot pre (env, neg, deep TLB, +DRAM) -> run compiled live, capture full compiled-post -> rewind -> run TCI +shadow -> compare -> restore compiled-post -> continue. Opt-in via `?tcgdiff2`. + +### Result +- **153,000+ live-compiled-vs-TCI comparisons with accumulation, zero + divergence** (bootloader region). Combined with E10 (160k reader-path), inline + compiled stores are correct even under live cross-TB accumulation. +- Revealed a large blind spot: ~90% of store TBs complete via the store + **slow-path** (`done_flag == 1`, TLB-miss -> ld/st helper), not the pure + inline path (`done_flag == 2`). Mode 2 was extended to compare slow-path + completions too. + +### Why it cannot reach the reader corruption (impractical in-browser) +The 2x execution + full DRAM (0x60000) + deep-TLB snapshot per eligible TB is +~1000x slower than normal. Consequences: +- The guest **interrupt watchdog** fires first: gating comparison to app-flash + produced an `Interrupt wdt timeout` panic (MEPC 0x40380264, IRAM idle) during + cold boot — a harness-overhead artifact, not the corruption Guru Meditation + (MEPC 0x4201e210). +- Ungated, it reached only ~153k comparisons (bootloader, gen 2) before the + 1100s timeout. Full reader-open is millions of TBs away. Defanging the + watchdog would not help; raw throughput is the wall. + +### Narrowed suspects (what the differential structurally cannot cheaply test) +1. **Store slow-path** (TLB miss -> `helper_stN_mmu` -> Asyncify unwind/resume), + the ~90% of stores mode 2 excluded by the `done_flag == 2` gate. Aligns with + E9's `DONE_FLAG` (thread-global) vs `UNWINDING` (per-call) finding. +2. **Helper-containing store TBs**, excluded by the side-effect-safety filter. +3. **8/32-bit store-width interaction** (E1-E8's "needs both widths"). + +### Decision +In-browser whole-execution differential is retired as a repro path: it proves +live-accumulation correctness of inline stores but cannot reach the crash. Next +instrument is a deterministic, framework-free **slow-path store differential +unit test** (no boot, no watchdog, no perf wall) targeting suspect (1)/(3). +Option A (sequential deterministic two-pass) remains a documented future option +if a full-execution comparison is later required. + +Both `?tcgdiff` (E10) and `?tcgdiff2` (E11) are retained as opt-in tools; +production store-TCI path is unchanged. diff --git a/third_party/qemu b/third_party/qemu index 1473b7a..4dd5425 160000 --- a/third_party/qemu +++ b/third_party/qemu @@ -1 +1 @@ -Subproject commit 1473b7ab103ef0865b74d04cdd5548a98596ec95 +Subproject commit 4dd54253ab8376cd523b8774c6338e44011f29c5 diff --git a/web/app.js b/web/app.js index fdef71f..52d5ae4 100644 --- a/web/app.js +++ b/web/app.js @@ -490,7 +490,8 @@ async function boot(file, variant, setStatus) { stdin: () => null, }; options.preRun = [() => { - if (params.has('tcgdiff')) options._wasm_diff_enable(); + if (params.has('tcgdiff2')) options._wasm_diff_enable2(); + else if (params.has('tcgdiff')) options._wasm_diff_enable(); options.FS.mkdirTree('/bios'); options.FS.mkdirTree('/var/tmp'); options.FS.writeFile('/bios/esp32c3-rom.bin', rom);