wasm-emu: add file-open diff2 arming and document compiled-store repro

Add ?nostoretci and ?tcgdiff2=open browser-test plumbing. ?nostoretci confirms
the original compiled-store corruption still reproduces at normal speed; the
file-open-armed E11 run catches stale register state before the known
0x4201e210 load fault. Production path remains store-TCI and browser raw-SD
stays green.
This commit is contained in:
2026-07-22 18:16:05 -04:00
parent a5de735742
commit 78df20560e
4 changed files with 54 additions and 2 deletions
+47
View File
@@ -360,3 +360,50 @@ 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.
## E12/E13: normal-speed compiled stores and file-open-armed E11
### `?nostoretci` probe
Added opt-in `?nostoretci` to disable the store-TCI workaround without enabling
any differential overhead. Result: the exact original corruption still
reproduces at normal speed during reader open:
- `Load access fault`
- `MEPC 0x4201e210`
- `MTVAL 0x3fcb8004` / invalid read near the inflate/text buffer region
So store-TCI is still required in production.
### `?tcgdiff2=open`
Arming E11 from boot was too slow or perturbed app init. Added a browser-test
hook so `?tcgdiff2=open` arms `wasm_diff_enable2()` immediately before the test
presses Confirm to open `example.txt`. Boot and app init therefore run on the
normal production path, and compiled stores become live only over the repro
interval.
With the all-app-flash window (`0x42000000..0x42200000`), the first observed
mode-2 divergence after arming is register state, not DRAM:
```text
TCGDIFF pc=0x4201d6ea gpr[2] compiled=0x3fcb42f0 tci=0x3fcb42e0
pre-ra=0x42029374 pre-sp=0x3fcb42f0
```
This is `utf8NextCodepoint()`: TCI applies the stack-frame prologue (`sp -= 16`)
while the compiled-live path has stale `sp` in `CPUArchState`. The later crash
is still the known invalid read at `MEPC 0x4201e210`.
### Current interpretation
E10 remains green because it forces no-chain and runs TCI live, then compiled as
a disposable shadow. E11 is the real compiled-live path and exposes state that is
stale across compiled execution/chaining boundaries. The remaining suspect is no
longer individual store codegen; it is **compiled TB state materialization**:
WASM register globals and `CPUArchState` can diverge across real compiled-live
entry/exit/chaining, and store-TCI masks the bad store-containing TBs by routing
them through TCI.
A tested broad fix candidate — making store TBs return to C instead of using the
same-module `BLOCK_PTR` chain — caused an app-init interrupt-watchdog timeout,
so it is not viable as-is. A narrower fix should target state materialization
(flush/reload live guest register state at compiled TB boundaries or before
helper/exception-visible points) rather than disabling chaining wholesale.
+2
View File
@@ -165,6 +165,8 @@ try:
press(confirm, 'opening Browse Files')
press(confirm, 'opening Test directory')
press(confirm, 'highlighting example.txt')
if 'tcgdiff2=open' in url:
driver.execute_script('window.xteinkArmTcgDiff2 && window.xteinkArmTcgDiff2()')
open_start = time.monotonic()
press(confirm, 'opening example.txt')
open_seconds = time.monotonic() - open_start
+4 -1
View File
@@ -490,8 +490,11 @@ async function boot(file, variant, setStatus) {
stdin: () => null,
};
options.preRun = [() => {
if (params.has('tcgdiff2')) options._wasm_diff_enable2();
if (params.get('tcgdiff2') === 'open') {
window.xteinkArmTcgDiff2 = () => options._wasm_diff_enable2();
} else if (params.has('tcgdiff2')) options._wasm_diff_enable2();
else if (params.has('tcgdiff')) options._wasm_diff_enable();
if (params.has('nostoretci')) options._wasm_disable_store_tci();
options.FS.mkdirTree('/bios');
options.FS.mkdirTree('/var/tmp');
options.FS.writeFile('/bios/esp32c3-rom.bin', rom);