mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
fix(wasm): disable Wasm OSR on Linux x64 to prevent crashes
Disables useWasmOSR on Linux x64 to work around a JavaScriptCore bug that causes crashes (SIGILL/segfault) when calling Emscripten-exported Wasm functions via direct method calls after many iterations. The crash occurs specifically with direct method call patterns like `module._func(arg)` vs working alternatives like `const fn = module._func; fn(arg)`. This workaround trades some Wasm performance (no on-stack replacement from interpreter to JIT) for stability. Wasm code still gets JIT-compiled after enough executions via the normal tiering mechanism. Fixes #26366 Fixes #26444 Fixes #17841 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -293,6 +293,14 @@ extern "C" void JSCInitialize(const char* envp[], size_t envc, void (*onCrash)(c
|
||||
JSC::Options::useJIT() = true;
|
||||
JSC::Options::useBBQJIT() = true;
|
||||
JSC::Options::useConcurrentJIT() = true;
|
||||
|
||||
#if OS(LINUX) && CPU(X86_64)
|
||||
// Workaround for JSC Wasm OSR bug on Linux x64 that causes crashes
|
||||
// with repeated direct method calls on Emscripten WASM modules.
|
||||
// https://github.com/oven-sh/bun/issues/26366
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=289009
|
||||
JSC::Options::useWasmOSR() = false;
|
||||
#endif
|
||||
// JSC::Options::useSigillCrashAnalyzer() = true;
|
||||
JSC::Options::useSourceProviderCache() = true;
|
||||
// JSC::Options::useUnlinkedCodeBlockJettisoning() = false;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { PGlite } from "@electric-sql/pglite";
|
||||
import { isCI, isLinux } from "harness";
|
||||
|
||||
describe("pglite", () => {
|
||||
// TODO(@190n) linux-x64 sometimes fails due to JavaScriptCore bug
|
||||
// https://github.com/oven-sh/bun/issues/17841#issuecomment-2695792567
|
||||
// This test previously failed on linux-x64 due to a JavaScriptCore Wasm OSR bug.
|
||||
// Fixed by disabling useWasmOSR on linux-x64 in ZigGlobalObject.cpp.
|
||||
// https://github.com/oven-sh/bun/issues/17841
|
||||
// https://github.com/oven-sh/bun/issues/26366
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=289009
|
||||
it.todoIf(isCI && isLinux && process.arch == "x64")("can initialize successfully", async () => {
|
||||
it("can initialize successfully", async () => {
|
||||
const db = new PGlite();
|
||||
expect(await db.query("SELECT version()")).toEqual({
|
||||
rows: [
|
||||
|
||||
64
test/regression/issue/26366.test.ts
Normal file
64
test/regression/issue/26366.test.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { bunEnv, bunExe } from "harness";
|
||||
|
||||
// Regression test for https://github.com/oven-sh/bun/issues/26366
|
||||
// Bun crashes with SIGILL/segfault when calling an Emscripten-exported Wasm function
|
||||
// via direct method call on the module object after many iterations.
|
||||
// This is caused by a JSC Wasm OSR/JIT bug, fixed by disabling useWasmOSR on Linux x64.
|
||||
|
||||
describe("issue #26366: JSC Wasm OSR crash with direct method calls", () => {
|
||||
// The original bug only manifested on Linux x64, but this test validates
|
||||
// the Wasm instantiation and direct method call pattern works on all platforms.
|
||||
test("repeated wasm module instantiation with direct method calls", async () => {
|
||||
// Create a minimal test that exercises the code path that would crash:
|
||||
// - Multiple Wasm module instantiations
|
||||
// - Direct method calls on the module object (module.export())
|
||||
const script = `
|
||||
// Minimal Wasm module: exports a function that returns its argument + 1
|
||||
// Generated with: (module (func (export "add1") (param i32) (result i32) local.get 0 i32.const 1 i32.add))
|
||||
const wasmBytes = new Uint8Array([
|
||||
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, // magic + version
|
||||
0x01, 0x06, 0x01, 0x60, 0x01, 0x7f, 0x01, 0x7f, // type section: (i32) -> i32
|
||||
0x03, 0x02, 0x01, 0x00, // function section
|
||||
0x07, 0x08, 0x01, 0x04, 0x61, 0x64, 0x64, 0x31, 0x00, 0x00, // export "add1"
|
||||
0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x0b, // code
|
||||
]);
|
||||
|
||||
// Use higher iterations on Linux x64 where the original crash occurred.
|
||||
// Allow override via env var for CI stress testing.
|
||||
const baseIterations = process.platform === 'linux' && process.arch === 'x64' ? 4000 : 500;
|
||||
const iterations = parseInt(process.env.TEST_ITERATIONS, 10) || baseIterations;
|
||||
|
||||
async function runTest() {
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
const wasmModule = await WebAssembly.compile(wasmBytes);
|
||||
const instance = await WebAssembly.instantiate(wasmModule);
|
||||
|
||||
// Direct method call - this was the pattern that triggered the crash
|
||||
const result = instance.exports.add1(i);
|
||||
|
||||
if (result !== i + 1) {
|
||||
throw new Error(\`Unexpected result at iteration \${i}: expected \${i + 1}, got \${result}\`);
|
||||
}
|
||||
}
|
||||
console.log(\`Completed \${iterations} iterations successfully\`);
|
||||
}
|
||||
|
||||
await runTest();
|
||||
`;
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "-e", script],
|
||||
env: bunEnv,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(stdout).toMatch(/Completed \d+ iterations successfully/);
|
||||
expect(stderr).toBe("");
|
||||
// The key assertion: process should exit normally without crash (SIGILL/SIGSEGV)
|
||||
expect(exitCode).toBe(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user