diff --git a/docs/wasm-inflate-panic.md b/docs/wasm-inflate-panic.md index 4b4e967..a2302f4 100644 --- a/docs/wasm-inflate-panic.md +++ b/docs/wasm-inflate-panic.md @@ -501,3 +501,39 @@ consistent with a torn/stale store of a pointer-sized value under a race. `?sthash` and `?stcw/stcmask/stcval` are opt-in debug tools; the default build path is unchanged (blanket store-TCI). The narrowed 32-bit-only fallback remains rejected as a default because it only lowers, not removes, the race probability. + +## E17: not the store block-replay/redispatch path + +Added opt-in `?yieldtrace`: the dispatch loop counts compiled-TB "yield/resume" +re-entries, defined as a compiled TB returning non-zero with the *same* `tb_ptr` +and `do_init == 0` (the `DONE_FLAG`-driven redispatch/resume path; chains set +`do_init = 1`, completion sets `tb_ptr = 0`). + +Result over three crashing `?nostoretci` runs: **all crashed at `0x4201e210`, +zero yield re-entries**. So the store-specific block-replay-via-redispatch +mechanism (upstream's stated reason for blanket store-TCI) does not fire during +this workload. That path is not the trigger here. + +Caveat: Asyncify-based unwinds (coroutine yields) restore the whole C stack and +are transparent to this counter, so a coroutine-mediated replay is not excluded. +But the store `DONE_FLAG` redispatch path is definitively ruled out. + +### Consolidated conclusion +The `0x4201e210` corruption is: +- nondeterministic (same config crashes/passes across runs), +- independent of which specific store TBs are compiled, +- gated only by having enough compiled stores of both widths (a timing knob), +- not the store `DONE_FLAG` redispatch/replay path. + +The consistent remaining explanation is a **data race on guest RAM** between the +CPU worker (compiled `i64.storeN`) and another agent that runs concurrently — +main-thread `QEMUTimer`/device-model callbacks (SPI/SD/GDMA/cache) reading or +writing guest DRAM. TCI's C store path masks it by changing timing/serialization, +not by being semantically different. This matches upstream qemu-wasm shipping +blanket store-TCI rather than a targeted fix. + +A real fix is a threading-correctness project: ensure guest-RAM accesses from +device/timer callbacks are properly serialized against vCPU stores (BQL/memory +barriers), or run device memory access on the vCPU thread. Opt-in repro/debug +controls now available: `?nostoretci`, `?sthash`, `?stcw/stcmask/stcval`, +`?yieldtrace`. Production default remains blanket store-TCI. diff --git a/third_party/qemu b/third_party/qemu index a18a299..9e91849 160000 --- a/third_party/qemu +++ b/third_party/qemu @@ -1 +1 @@ -Subproject commit a18a2991c681fb967b096b895c51f0eff4da1ddc +Subproject commit 9e9184949d44669a52307e28641c8a6af3c7cbe5 diff --git a/web/app.js b/web/app.js index 566c297..fb42ab9 100644 --- a/web/app.js +++ b/web/app.js @@ -493,6 +493,7 @@ async function boot(file, variant, setStatus) { } 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(); + if (params.has('yieldtrace')) options._wasm_enable_yield_trace(); 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); options.FS.mkdirTree('/bios');