Skip to content

qjs panics on web-tooling dist/cli.js (wasm out of bounds) #43

Description

@buke

Hi, I am hitting a reproducible crash when running the QuickJS web-tooling benchmark bundle through qjs.

Environment

qjs: v0.0.6
wazero: v1.9.0
Go: go1.26.1 darwin/arm64
OS: macOS (Darwin Kernel 25.3.0, arm64)

What I run

Use cli.js from https://github.com/quickjs-ng/web-tooling-benchmark

I evaluate quickjs-ng/web-tooling-benchmark dist/cli.js through qjs.Context().Eval, with a small prelude that defines print and console.log and then returns JSON.stringify of captured output.

Minimal reproducer

Save as repro-qjs-webtooling.go and run go run repro-qjs-webtooling.go.

package main

import (
    "fmt"
    "os"

    qjs "github.com/fastschema/qjs"
)

func main() {
    content, err := os.ReadFile("cli.js")
    if err != nil {
        panic(err)
    }

    prelude := "var __bench_output = [];\n" +
        "function print(value) { __bench_output.push(String(value)); }\n" +
        "var console = {\n" +
        "  log: function(value) { __bench_output.push(String(value)); },\n" +
        "  error: function(value) { __bench_output.push(String(value)); }\n" +
        "};\n"

    script := prelude + string(content) + "\nJSON.stringify(__bench_output);"

    rt, err := qjs.New(qjs.Option{
        MemoryLimit:  1536 * 1024 * 1024,
        MaxStackSize: 8 * 1024 * 1024,
        GCThreshold:  128 * 1024 * 1024,
    })
    if err != nil {
        panic(err)
    }
    defer rt.Close()

    v, err := rt.Context().Eval("web-tooling.js", qjs.Code(script))
    if err != nil {
        panic(err)
    }
    defer v.Free()

    fmt.Println("ok", len(v.String()))
}

and the output :

panic: failed to call QJS_Eval: wasm error: out of bounds memory access
        wasm stack trace:
                .$104(i32)
                .$610(i32,i32)
                .$346(i32,i32)
                .$308(i32,i32,i32,i32) i32
                .$414(i32,i64,i32,i64,i64,i32) i32
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                .$266(i32,i64,i64,i64,i32,i32,i32) i64
                ... maybe followed by omitted frames
        stack: goroutine 1 [running]:
        runtime/debug.Stack()
                /opt/homebrew/Cellar/go/1.26.1/libexec/src/runtime/debug/stack.go:26 +0x64
        github.com/fastschema/qjs.(*Runtime).call(0x6d8f96392000, {0x104d89e93, 0x8}, {0x6d8f90a920f0, 0x2, 0x2})
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:320 +0xa0
        github.com/fastschema/qjs.(*Runtime).Call(0x6d8f96392000, {0x104d89e93?, 0x0?}, {0x6d8f90a920f0?, 0x60000000?, 0x800000?})
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:240 +0x28
        github.com/fastschema/qjs.(*Context).Call(...)
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/context.go:21
        github.com/fastschema/qjs.eval(0x6d8f96394000, {0x104d8c55c, 0xe}, {0x6d8f950f1e38, 0x1, 0x104d94261?})
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/eval.go:31 +0x170
        github.com/fastschema/qjs.(*Context).Eval(...)
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/context.go:90
        main.main()
                /tmp/repro-qjs-webtooling.go:35 +0x120

        panic: failed to call QJS_Free: wasm error: unreachable
        wasm stack trace:
                .$273(i32)
                .$272(i32)
                .$92(i32)
        stack: goroutine 1 [running]:
        runtime/debug.Stack()
                /opt/homebrew/Cellar/go/1.26.1/libexec/src/runtime/debug/stack.go:26 +0x64
        github.com/fastschema/qjs.(*Runtime).call(0x6d8f96392000, {0x104d89e9b, 0x8}, {0x6d8f90e0c380, 0x1, 0x1})
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:320 +0xa0
        github.com/fastschema/qjs.(*Runtime).Call(0x6d8f96392000, {0x104d89e9b?, 0x6d8f90c93ae8?}, {0x6d8f90e0c380?, 0x6d8f90c93b28?, 0x104bd1278?})
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:240 +0x28
        github.com/fastschema/qjs.(*Runtime).FreeQJSRuntime(0x6d8f96392000)
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:171 +0x74
        github.com/fastschema/qjs.(*Runtime).Close(0x6d8f96392000)
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:192 +0x2c
        panic({0x104fba3a0?, 0x6d8f90ab5cc0?})
                /opt/homebrew/Cellar/go/1.26.1/libexec/src/runtime/panic.go:860 +0x12c
        github.com/fastschema/qjs.(*Runtime).call(0x6d8f96392000, {0x104d89e93, 0x8}, {0x6d8f90a920f0, 0x2, 0x2})
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:321 +0x130
        github.com/fastschema/qjs.(*Runtime).Call(0x6d8f96392000, {0x104d89e93?, 0x0?}, {0x6d8f90a920f0?, 0x60000000?, 0x800000?})
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:240 +0x28
        github.com/fastschema/qjs.(*Context).Call(...)
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/context.go:21
        github.com/fastschema/qjs.eval(0x6d8f96394000, {0x104d8c55c, 0xe}, {0x6d8f950f1e38, 0x1, 0x104d94261?})
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/eval.go:31 +0x170
        github.com/fastschema/qjs.(*Context).Eval(...)
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/context.go:90
        main.main()
                /tmp/repro-qjs-webtooling.go:35 +0x120
         [recovered]
        panic: failed to free QJS runtime: failed to call QJS_Free: wasm error: unreachable
        wasm stack trace:
                .$273(i32)
                .$272(i32)
                .$92(i32)
        stack: goroutine 1 [running]:
        runtime/debug.Stack()
                /opt/homebrew/Cellar/go/1.26.1/libexec/src/runtime/debug/stack.go:26 +0x64
        github.com/fastschema/qjs.(*Runtime).call(0x6d8f96392000, {0x104d89e9b, 0x8}, {0x6d8f90e0c380, 0x1, 0x1})
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:320 +0xa0
        github.com/fastschema/qjs.(*Runtime).Call(0x6d8f96392000, {0x104d89e9b?, 0x6d8f90c93ae8?}, {0x6d8f90e0c380?, 0x6d8f90c93b28?, 0x104bd1278?})
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:240 +0x28
        github.com/fastschema/qjs.(*Runtime).FreeQJSRuntime(0x6d8f96392000)
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:171 +0x74
        github.com/fastschema/qjs.(*Runtime).Close(0x6d8f96392000)
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:192 +0x2c
        panic({0x104fba3a0?, 0x6d8f90ab5cc0?})
                /opt/homebrew/Cellar/go/1.26.1/libexec/src/runtime/panic.go:860 +0x12c
        github.com/fastschema/qjs.(*Runtime).call(0x6d8f96392000, {0x104d89e93, 0x8}, {0x6d8f90a920f0, 0x2, 0x2})
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:321 +0x130
        github.com/fastschema/qjs.(*Runtime).Call(0x6d8f96392000, {0x104d89e93?, 0x0?}, {0x6d8f90a920f0?, 0x60000000?, 0x800000?})
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:240 +0x28
        github.com/fastschema/qjs.(*Context).Call(...)
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/context.go:21
        github.com/fastschema/qjs.eval(0x6d8f96394000, {0x104d8c55c, 0xe}, {0x6d8f950f1e38, 0x1, 0x104d94261?})
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/eval.go:31 +0x170
        github.com/fastschema/qjs.(*Context).Eval(...)
                /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/context.go:90
        main.main()
                /tmp/repro-qjs-webtooling.go:35 +0x120


