From 28ea98687558b1efe0399f9e74ff346bd5401ce3 Mon Sep 17 00:00:00 2001 From: Evan Reichard Date: Thu, 23 Jul 2026 10:37:23 -0400 Subject: [PATCH] wasm-emu: isolate compiled-store corruption to uzlib fault-path TB (E32-E34) --- docs/wasm-inflate-panic.md | 52 ++++++++++++++++++++++++++++++++++++++ tests/browser_raw_sd.py | 2 ++ third_party/qemu | 2 +- web/app.js | 7 +++++ 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/docs/wasm-inflate-panic.md b/docs/wasm-inflate-panic.md index 3d8ea9a..1947a3a 100644 --- a/docs/wasm-inflate-panic.md +++ b/docs/wasm-inflate-panic.md @@ -742,3 +742,55 @@ remaining causes: stale/corrupted TLB addend for the store's page, wasm32 address truncation/sign-extension around addends, or an in-TB ordering bug in the compiled store TLB lookup. The next focused probe is to trace the store fast-path host address for the `0x4201e198` TB before the `i64.store32` executes. + + +## E32-E34: fault-path TB isolation + +Tried a narrow compiled-store guard (`?tlbguard=open`) that forces a store fast +path to the C helper if the computed host address lands inside the vCPU TLB +arrays. It did **not** suppress the crash, even though the guard range included +`fulltlb[56]`: + +```text +TLBGUARD range lo=0x13e8000 hi=0x353c000 +``` + +So the corrupting write is not caught as a normal compiled `qemu_st` fast-path +whose final host pointer falls directly inside that TLB-array range. The +corruption still appears between the TLB fill and the following byte load. + +Added `?stpc=LO-HI`, an opt-in PC-window override that interprets only +store-containing TBs whose start PC lands in the window, while `?nostoretci` +keeps every other store TB compiled. + +Results: + +- plain `?nostoretci`: still crashes at the original signature. +- `?nostoretci&stpc=0x4201e180-0x4201e220`: **3/3 pass** + (`open example.txt` about 46-52s). +- wider/narrowed windows around `uzlib_uncompress` also pass as long as they + include the fault-path TB start. + +This pins the corruption to compiled execution of the `uzlib_uncompress` TB that +contains the copy-loop sequence around: + +```text +4201e18c: lw a3,56(s0) +4201e18e: lw a5,24(s0) +4201e190: lw a4,52(s0) +4201e194: addi a2,a5,1 +4201e198: sw a2,24(s0) +4201e19a: add a3,a3,a4 +4201e19c: lbu a4,0(a3) +4201e1a0: sb a4,0(a5) +``` + +The PC-window override is firmware-specific and **not** a production fix, but it +is now the smallest positive isolation: one compiled store-containing TB in the +inflate copy loop is sufficient to corrupt the vCPU TLB entry; interpreting that +TB avoids the crash while preserving compiled stores elsewhere. + +Next useful step: dump or instrument the generated WASM for this exact TB and +inspect the `sw a2,24(s0)` / following `lbu` fast-path sequence. The current +node dump helper only captures early boot TBs, so late-reader TB dumping needs a +browser/open-window trigger or a PC-filtered `wasm_dump_module` path. diff --git a/tests/browser_raw_sd.py b/tests/browser_raw_sd.py index 3be1c18..c9ab8e7 100644 --- a/tests/browser_raw_sd.py +++ b/tests/browser_raw_sd.py @@ -171,6 +171,8 @@ try: driver.execute_script('window.xteinkEnableRamrace && window.xteinkEnableRamrace()') if 'stateflush=open' in url: driver.execute_script('window.xteinkEnableStateflush && window.xteinkEnableStateflush()') + if 'tlbguard=open' in url: + driver.execute_script('window.xteinkEnableTlbguard && window.xteinkEnableTlbguard()') open_start = time.monotonic() press(confirm, 'opening example.txt') open_seconds = time.monotonic() - open_start diff --git a/third_party/qemu b/third_party/qemu index cd3f083..e4a6de0 160000 --- a/third_party/qemu +++ b/third_party/qemu @@ -1 +1 @@ -Subproject commit cd3f083fb052a32bcde00f5d0f92d7cb2141a457 +Subproject commit e4a6de07c63b20116af697f4ac6ba678c9208626 diff --git a/web/app.js b/web/app.js index 0483695..3a88974 100644 --- a/web/app.js +++ b/web/app.js @@ -501,8 +501,15 @@ async function boot(file, variant, setStatus) { if (params.get('stateflush') === 'open') { window.xteinkEnableStateflush = () => options._wasm_stateflush_enable(); } else if (params.has('stateflush')) options._wasm_stateflush_enable(); + if (params.get('tlbguard') === 'open') { + window.xteinkEnableTlbguard = () => options._wasm_tlb_guard_enable(); + } else if (params.has('tlbguard')) options._wasm_tlb_guard_enable(); if (params.has('sthash')) options._wasm_set_store_tci_hash(Number(params.get('sthash')) || 1); if (params.has('stcmask')) options._wasm_set_store_bisect(Number(params.get('stcw')) || 0, Number(params.get('stcmask')) >>> 0, (Number(params.get('stcval')) || 0) >>> 0); + if (params.has('stpc')) { + const [lo, hi] = params.get('stpc').split('-').map(v => Number(v)); + options._wasm_set_store_pc_window(lo >>> 0, hi >>> 0); + } options.FS.mkdirTree('/bios'); options.FS.mkdirTree('/var/tmp'); options.FS.writeFile('/bios/esp32c3-rom.bin', rom);