-
Notifications
You must be signed in to change notification settings - Fork 20
Description
Morning,
I found an issue where WasmKit enters an infinite loop when executing a WebAssembly module that has been transformed with Binaryen's Asyncify optimization. The same WASM module executes successfully in other runtimes (Bun/V8, Node.js) even without wrapping exports/imports with asyncify instrumentation.
But for the sake of verifying I also rolled a novel Asyncify wrapper for WasmKit, but the issue persist. Maybe an issue with indirect calls?
Reproduction
Repository: https://github.com/andrewmd5/WasmKitRepro
To reproduce the bug:
swift testTo see the same WASM module working correctly:
cd js && bun index.tsExpected Behavior
The module should complete execution and return, as it does in Bun/V8.
Actual Behavior
WasmKit enters an infinite loop during the asyncify unwind/rewind cycle.
Debug Output (WasmKit - Infinite Loop)
setjmp.c:89: enter _asyncjmp_setjmp_internal
setjmp.c:94: JMP_BUF_STATE_INITIALIZED
setjmp.c:218: enter asyncjmp_handle_jmp_unwind
setjmp.c:227: JMP_BUF_STATE_CAPTURING
setjmp.c:89: enter _asyncjmp_setjmp_internal
setjmp.c:106: JMP_BUF_STATE_CAPTURING
Attempt to free unreferenced scalar: SV 0x1.
Attempt to free unreferenced scalar: SV 0x1.
[repeats indefinitely]
Debug Output (Bun - Works Correctly)
setjmp.c:89: enter _asyncjmp_setjmp_internal
setjmp.c:94: JMP_BUF_STATE_INITIALIZED
setjmp.c:218: enter asyncjmp_handle_jmp_unwind
setjmp.c:227: JMP_BUF_STATE_CAPTURING
setjmp.c:89: enter _asyncjmp_setjmp_internal
setjmp.c:106: JMP_BUF_STATE_CAPTURING
setjmp.c:89: enter _asyncjmp_setjmp_internal
setjmp.c:94: JMP_BUF_STATE_INITIALIZED
setjmp.c:218: enter asyncjmp_handle_jmp_unwind
setjmp.c:227: JMP_BUF_STATE_CAPTURING
setjmp.c:89: enter _asyncjmp_setjmp_internal
setjmp.c:106: JMP_BUF_STATE_CAPTURING
setjmp.c:128: enter _asyncjmp_longjmp
setjmp.c:218: enter asyncjmp_handle_jmp_unwind
setjmp.c:233: JMP_BUF_STATE_RETURNING
setjmp.c:89: enter _asyncjmp_setjmp_internal
setjmp.c:114: JMP_BUF_STATE_RETURNING
[exits successfully]
Analysis
The issue occurs after asyncify_start_unwind() is called and the control flow should rewind back into the function. WasmKit appears to be re-entering the function from the beginning instead of resuming from the saved rewind point, causing the state machine to loop in JMP_BUF_STATE_CAPTURING.
Note: The "Attempt to free unreferenced scalar" error messages are likely a symptom rather than the root cause. These appear to be caused by WasmKit's incorrect control flow causing the Perl runtime to execute WASI fd_write calls outside of the expected asyncify runtime loop context, resulting in corrupted state.
Additional Context
This module implements the same setjmp/longjmp mechanism from ruby.wasm using Binaryen's Asyncify transformation (implementation: https://github.com/6over3/zeroperl/tree/main/stubs).