goroutine 1 [running]:
github.com/fastschema/qjs.(*Runtime).FreeQJSRuntime.func1()
        /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:167 +0x6c
panic({0x104fba3a0?, 0x6d8f90e263e0?})
        /opt/homebrew/Cellar/go/1.26.1/libexec/src/runtime/panic.go:860 +0x12c
github.com/fastschema/qjs.(*Runtime).call(0x6d8f96392000, {0x104d89e9b, 0x8}, {0x6d8f90e0c380, 0x1, 0x1})
        /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:321 +0x130
github.com/fastschema/qjs.(*Runtime).Call(0x6d8f96392000, {0x104d89e9b?, 0x6d8f90c93ae8?}, {0x6d8f90e0c380?, 0x6d8f90c93b28?, 0x104bd1278?})
        /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:240 +0x28
github.com/fastschema/qjs.(*Runtime).FreeQJSRuntime(0x6d8f96392000)
        /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:171 +0x74
github.com/fastschema/qjs.(*Runtime).Close(0x6d8f96392000)
        /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:192 +0x2c
panic({0x104fba3a0?, 0x6d8f90ab5cc0?})
        /opt/homebrew/Cellar/go/1.26.1/libexec/src/runtime/panic.go:860 +0x12c
github.com/fastschema/qjs.(*Runtime).call(0x6d8f96392000, {0x104d89e93, 0x8}, {0x6d8f90a920f0, 0x2, 0x2})
        /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:321 +0x130
github.com/fastschema/qjs.(*Runtime).Call(0x6d8f96392000, {0x104d89e93?, 0x0?}, {0x6d8f90a920f0?, 0x60000000?, 0x800000?})
        /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/runtime.go:240 +0x28
github.com/fastschema/qjs.(*Context).Call(...)
        /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/context.go:21
github.com/fastschema/qjs.eval(0x6d8f96394000, {0x104d8c55c, 0xe}, {0x6d8f950f1e38, 0x1, 0x104d94261?})
        /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/eval.go:31 +0x170
github.com/fastschema/qjs.(*Context).Eval(...)
        /Users/wangbuke/go/pkg/mod/github.com/fastschema/qjs@v0.0.6/context.go:90
main.main()
        /tmp/repro-qjs-webtooling.go:35 +0x120
exit status 2

Notes

I already tried increasing qjs runtime limits via qjs.Option:
MemoryLimit = 1536MB
MaxStackSize = 8MB
GCThreshold = 128MB
The crash still reproduces.

Thanks.

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