Severity: medium-low — violates intrinsics.md v0.2 amendment 2's posture.
createSyncStartCall releases lenderScope only on the success path
(intrinsics/fact_calls.ts:692, :702); the trap rethrow (:639-652) and the
needsJspi bail (:697-700) leave every lent handle's numLends elevated
forever. createAsyncStartCall: lenders release only via
deliverResolve; a trap during thread.resume() (:816-829) leaves the
subtask unresolved forever.
intrinsics.md v0.2 amendment 2 names the obligation: on a trap escaping a
FACT bracket the host must unwind sync-call scopes (releasing lenders)
because this runtime deliberately supports post-trap re-entry on the
caller side (the reference kills the whole store instead). Consequence
today: after a callee trap through [sync|async]-start, every later
lift_own/resource.drop of the lent handles traps "handle still lent
out". The NeedsJspi variant is worse in kind: it is expressly a
non-poisoning capability signal yet strands lenders.
Also fold in: mkCalleeTask's sync post-return leg (:507-513) has no
try/finally; a trapping post-return may strand mayLeave === false unless
the executor-side unwind covers that site — verify and close.
Fix: try/finally the start-call bodies (and the post-return bracket) so
lender release runs on every non-success exit that does not poison the
caller; add trap-path regression tests.
Found in the 2026-08-10 adversarial conformance review (runtime + translator vs component-model @ 73b7ad5 definitions.py; wasmtime-environ 47.0.3 treated as trusted).
Severity: medium-low — violates intrinsics.md v0.2 amendment 2's posture.
createSyncStartCallreleaseslenderScopeonly on the success path(intrinsics/fact_calls.ts:692, :702); the trap rethrow (:639-652) and the
needsJspibail (:697-700) leave every lent handle'snumLendselevatedforever.
createAsyncStartCall: lenders release only viadeliverResolve; a trap duringthread.resume()(:816-829) leaves thesubtask unresolved forever.
intrinsics.md v0.2 amendment 2 names the obligation: on a trap escaping a
FACT bracket the host must unwind sync-call scopes (releasing lenders)
because this runtime deliberately supports post-trap re-entry on the
caller side (the reference kills the whole store instead). Consequence
today: after a callee trap through
[sync|async]-start, every laterlift_own/resource.dropof the lent handles traps "handle still lentout". The NeedsJspi variant is worse in kind: it is expressly a
non-poisoning capability signal yet strands lenders.
Also fold in:
mkCalleeTask's sync post-return leg (:507-513) has notry/finally; a trapping post-return may strand
mayLeave === falseunlessthe executor-side unwind covers that site — verify and close.
Fix: try/finally the start-call bodies (and the post-return bracket) so
lender release runs on every non-success exit that does not poison the
caller; add trap-path regression tests.
Found in the 2026-08-10 adversarial conformance review (runtime + translator vs component-model @ 73b7ad5 definitions.py; wasmtime-environ 47.0.3 treated as trusted).