Skip to content
Merged
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
20 changes: 20 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,26 @@ pub fn build(b: *Build) !void {
.{ .os = .linux, .arch = .aarch64 },
}, &.{.Debug});
}
// check-android needs the NDK sysroot for translate-c (zig doesn't
// bundle bionic headers). Skip step creation entirely when none was
// passed, so plain `zig build check` doesn't try to construct a
// translate-c step that would panic.
if (android_ndk_sysroot != null) {
{
const step = b.step("check-android", "Check for semantic analysis errors on Android");
addMultiCheck(b, step, build_options, &.{
.{ .os = .linux, .arch = .x86_64, .android = true },
.{ .os = .linux, .arch = .aarch64, .android = true },
}, &.{ .Debug, .ReleaseFast });
}
{
const step = b.step("check-android-debug", "Check for semantic analysis errors on Android");
addMultiCheck(b, step, build_options, &.{
.{ .os = .linux, .arch = .x86_64, .android = true },
.{ .os = .linux, .arch = .aarch64, .android = true },
}, &.{.Debug});
}
}
// check-freebsd needs the sysroot for translate-c (zig doesn't bundle
// FreeBSD libc headers). Skip step creation entirely when none was
// passed, so plain `zig build check` doesn't try to construct a
Expand Down
6 changes: 3 additions & 3 deletions src/bun_core/Global.zig
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,12 @@ pub fn raiseIgnoringPanicHandler(sig: bun.SignalCode) noreturn {

// clear signal handler
if (bun.Environment.os != .windows) {
var sa: std.c.Sigaction = .{
var sa: bun.sys.Sigaction = .{
.handler = .{ .handler = std.posix.SIG.DFL },
.mask = std.posix.sigemptyset(),
.mask = bun.sys.sigemptyset(),
.flags = std.posix.SA.RESETHAND,
};
_ = std.c.sigaction(@intFromEnum(sig), &sa, null);
bun.sys.sigaction(@intFromEnum(sig), &sa, null);
}

// kill self
Expand Down
6 changes: 3 additions & 3 deletions src/cli/filter_run.zig
Original file line number Diff line number Diff line change
Expand Up @@ -391,12 +391,12 @@ const AbortHandler = struct {

pub fn install() void {
if (Environment.isPosix) {
const action = std.posix.Sigaction{
const action = bun.sys.Sigaction{
.handler = .{ .sigaction = AbortHandler.posixSignalHandler },
.mask = std.posix.sigemptyset(),
.mask = bun.sys.sigemptyset(),
.flags = std.posix.SA.SIGINFO | std.posix.SA.RESTART | std.posix.SA.RESETHAND,
};
std.posix.sigaction(std.posix.SIG.INT, &action, null);
bun.sys.sigaction(std.posix.SIG.INT, &action, null);
} else {
const res = bun.c.SetConsoleCtrlHandler(windowsCtrlHandler, std.os.windows.TRUE);
if (res == 0) {
Expand Down
6 changes: 3 additions & 3 deletions src/cli/multi_run.zig
Original file line number Diff line number Diff line change
Expand Up @@ -367,12 +367,12 @@ const AbortHandler = struct {

pub fn install() void {
if (Environment.isPosix) {
const action = std.posix.Sigaction{
const action = bun.sys.Sigaction{
.handler = .{ .sigaction = AbortHandler.posixSignalHandler },
.mask = std.posix.sigemptyset(),
.mask = bun.sys.sigemptyset(),
.flags = std.posix.SA.SIGINFO | std.posix.SA.RESTART | std.posix.SA.RESETHAND,
};
std.posix.sigaction(std.posix.SIG.INT, &action, null);
bun.sys.sigaction(std.posix.SIG.INT, &action, null);
} else {
const res = bun.c.SetConsoleCtrlHandler(windowsCtrlHandler, std.os.windows.TRUE);
if (res == 0) {
Expand Down
12 changes: 6 additions & 6 deletions src/cli/repl.zig
Original file line number Diff line number Diff line change
Expand Up @@ -794,12 +794,12 @@ fn enableSignalsDuringWait(self: *Repl) void {
_ = bun.tty.setMode(0, .normal);

// Install SIGINT handler
const act = std.posix.Sigaction{
const act = bun.sys.Sigaction{
.handler = .{ .handler = sigintHandler },
.mask = std.posix.sigemptyset(),
.mask = bun.sys.sigemptyset(),
.flags = 0,
};
std.posix.sigaction(std.posix.SIG.INT, &act, null);
bun.sys.sigaction(std.posix.SIG.INT, &act, null);
}
// On Windows, ENABLE_PROCESSED_INPUT is already set so Ctrl+C works
}
Expand All @@ -814,12 +814,12 @@ fn disableSignalsDuringWait(self: *Repl) void {
_ = bun.tty.setMode(0, .raw);

// Restore default SIGINT handling
const act = std.posix.Sigaction{
const act = bun.sys.Sigaction{
.handler = .{ .handler = std.posix.SIG.DFL },
.mask = std.posix.sigemptyset(),
.mask = bun.sys.sigemptyset(),
.flags = 0,
};
std.posix.sigaction(std.posix.SIG.INT, &act, null);
bun.sys.sigaction(std.posix.SIG.INT, &act, null);
}
}

Expand Down
16 changes: 8 additions & 8 deletions src/cli/test/parallel/Coordinator.zig
Original file line number Diff line number Diff line change
Expand Up @@ -449,8 +449,8 @@ pub const Coordinator = struct {
/// this (SIGKILL).
pub const AbortHandler = struct {
var should_abort: std.atomic.Value(bool) = .init(false);
var prev_int: if (Environment.isPosix) std.posix.Sigaction else void = undefined;
var prev_term: if (Environment.isPosix) std.posix.Sigaction else void = undefined;
var prev_int: if (Environment.isPosix) bun.sys.Sigaction else void = undefined;
var prev_term: if (Environment.isPosix) bun.sys.Sigaction else void = undefined;

fn posixHandler(_: i32, _: *const std.posix.siginfo_t, _: ?*const anyopaque) callconv(.c) void {
should_abort.store(true, .release);
Expand All @@ -468,22 +468,22 @@ pub const Coordinator = struct {

pub fn install() void {
if (Environment.isPosix) {
const act = std.posix.Sigaction{
const act = bun.sys.Sigaction{
.handler = .{ .sigaction = posixHandler },
.mask = std.posix.sigemptyset(),
.mask = bun.sys.sigemptyset(),
.flags = std.posix.SA.SIGINFO,
};
std.posix.sigaction(std.posix.SIG.INT, &act, &prev_int);
std.posix.sigaction(std.posix.SIG.TERM, &act, &prev_term);
bun.sys.sigaction(std.posix.SIG.INT, &act, &prev_int);
bun.sys.sigaction(std.posix.SIG.TERM, &act, &prev_term);
} else {
_ = bun.c.SetConsoleCtrlHandler(windowsCtrlHandler, std.os.windows.TRUE);
}
}

pub fn uninstall() void {
if (Environment.isPosix) {
std.posix.sigaction(std.posix.SIG.INT, &prev_int, null);
std.posix.sigaction(std.posix.SIG.TERM, &prev_term, null);
bun.sys.sigaction(std.posix.SIG.INT, &prev_int, null);
bun.sys.sigaction(std.posix.SIG.TERM, &prev_term, null);
} else {
_ = bun.c.SetConsoleCtrlHandler(windowsCtrlHandler, std.os.windows.FALSE);
}
Expand Down
22 changes: 11 additions & 11 deletions src/crash_handler/crash_handler.zig
Original file line number Diff line number Diff line change
Expand Up @@ -888,7 +888,7 @@ fn handleSegfaultPosix(sig: i32, info: *const std.posix.siginfo_t, _: ?*const an
var did_register_sigaltstack = false;
var sigaltstack: [512 * 1024]u8 = undefined;

fn updatePosixSegfaultHandler(act: ?*std.posix.Sigaction) !void {
fn updatePosixSegfaultHandler(act: ?*bun.sys.Sigaction) !void {
if (act) |act_| {
if (!did_register_sigaltstack) {
var stack: std.c.stack_t = .{
Expand All @@ -904,19 +904,19 @@ fn updatePosixSegfaultHandler(act: ?*std.posix.Sigaction) !void {
}
}

std.posix.sigaction(std.posix.SIG.SEGV, act, null);
std.posix.sigaction(std.posix.SIG.ILL, act, null);
std.posix.sigaction(std.posix.SIG.BUS, act, null);
std.posix.sigaction(std.posix.SIG.FPE, act, null);
bun.sys.sigaction(std.posix.SIG.SEGV, act, null);
bun.sys.sigaction(std.posix.SIG.ILL, act, null);
bun.sys.sigaction(std.posix.SIG.BUS, act, null);
bun.sys.sigaction(std.posix.SIG.FPE, act, null);
}

var windows_segfault_handle: ?windows.HANDLE = null;

pub fn resetOnPosix() void {
if (bun.Environment.enable_asan) return;
var act = std.posix.Sigaction{
var act = bun.sys.Sigaction{
.handler = .{ .sigaction = handleSegfaultPosix },
.mask = std.posix.sigemptyset(),
.mask = bun.sys.sigemptyset(),
.flags = (std.posix.SA.SIGINFO | std.posix.SA.RESTART | std.posix.SA.RESETHAND),
};
updatePosixSegfaultHandler(&act) catch {};
Expand Down Expand Up @@ -948,9 +948,9 @@ pub fn resetSegfaultHandler() void {
return;
}

var act = std.posix.Sigaction{
var act = bun.sys.Sigaction{
.handler = .{ .handler = std.posix.SIG.DFL },
.mask = std.posix.sigemptyset(),
.mask = bun.sys.sigemptyset(),
.flags = 0,
};
// To avoid a double-panic, do nothing if an error happens here.
Expand Down Expand Up @@ -1593,7 +1593,7 @@ fn crash() noreturn {
},
else => {
// Install default handler so that the tkill below will terminate.
const sigact = std.posix.Sigaction{ .handler = .{ .handler = std.posix.SIG.DFL }, .mask = std.posix.sigemptyset(), .flags = 0 };
const sigact = bun.sys.Sigaction{ .handler = .{ .handler = std.posix.SIG.DFL }, .mask = bun.sys.sigemptyset(), .flags = 0 };
inline for (.{
std.posix.SIG.SEGV,
std.posix.SIG.ILL,
Expand All @@ -1603,7 +1603,7 @@ fn crash() noreturn {
std.posix.SIG.HUP,
std.posix.SIG.TERM,
}) |sig| {
std.posix.sigaction(sig, &sigact, null);
bun.sys.sigaction(sig, &sigact, null);
}

@trap();
Expand Down
8 changes: 8 additions & 0 deletions src/js/internal-for-testing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,14 @@ export const sysErrorNameFromLibuv: (errno: number) => string | undefined = $new
1,
);

export const sigactionLayout: () =>
| undefined
| {
installed: { handler: number; flags: number };
readback: { handler: number; flags: number };
sizeof: number;
} = $newZigFunction("sys.zig", "TestingAPIs.sigactionLayout", 0);

export const stringsInternals = {
/**
* Calls `bun.strings.toUTF16AllocForReal(allocator, bytes, false, true)` and
Expand Down
8 changes: 4 additions & 4 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ pub fn main() void {
_bun.crash_handler.init();

if (Environment.isPosix) {
var act: std.posix.Sigaction = .{
var act: _bun.sys.Sigaction = .{
.handler = .{ .handler = std.posix.SIG.IGN },
.mask = std.posix.sigemptyset(),
.mask = _bun.sys.sigemptyset(),
.flags = 0,
};
std.posix.sigaction(std.posix.SIG.PIPE, &act, null);
std.posix.sigaction(std.posix.SIG.XFSZ, &act, null);
_bun.sys.sigaction(std.posix.SIG.PIPE, &act, null);
_bun.sys.sigaction(std.posix.SIG.XFSZ, &act, null);
}

if (Environment.isDebug) {
Expand Down
8 changes: 4 additions & 4 deletions src/runtime/api/bun/process.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1005,14 +1005,14 @@ const WaiterThreadPosix = struct {
}

if (comptime Environment.isLinux) {
var current_mask = std.posix.sigemptyset();
std.os.linux.sigaddset(current_mask[0..1], std.posix.SIG.CHLD);
const act = std.posix.Sigaction{
var current_mask = bun.sys.sigemptyset();
bun.sys.sigaddset(&current_mask, std.posix.SIG.CHLD);
const act = bun.sys.Sigaction{
.handler = .{ .handler = &wakeup },
.mask = current_mask,
.flags = std.posix.SA.NOCLDSTOP,
};
std.posix.sigaction(std.posix.SIG.CHLD, &act, null);
bun.sys.sigaction(std.posix.SIG.CHLD, &act, null);
}
}

Expand Down
68 changes: 68 additions & 0 deletions src/sys/sys.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2224,6 +2224,74 @@ pub fn poll(fds: []std.posix.pollfd, timeout: i32) Maybe(usize) {
}
}

/// bionic's `struct sigaction` and `sigset_t` do not match the glibc/musl
/// layout that `std.c.Sigaction` / `std.c.sigset_t` assume for `.linux`. On
/// LP64 bionic, `sigset_t` is a single `unsigned long` and `struct sigaction`
/// is `{ int sa_flags; union sa_handler; sigset_t sa_mask; sa_restorer }` —
/// `sa_flags` comes *first*. Passing the glibc-shaped struct to bionic's
/// `sigaction()` makes it read `sa_handler` from what Zig thinks is
/// `mask[0]`, so a zeroed mask silently installs `SIG_DFL` and a mask with
/// `SIGCHLD` set installs the wild pointer `0x10000` (which segfaults on
/// delivery). Until the Zig stdlib grows an `abi.isAndroid()` case, use
/// these wrappers instead of `std.posix.Sigaction` / `std.posix.sigaction`.
pub const sigset_t = if (Environment.isAndroid) c_ulong else posix.sigset_t;

pub const Sigaction = if (Environment.isAndroid) extern struct {
// bionic libc/include/bits/signal_types.h (`__LP64__`). Bun does not
// target 32-bit Android, whose layout is handler-first instead.
comptime {
bun.assert(@sizeOf(usize) == 8);
// Trip when the Zig stdlib gains a bionic `Sigaction` so this
// workaround can be dropped. bionic puts `sa_flags` at offset 0;
// the glibc-shaped struct std currently uses puts it after a
// 128-byte mask.
if (@offsetOf(std.c.Sigaction, "flags") == @offsetOf(@This(), "flags") and
@offsetOf(std.c.Sigaction, "handler") == @offsetOf(@This(), "handler"))
@compileError("std.c.Sigaction now matches bionic; remove the bun.sys.Sigaction workaround");
}

pub const handler_fn = *align(1) const fn (i32) callconv(.c) void;
pub const sigaction_fn = *const fn (i32, *const posix.siginfo_t, ?*anyopaque) callconv(.c) void;

// bionic declares `int sa_flags`, but `SA_RESETHAND` is `0x80000000`
// which doesn't fit a `c_int` literal in Zig. `c_uint` is ABI-identical
// and matches what `std.c.Sigaction` uses for every other Linux libc.
flags: c_uint,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pretty sure this should be c_int instead of c_uint

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bionic does declare int sa_flags, but SA_RESETHAND = 0x80000000 — Zig refuses to coerce that into a c_int literal, so .flags = SA.SIGINFO | SA.RESTART | SA.RESETHAND in crash_handler.zig / filter_run.zig / multi_run.zig would stop compiling on Android. c_uint is ABI-identical (same size/align) and is what std.c.Sigaction uses for every other Linux libc for the same reason. Added a comment at the field explaining this.

handler: extern union {
handler: ?handler_fn,
sigaction: ?sigaction_fn,
},
mask: sigset_t,
restorer: ?*const fn () callconv(.c) void = null,
} else posix.Sigaction;

pub inline fn sigemptyset() sigset_t {
if (comptime Environment.isAndroid) return 0;
return posix.sigemptyset();
}

pub inline fn sigaddset(set: *sigset_t, sig: u8) void {
if (comptime Environment.isAndroid) {
set.* |= @as(c_ulong, 1) << @as(u6, @intCast(sig - 1));
return;
}
posix.sigaddset(set, sig);
}

pub fn sigaction(sig: u8, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) void {
// Call libc directly on every platform. `std.posix.sigaction` does
// `else => unreachable` on error, but `raiseIgnoringPanicHandler` can
// legitimately pass `SIGKILL`/`SIGSTOP` here (when re-raising a child's
// terminating signal), for which libc returns `EINVAL`. On Android we
// also can't reuse `std.c.sigaction` because its parameter type is the
// glibc-shaped `Sigaction`.
const libc_sigaction = if (comptime Environment.isAndroid) @extern(
*const fn (c_int, noalias ?*const Sigaction, noalias ?*Sigaction) callconv(.c) c_int,
.{ .name = "sigaction" },
) else std.c.sigaction;
_ = libc_sigaction(sig, act, oact);
}

pub fn ppoll(fds: []std.posix.pollfd, timeout: ?*std.posix.timespec, sigmask: ?*const std.posix.sigset_t) Maybe(usize) {
while (true) {
const rc = switch (Environment.os) {
Expand Down
44 changes: 44 additions & 0 deletions src/sys_jsc/error_jsc.zig
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,52 @@ pub const TestingAPIs = struct {
const result = bun.windows.libuv.translateUVErrorToE(code);
return bun.String.createUTF8ForJS(globalThis, @tagName(result));
}

/// Verifies `bun.sys.Sigaction`'s layout matches the host libc by
/// round-tripping a known handler through `sigaction(2)`. If the struct
/// layout disagrees with libc (as `std.posix.Sigaction` does on Android
/// bionic — `sa_flags` first, 8-byte `sigset_t`), libc reads/writes the
/// fields at the wrong offsets and the returned handler/flags won't match
/// what we installed. Returns `{ installed, readback }` for the test to
/// compare. POSIX-only.
pub fn sigactionLayout(globalThis: *jsc.JSGlobalObject, _: *jsc.CallFrame) bun.JSError!jsc.JSValue {
if (comptime !Environment.isPosix) return .js_undefined;

const posix = std.posix;
const sentry = struct {
fn handler(_: c_int) callconv(.c) void {}
};
var mask = bun.sys.sigemptyset();
bun.sys.sigaddset(&mask, posix.SIG.USR2);
const act = bun.sys.Sigaction{
.handler = .{ .handler = &sentry.handler },
.mask = mask,
.flags = posix.SA.RESTART,
};
var prev: bun.sys.Sigaction = undefined;
var readback: bun.sys.Sigaction = undefined;
bun.sys.sigaction(posix.SIG.USR2, &act, &prev);
bun.sys.sigaction(posix.SIG.USR2, null, &readback);
bun.sys.sigaction(posix.SIG.USR2, &prev, null);

const installed = (try jsc.JSObject.create(.{
.handler = @as(f64, @floatFromInt(@intFromPtr(&sentry.handler))),
.flags = @as(f64, @floatFromInt(act.flags & posix.SA.RESTART)),
}, globalThis)).toJS();
const rb = (try jsc.JSObject.create(.{
.handler = @as(f64, @floatFromInt(@intFromPtr(readback.handler.handler))),
.flags = @as(f64, @floatFromInt(readback.flags & posix.SA.RESTART)),
}, globalThis)).toJS();
return (try jsc.JSObject.create(.{
.installed = installed,
.readback = rb,
.sizeof = @as(f64, @floatFromInt(@sizeOf(bun.sys.Sigaction))),
}, globalThis)).toJS();
}
};

const std = @import("std");

const bun = @import("bun");
const Environment = bun.Environment;
const jsc = bun.jsc;
Expand Down
Loading
Loading