Skip to content

BridgeJS: Guard FinalizationRegistry deinit callback against Wasm teardown#662

Draft
krodak wants to merge 1 commit intoswiftwasm:mainfrom
PassiveLogic:kr/fix-finalization-crash
Draft

BridgeJS: Guard FinalizationRegistry deinit callback against Wasm teardown#662
krodak wants to merge 1 commit intoswiftwasm:mainfrom
PassiveLogic:kr/fix-finalization-crash

Conversation

@krodak
Copy link
Member

@krodak krodak commented Feb 19, 2026

Overview

Fixes #661.

The shared swiftHeapObjectFinalizationRegistry (added in #648) crashes intermittently in CI when its callback fires during Node.js process shutdown. V8 GC collects leftover SwiftHeapObject wrappers, the finalizer calls state.deinit(state.pointer) (a Wasm export), but by that point the Wasm linear memory or function table is already torn down. Result: RuntimeError: memory access out of bounds or table index is out of bounds, roughly 15% of CI runs.

The hasReleased flag prevents double-release but doesn't help here - the object hasn't been released, the Wasm instance is just gone.

Fix

Wrap state.deinit(state.pointer) in try/catch in both the FinalizationRegistry callback and the release() method. If Wasm is already torn down, we swallow the error. The process is exiting anyway.

An instanceAlive flag was considered but requires hooking into teardown lifecycle for no real benefit over a simple try/catch. The finalizer is best-effort cleanup by design.

Test

testReleaseHandlesThrowingDeinit tests the release() path: JS replaces the wrapper's deinit to throw WebAssembly.RuntimeError after calling the real one, then calls release(). Without the try/catch guard, the error propagates and crashes the process.

The FinalizationRegistry callback path isn't tested directly because the import-side wrapper uses passUnretained (no extra retain), so triggering GC on it would over-release and corrupt the Swift heap - a test artifact, not a production scenario. The template change for both paths is identical (try { state.deinit(state.pointer); } catch {}), so the release() test covers the logic.

@krodak krodak marked this pull request as draft February 19, 2026 10:20
@krodak krodak force-pushed the kr/fix-finalization-crash branch 3 times, most recently from b88e3f9 to 539b8f3 Compare February 19, 2026 12:10
@krodak krodak force-pushed the kr/fix-finalization-crash branch from 539b8f3 to f81bdca Compare February 19, 2026 12:28
@kateinoigakukun
Copy link
Member

by that point the Wasm linear memory or function table is already torn down.

Hmm, is it true? I still think we have double free issues. Let me take a closer look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flaky CI: FinalizationRegistry callback crashes during process shutdown

2 participants

Comments