Skip to content

Fix exception ref-count leak in non-ref catch handlers.#12860

Open
cfallin wants to merge 1 commit intobytecodealliance:mainfrom
cfallin:exception-leak
Open

Fix exception ref-count leak in non-ref catch handlers.#12860
cfallin wants to merge 1 commit intobytecodealliance:mainfrom
cfallin:exception-leak

Conversation

@cfallin
Copy link
Copy Markdown
Member

@cfallin cfallin commented Mar 27, 2026

When a Wasm throw instruction executes, the throw_ref libcall was cloning the GC ref (incrementing the refcount), but the catch handler never decremented it. This caused every caught exception to leak, leading to unbounded GC heap growth in throw/catch loops.

Two fixes:

  1. Remove the unnecessary clone_gc_ref() in throw_ref. The throw/throw_ref instructions consume the exnref operand, so ownership transfers naturally to pending_exception without cloning.

  2. In create_catch_block, emit a drop_gc_ref call for non-ref catches (Catch::One, Catch::All) after field extraction. These catches consume the exnref without passing it to the branch target, so the refcount must be decremented.

Also adds Store::gc_heap_size() / StoreContext::gc_heap_size() accessors and a throw_catch_many_times integration test that throws and catches 100K exceptions in a loop, asserting the GC heap stays within a single 64 KiB page.

@cfallin cfallin requested review from a team as code owners March 27, 2026 23:17
@cfallin cfallin requested review from fitzgen and removed request for a team March 27, 2026 23:17
@github-actions github-actions bot added the wasmtime:api Related to the API of the `wasmtime` crate itself label Mar 28, 2026
When a Wasm `throw` instruction executes, the `throw_ref` libcall was
cloning the GC ref (incrementing the refcount), but the catch handler
never decremented it. This caused every caught exception to leak,
leading to unbounded GC heap growth in throw/catch loops.

Two fixes:

1. Remove the unnecessary `clone_gc_ref()` in `throw_ref`. The
   `throw`/`throw_ref` instructions consume the exnref operand, so
   ownership transfers naturally to `pending_exception` without
   cloning.

2. In `create_catch_block`, emit a `drop_gc_ref` call for non-ref
   catches (`Catch::One`, `Catch::All`) after field extraction. These
   catches consume the exnref without passing it to the branch target,
   so the refcount must be decremented.

Also adds `Store::gc_heap_size()` / `StoreContext::gc_heap_size()`
accessors and a `throw_catch_many_times` integration test that throws
and catches 100K exceptions in a loop, asserting the GC heap stays
within a single 64 KiB page.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

wasmtime:api Related to the API of the `wasmtime` crate itself

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant