Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion scripts/build/deps/webkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// -lto variants built with ThinLTO (per-module summaries for cross-language
// importing), and the Windows ICU data table filtered + per-item zstd
// compressed (lazily decompressed via bun_icu_decompress.cpp).
export const WEBKIT_VERSION = "4895f45dfbd0d1226c4d41799887bc0ecb9f341b";
export const WEBKIT_VERSION = "autobuild-preview-pr-297-3946a08b";

/**
* WebKit (JavaScriptCore) — the JS engine.
Expand Down
45 changes: 45 additions & 0 deletions test/js/bun/jsc/parser-deep-nesting.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// JSC parser: once the native stack check fires on a deeply nested object/array
// literal, save-point backtracking (assignment-expression -> destructuring
// pattern -> member expression) used to clear the error and retry at every
// nesting level, turning an O(depth) failure into an exponential reparse loop
// that hung with unbounded memory growth. Now the first overflow is sticky and
// eval() rejects with RangeError in constant time regardless of depth.
import { expect, test } from "bun:test";
import { bunEnv, bunExe } from "harness";

for (const [name, open, close] of [
["object literal", "{a:", "}"],
["array literal", "[", "]"],
] as const) {
test(`deeply nested ${name} passed to eval() throws RangeError instead of hanging`, async () => {
// 50000 is far beyond the stack limit on every platform and build config;
// before the fix this hung forever (killed by the spawn timeout below).
const depth = 50000;
const fixture = `
const src = "const y = " + Buffer.alloc(${depth * open.length}, ${JSON.stringify(open)}).toString()
+ "1" + Buffer.alloc(${depth * close.length}, ${JSON.stringify(close)}).toString() + ";";
try {
eval(src);
console.log("no-throw");
} catch (e) {
console.log(e.constructor.name + ": " + e.message);
}
`;

await using proc = Bun.spawn({
cmd: [bunExe(), "-e", fixture],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
timeout: 20_000,
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);

expect({ stdout: stdout.trim(), stderr, exitCode, signalCode: proc.signalCode }).toEqual({
stdout: "RangeError: Maximum call stack size exceeded.",
stderr: "",
exitCode: 0,
signalCode: null,
});
}, 30_000);
}
Loading