Skip to content

[BUG] Infinite Loop with Asyncify-Transformed WebAssembly Module #229

@andrewmd5

Description

@andrewmd5

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 test

To see the same WASM module working correctly:

cd js && bun index.ts

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

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