diff --git a/.vscode/launch.json b/.vscode/launch.json index 4a7052e99b29b3..06fd6e26e21613 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -145,7 +145,7 @@ "request": "launch", "name": "bun run [file]", "program": "${workspaceFolder}/build/bun-debug", - "args": ["run", "${file}"], + "args": ["run", "${fileBasename}"], "cwd": "${fileDirname}", "env": { "FORCE_COLOR": "0", diff --git a/src/Global.zig b/src/Global.zig index 16882e0cae54b1..f572cdba1232f8 100644 --- a/src/Global.zig +++ b/src/Global.zig @@ -119,27 +119,12 @@ pub fn exit(code: u32) noreturn { bun.C.quick_exit(@bitCast(code)); } -pub fn raiseIgnoringPanicHandler(sig: anytype) noreturn { - if (comptime @TypeOf(sig) == bun.SignalCode) { - return raiseIgnoringPanicHandler(@intFromEnum(sig)); - } - +pub fn raiseIgnoringPanicHandler(sig: bun.SignalCode) noreturn { Output.flush(); - - if (!Environment.isWindows) { - if (sig >= 1 and sig != std.posix.SIG.STOP and sig != std.posix.SIG.KILL) { - const act = std.posix.Sigaction{ - .handler = .{ .sigaction = @ptrCast(@alignCast(std.posix.SIG.DFL)) }, - .mask = std.posix.empty_sigset, - .flags = 0, - }; - std.posix.sigaction(@intCast(sig), &act, null) catch {}; - } - } - Output.Source.Stdio.restore(); - _ = std.c.raise(sig); + bun.crash_handler.resetSegfaultHandler(); + _ = std.c.raise(@intFromEnum(sig)); std.c.abort(); } diff --git a/src/bun.js/WebKit b/src/bun.js/WebKit index 49018961cccf8c..49907bff878171 160000 --- a/src/bun.js/WebKit +++ b/src/bun.js/WebKit @@ -1 +1 @@ -Subproject commit 49018961cccf8cdcb3fd98e75a8a2226a295ed3c +Subproject commit 49907bff8781719bc2ded068b0c934f6d0074d1e diff --git a/src/cli/run_command.zig b/src/cli/run_command.zig index e735b5ff0a6636..5e8abbe276e2a7 100644 --- a/src/cli/run_command.zig +++ b/src/cli/run_command.zig @@ -578,8 +578,7 @@ pub const RunCommand = struct { }); } - Output.flush(); - Global.raiseIgnoringPanicHandler(@intFromEnum(signal)); + Global.raiseIgnoringPanicHandler(signal); }, .exited => |exit_code| { @@ -592,8 +591,7 @@ pub const RunCommand = struct { }); } - Output.flush(); - Global.raiseIgnoringPanicHandler(@intFromEnum(exit_code.signal)); + Global.raiseIgnoringPanicHandler(exit_code.signal); } const code = exit_code.code; diff --git a/src/crash_handler.zig b/src/crash_handler.zig index 487688fc68429d..83768e34b3cff3 100644 --- a/src/crash_handler.zig +++ b/src/crash_handler.zig @@ -208,6 +208,9 @@ pub fn crashHandler( break :check_flag false; } } + // Act like release build when explicitly enabling reporting + if (isReportingEnabled()) break :check_flag false; + break :check_flag true; }; @@ -1562,6 +1565,7 @@ pub const js_bindings = struct { .{ "panic", jsPanic }, .{ "rootError", jsRootError }, .{ "outOfMemory", jsOutOfMemory }, + .{ "raiseIgnoringPanicHandler", jsRaiseIgnoringPanicHandler }, }) |tuple| { const name = JSC.ZigString.static(tuple[0]); obj.put(global, name, JSC.createCallback(global, name, 1, tuple[1])); @@ -1599,6 +1603,10 @@ pub const js_bindings = struct { bun.outOfMemory(); } + pub fn jsRaiseIgnoringPanicHandler(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { + bun.Global.raiseIgnoringPanicHandler(.SIGSEGV); + } + pub fn jsGetFeaturesAsVLQ(global: *JSC.JSGlobalObject, _: *JSC.CallFrame) JSC.JSValue { const bits = bun.Analytics.packedFeatures(); var buf = std.BoundedArray(u8, 16){}; diff --git a/src/fd.zig b/src/fd.zig index 27e9745e2ef115..b8c7fec50e34d8 100644 --- a/src/fd.zig +++ b/src/fd.zig @@ -272,7 +272,6 @@ pub const FDImpl = packed struct { if (env.isDebug) { if (result) |err| { if (err.errno == @intFromEnum(posix.E.BADF)) { - // TODO(@paperdave): Zig Compiler Bug, if you remove `this` from the log. An error is correctly printed, but with the wrong reference trace bun.Output.debugWarn("close({s}) = EBADF. This is an indication of a file descriptor UAF", .{this_fmt}); } else { log("close({s}) = {}", .{ this_fmt, err }); diff --git a/src/install/lifecycle_script_runner.zig b/src/install/lifecycle_script_runner.zig index 0d1eeddef310ea..f9df8bfd639f28 100644 --- a/src/install/lifecycle_script_runner.zig +++ b/src/install/lifecycle_script_runner.zig @@ -353,15 +353,15 @@ pub const LifecycleScriptSubprocess = struct { }, .signaled => |signal| { this.printOutput(); + const signal_code = bun.SignalCode.from(signal); + Output.prettyErrorln("error: {s} script from \"{s}\" terminated by {}", .{ this.scriptName(), this.package_name, - - bun.SignalCode.from(signal).fmt(Output.enable_ansi_colors_stderr), + signal_code.fmt(Output.enable_ansi_colors_stderr), }); - Global.raiseIgnoringPanicHandler(@intFromEnum(signal)); - return; + Global.raiseIgnoringPanicHandler(signal); }, .err => |err| { Output.prettyErrorln("error: Failed to run {s} script from \"{s}\" due to\n{}", .{ @@ -372,7 +372,6 @@ pub const LifecycleScriptSubprocess = struct { this.deinit(); Output.flush(); Global.exit(1); - return; }, else => { Output.panic("error: Failed to run {s} script from \"{s}\" due to unexpected status\n{any}", .{ diff --git a/src/js/internal-for-testing.ts b/src/js/internal-for-testing.ts index 4f6f42231b4e6e..9314943ab712d4 100644 --- a/src/js/internal-for-testing.ts +++ b/src/js/internal-for-testing.ts @@ -57,6 +57,7 @@ export const crash_handler = $zig("crash_handler.zig", "js_bindings.generate") a panic: () => void; rootError: () => void; outOfMemory: () => void; + raiseIgnoringPanicHandler: () => void; }; export const upgrade_test_helpers = $zig("upgrade_command.zig", "upgrade_js_bindings.generate") as { diff --git a/test/cli/run/fixture-crash.js b/test/cli/run/fixture-crash.js index 9a56452ac7022e..c90049da5d2dba 100644 --- a/test/cli/run/fixture-crash.js +++ b/test/cli/run/fixture-crash.js @@ -11,5 +11,5 @@ const approach = process.argv[2]; if (approach in crash_handler) { crash_handler[approach](); } else { - console.error("usage: bun fixture-crash.js "); + console.error("usage: bun fixture-crash.js "); } diff --git a/test/cli/run/run-crash-handler.test.ts b/test/cli/run/run-crash-handler.test.ts index 0cc84f40c79fdb..0769129f8ac70f 100644 --- a/test/cli/run/run-crash-handler.test.ts +++ b/test/cli/run/run-crash-handler.test.ts @@ -11,58 +11,84 @@ test.if(process.platform === "darwin")("macOS has the assumed image offset", () expect(getMachOImageZeroOffset()).toBe(0x100000000); }); +test("raise ignoring panic handler does not trigger the panic handler", async () => { + let sent = false; + let onresolve = Promise.withResolvers(); + + using server = Bun.serve({ + port: 0, + fetch(request, server) { + sent = true; + onresolve.resolve(); + return new Response("OK"); + }, + }); + + const proc = Bun.spawn({ + cmd: [bunExe(), path.join(import.meta.dir, "fixture-crash.js"), "raiseIgnoringPanicHandler"], + env: mergeWindowEnvs([ + bunEnv, + { + BUN_CRASH_REPORT_URL: server.url.toString(), + BUN_ENABLE_CRASH_REPORTING: "1", + }, + ]), + }); + await proc.exited; + + await Promise.race([onresolve.promise, Bun.sleep(1000)]); + + expect(proc.exitCode).not.toBe(0); + expect(sent).toBe(false); +}); + describe("automatic crash reporter", () => { - const has_reporting = process.platform !== "linux"; + for (const approach of ["panic", "segfault", "outOfMemory"]) { + test(`${approach} should report`, async () => { + let sent = false; + let onresolve = Promise.withResolvers(); - for (const should_report of has_reporting ? [true, false] : [false]) { - for (const approach of ["panic", "segfault"]) { - // TODO: this dependency injection no worky. fix later - test.todo(`${approach} ${should_report ? "should" : "should not"} report`, async () => { - const temp = tempDirWithFiles("crash-handler-path", { - "curl": ({ root }) => `#!/usr/bin/env bash -echo $@ > ${root}/request.out -`, - "powershell.cmd": ({ root }) => `echo true > ${root}\\request.out -`, - }); + // Self host the crash report backend. + using server = Bun.serve({ + port: 0, + fetch(request, server) { + expect(request.url).toEndWith("/ack"); + sent = true; + onresolve.resolve(); + return new Response("OK"); + }, + }); - const env: any = mergeWindowEnvs([ + const proc = Bun.spawn({ + cmd: [bunExe(), path.join(import.meta.dir, "fixture-crash.js"), approach], + env: mergeWindowEnvs([ + bunEnv, { - ...bunEnv, + BUN_CRASH_REPORT_URL: server.url.toString(), + BUN_ENABLE_CRASH_REPORTING: "1", GITHUB_ACTIONS: undefined, CI: undefined, }, - { - PATH: temp + path.delimiter + process.env.PATH, - }, - ]); - - if (!should_report) { - env.DO_NOT_TRACK = "1"; - } + ]), + stdio: ["ignore", "pipe", "pipe"], + }); + await proc.exited; - const result = Bun.spawnSync( - [ - bunExe(), - path.join(import.meta.dir, "fixture-crash.js"), - approach, - "--debug-crash-handler-use-trace-string", - ], - { env }, - ); + await Promise.race([onresolve.promise, Bun.sleep(1000)]); - console.log(result.stderr.toString("utf-8")); - try { - expect(result.stderr.toString("utf-8")).toInclude("https://bun.report/"); - } catch (e) { - throw e; - } + const stderr = await Bun.readableStreamToText(proc.stderr); - await Bun.sleep(1000); + console.log(stderr); - const did_report = existsSync(path.join(temp, "request.out")); - expect(did_report).toBe(should_report); - }); - } + expect(proc.exitCode).not.toBe(0); + expect(stderr).toContain(server.url.toString()); + if (approach !== "outOfMemory") { + expect(stderr).toContain("oh no: Bun has crashed. This indicates a bug in Bun, not your code"); + } else { + expect(stderr.toLowerCase()).toContain("out of memory"); + expect(stderr.toLowerCase()).not.toContain("panic"); + } + expect(sent).toBe(true); + }); } });