Skip to content

resources: own-drop dtor bypasses may_enter gating and trap-poisoning; JS-initiated drops skip the promising entry #85

Description

@lann

Severity: high — missing spec-mandated trap; docs claim it implemented.

canonResourceDrop (cabi/handles.ts:167-171) calls the raw dtor core
export directly (in-code comment: "Reference: dtor invoked through
store.lift/store.lower so that may_enter gating applies... Deferred;
direct call."). The reference routes the dtor through the store lift path
(definitions.py:2326-2333 -> :579-584): trap_if(not inst.may_enter_from(caller)) with the same-instance exemption, may_enter
cleared during the call, and a dtor trap poisons the impl instance (no
leave on trap).

Guest-observable divergence: a guest can drop an own handle whose dtor
re-enters a currently-entered sibling instance — the reference traps
"cannot enter component instance"; deltic silently runs the dtor. A
trapping dtor also fails to poison the implementing instance (and must
trigger the #66 retireInstanceAsyncEnds walk via the existing poison
seam).

Second half: docs/architecture.md §7 says "JS-initiated drops (using,
FinalizationRegistry) enter via a promising trampoline" — but
dropWrapper (embedder/resources.ts:92-103) and the FinalizationRegistry
callback (:54-63) call s.rt.dtor directly. A dtor that legally calls a
Suspending host import (explicitly permitted by §7) dies with
SuspendError on these paths.

Fix shape:

  • implement the gate: same-instance exemption per the reference text;
    enter/leave bracket so a dtor trap poisons the impl instance;
  • JS-initiated drops: enter through a promising-wrapped path when the mode
    supports it; keep drop(): void non-blocking (track the in-flight
    promise via pendingHostCalls, surface async dtor failure through the
    host-failure channel + instance poisoning).

Explicitly OUT of scope here (document instead): the §5 "guest-initiated
cross-component dtor calls route through generated wasm" claim. Today the
dtor call is a JS frame inside the drop trampoline, so a guest-initiated
drop whose dtor suspends traps under the JSPI frame rule (deterministic,
loud). A pure-wasm dispatch path is real machinery; docs §5/§7 get a
known-limitation note until it exists.

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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions