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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions