Severity: high — use-after-free of the rep where the reference traps.
There is no host-side analogue of num_lends: lowering a host-held own
wrapper as borrow<R> records nothing (takeRep(w, false, ...),
embedder/resources.ts:113+), and the FinalizationRegistry backstop
(:54-63) checks only s.valid && s.owns.
The reference models host-held owns as table entries whose num_lends is
bumped on borrow-lift (definitions.py:890-892, 1516-1521), making drop trap
while lent (definitions.py:2325). wasmtime mirrors this (host_table +
lend tracking, runtime/vm/component/resources.rs).
Repro: host passes an own wrapper as borrow<R> into a guest call; the
wrapper is unreferenced after lowering; GC finalizes during the in-flight
call (trivially possible under a JSPI park) -> guest dtor runs while the
guest holds a live borrow of the same rep.
Independent second defect: the backstop's catch {} (:59-61) swallows a
trapping dtor with no instance poisoning — the reference propagates the
trap and leaves the impl instance unenterable. A half-destroyed guest
instance stays re-enterable.
Fix shape: WrapperState gains a lend counter — increment at borrow
lowering, decrement at the same points guest-side lender release happens
(subtask resolve / call return); dropWrapper and the backstop defer
while lent (mark pending-drop; execute on last release through the gated
dtor path of the dtor-gating issue); backstop dtor traps route into
instance poisoning + the host-failure channel instead of catch {}.
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: high — use-after-free of the rep where the reference traps.
There is no host-side analogue of
num_lends: lowering a host-held ownwrapper as
borrow<R>records nothing (takeRep(w, false, ...),embedder/resources.ts:113+), and the FinalizationRegistry backstop
(:54-63) checks only
s.valid && s.owns.The reference models host-held owns as table entries whose
num_lendsisbumped on borrow-lift (definitions.py:890-892, 1516-1521), making drop trap
while lent (definitions.py:2325). wasmtime mirrors this (
host_table+lend tracking, runtime/vm/component/resources.rs).
Repro: host passes an own wrapper as
borrow<R>into a guest call; thewrapper is unreferenced after lowering; GC finalizes during the in-flight
call (trivially possible under a JSPI park) -> guest dtor runs while the
guest holds a live borrow of the same rep.
Independent second defect: the backstop's
catch {}(:59-61) swallows atrapping dtor with no instance poisoning — the reference propagates the
trap and leaves the impl instance unenterable. A half-destroyed guest
instance stays re-enterable.
Fix shape:
WrapperStategains a lend counter — increment at borrowlowering, decrement at the same points guest-side lender release happens
(subtask resolve / call return);
dropWrapperand the backstop deferwhile lent (mark pending-drop; execute on last release through the gated
dtor path of the dtor-gating issue); backstop dtor traps route into
instance poisoning + the host-failure channel instead of
catch {}.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).