Commit 57f4169
committed
fast-interp: unwind skipped EH entries on outer-catch dispatch
When a throw from a nested try is caught by an OUTER handler, the
walker previously left the inner-try entries between the throw site
and the matched outer entry on the eh-stack. The matched entry got
its `EH_TRY_CATCH_STATE_BIT` set, but `frame->eh_count` stayed
unchanged. After the outer catch body's END decremented eh_count by
one, the inner-try slot remained at the top of the eh-stack with
the matched outer entry now sitting *under* it (in-progress bit
set).
A subsequent throw inside (or after) the outer catch body would
walk that stale state. The walker SKIPs entries with the state bit
set, so the outer entry was correctly ignored — but the inner-try
entry (no state bit) was treated as live. If the inner try's typed
catch happened to match the new tag, the walker dispatched against
that stale entry — an out-of-scope catch.
Worse, in a tight loop of `outer try { inner try { throw }
catch_other catch_outer { ... } }`, every iteration leaked one
inner-try entry. After more iterations than the function's
`exception_handler_count`, the next TRY push wrote past the static
eh-stack reservation (silently in release builds since `bh_assert`
is a no-op without `BH_DEBUG`).
Fix: at each match-and-dispatch site in `find_a_catch_handler` —
both the typed-catch branch and the catch_all branch — set
`frame->eh_count = i;` before jumping to the handler. `i` is the
loop counter, which equals the index of the matched entry plus
one. This pops the nested-try entries above the match in a single
indexed store. The matched entry stays at index i-1 with its state
bit set; the catch body's END pops it normally when the body
completes.
Cost shape: one extra indexed store on the cold throw path, only
when a typed catch or catch_all matches. CALL / LOAD / STORE
handlers are untouched.
Test added in the external integration suite at
`crates/benchmark-core/tests/eh_correctness.rs::
outer_catch_unwinds_inner_eh_entries`. The test pattern is: outer
try catches `$err`; inner try has a catch for `$err2`. Inner throw
of `$err` is caught by outer. Outer catch body re-throws `$err2`,
which must propagate UNCAUGHT (inner try is out of scope). Pre-fix
walker found the stale inner catch and dispatched to it,
producing a Ok(99) instead of the trap; post-fix the walker has
no in-scope entries and the throw escapes correctly.
Codex P1 review feedback on rebeckerspecialties/wasm-micro-
runtime PR #2: "Unwind skipped EH entries before dispatching
catches".1 parent 04625e7 commit 57f4169
1 file changed
Lines changed: 25 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2036 | 2036 | | |
2037 | 2037 | | |
2038 | 2038 | | |
| 2039 | + | |
| 2040 | + | |
| 2041 | + | |
| 2042 | + | |
| 2043 | + | |
| 2044 | + | |
| 2045 | + | |
| 2046 | + | |
| 2047 | + | |
| 2048 | + | |
| 2049 | + | |
| 2050 | + | |
| 2051 | + | |
| 2052 | + | |
| 2053 | + | |
| 2054 | + | |
| 2055 | + | |
| 2056 | + | |
| 2057 | + | |
2039 | 2058 | | |
2040 | 2059 | | |
2041 | 2060 | | |
| |||
2048 | 2067 | | |
2049 | 2068 | | |
2050 | 2069 | | |
| 2070 | + | |
| 2071 | + | |
| 2072 | + | |
| 2073 | + | |
| 2074 | + | |
| 2075 | + | |
2051 | 2076 | | |
2052 | 2077 | | |
2053 | 2078 | | |
| |||
0 commit comments