docs(wasm): E9 static analysis - found UNWINDING/DONE_FLAG bug; reframe root cause to register-value codegen
This commit is contained in:
@@ -204,6 +204,41 @@ remaining paths are generated-wasm inspection of the BLOCK_PTR/register restart
|
||||
sequence, or building per-op TCI. A deterministic reproducer exists (remove
|
||||
`wasm_tci_only_tb`, open example.txt, faults at MEPC 0x4201e210, gen 16).
|
||||
|
||||
### E9 — static analysis of generated wasm (dump + wasm2wat)
|
||||
Added a node-only dump hook (`wasm_dump_module`, tcg.c finalize) that writes the
|
||||
first compiled store-TB and load-TB modules; `_scratch/dump-tbs.mjs` boots the
|
||||
firmware under node far enough to emit them. `wasm2wat --enable-threads` gives
|
||||
readable IR.
|
||||
|
||||
**Finding 1 (real bug, fixed-then-shelved):** the ld/st slow paths detect an
|
||||
asyncify yield with the thread-global `DONE_FLAG` (ctx+20), while the general
|
||||
call path uses `UNWINDING` (ctx+28), which is reset before each call and set
|
||||
*only* by the coroutine switch. `DONE_FLAG` is set by *any* nested ld/st helper,
|
||||
so a nested guest access inside a store/load helper that then yields makes the
|
||||
outer op falsely read "completed" and swallow the unwind. Converting ld/st to
|
||||
the `UNWINDING` mechanism is the correct fix. **But** rebuilding with compiled
|
||||
stores + the UNWINDING fix still crashes identically (gen 16, same MEPC), so the
|
||||
store helpers on this path never actually yield — this bug is latent here. Change
|
||||
shelved (not shipped) to avoid touching the working compiled-load path without a
|
||||
green test; recorded for a future upstream fix.
|
||||
|
||||
**Finding 2 (reframes the root cause):** combine E5 with the load result. E5
|
||||
routes *every* store through the C softmmu helper (correct memory write) and
|
||||
still crashes; loads share the identical restart/block structure and work. So
|
||||
the corruption is **not** in the store data codegen, the restart machinery, or
|
||||
the yield handling. What remains is that a store *writes* guest memory using an
|
||||
address/value taken from guest registers (wasm globals). A compiled TB that
|
||||
computes a wrong register value corrupts memory only through a store — a wrong
|
||||
address fed to a *load* just reads harmless garbage. This matches every prior
|
||||
result: emergent (depends which ops precede a store), deterministic, and
|
||||
non-localizable by store region or width. The remaining hunt is a
|
||||
register-producing op whose compiled codegen diverges from TCI, exposed
|
||||
destructively via stores — which needs a compiled-vs-TCI per-TB register
|
||||
differential, not more whole-TB gating.
|
||||
|
||||
Tooling retained: `wasm_dump_module` + `wasm_tb_had_store/load` tags +
|
||||
`_scratch/dump-tbs.mjs`.
|
||||
|
||||
### Next options
|
||||
1. **Ship E1 as a guarded workaround** — reader works now; measure real
|
||||
navigation/page-turn perf before judging it unacceptable.
|
||||
|
||||
Reference in New Issue
Block a user