authorgravatar for jan.hafer@rwth-aachen.deJan Philipp Hafer <jan.hafer@rwth-aachen.de> 2022-05-02 00:27:02+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-06-06 15:32:39-07:00
logd1e8b73939ae9f139d90157a4623c5fe68b0ae64
tree2029c88d5d1fea6bb309b0f79f272cfefe3f272a
parent367e2b2fe43a2de09767ad8d5657866088b44678

std.os.abort: ported signal handling from musl

* Document deviation from Linux man page, which is identical to musl. Man page wants always enabled user-provided abort handlers. Worst case logic bug, which this can introduce: + user disables SIGABRT handler to prevent tear down to last safe state + abort() gets called and enables user-provided SIGABRT handler + SIGABRT tears down to supposed last safe state instead of crash + Application, instead of crashing, continues * Pid 1 within containers needs special handling. - fatal signals are not transmitted without privileges, so use exit as fallback * Fix some signaling bits * Add checks in Debug and ReleaseSafe for wrong sigprocmask

1 files changed, 61 insertions(+), 3 deletions(-)

lib/std/os.zig+61-3
...@@ -250,6 +250,15 @@ pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (builtin....@@ -250,6 +250,15 @@ pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (builtin.
250 else => undefined,250 else => undefined,
251};251};
252252
253/// Atomic to guard correct program teardown in abort()
254var abort_entered = impl: {
255 if (builtin.single_threaded) {
256 break :impl {};
257 } else {
258 break :impl std.atomic.Atomic(bool).init(false);
259 }
260};
261
253/// To obtain errno, call this function with the return value of the262/// To obtain errno, call this function with the return value of the
254/// system function call. For some systems this will obtain the value directly263/// system function call. For some systems this will obtain the value directly
255/// from the return code; for others it will use a thread-local errno variable.264/// from the return code; for others it will use a thread-local errno variable.
...@@ -442,6 +451,7 @@ fn getRandomBytesDevURandom(buf: []u8) !void {...@@ -442,6 +451,7 @@ fn getRandomBytesDevURandom(buf: []u8) !void {
442/// Causes abnormal process termination.451/// Causes abnormal process termination.
443/// If linking against libc, this calls the abort() libc function. Otherwise452/// If linking against libc, this calls the abort() libc function. Otherwise
444/// it raises SIGABRT followed by SIGKILL and finally lo453/// it raises SIGABRT followed by SIGKILL and finally lo
454/// assume: Current signal handler for SIGABRT does **not call abort**.
445pub fn abort() noreturn {455pub fn abort() noreturn {
446 @setCold(true);456 @setCold(true);
447 // MSVCRT abort() sometimes opens a popup window which is undesirable, so457 // MSVCRT abort() sometimes opens a popup window which is undesirable, so
...@@ -454,11 +464,48 @@ pub fn abort() noreturn {...@@ -454,11 +464,48 @@ pub fn abort() noreturn {
454 windows.kernel32.ExitProcess(3);464 windows.kernel32.ExitProcess(3);
455 }465 }
456 if (!builtin.link_libc and builtin.os.tag == .linux) {466 if (!builtin.link_libc and builtin.os.tag == .linux) {
467 // Linux man page wants to first "unblock SIG.ABRT", but this is a footgun
468 // for user-defined signal handlers that want to restore some state in
469 // some program sections and crash in others
470
471 // user installed SIGABRT handler is run, if installed
457 raise(SIG.ABRT) catch {};472 raise(SIG.ABRT) catch {};
458473
459 // TODO the rest of the implementation of abort() from musl libc here474 // disable all signal handlers
475 sigprocmask(SIG.BLOCK, &linux.all_mask, null);
476
477 // ensure teardown by one thread
478 if (!builtin.single_threaded) {
479 while (abort_entered.compareAndSwap(false, true, .SeqCst, .SeqCst)) |_| {}
480 }
481
482 // install default handler to terminate
483 const sigact = Sigaction{
484 .handler = .{ .sigaction = SIG.DFL },
485 .mask = undefined,
486 .flags = undefined,
487 .restorer = undefined,
488 };
489 sigaction(SIG.ABRT, &sigact, null) catch unreachable;
490
491 // make sure we have a pending SIGABRT queued
492 const tid = std.Thread.getCurrentId();
493 _ = linux.tkill(@intCast(i32, tid), SIG.ABRT);
460494
495 // SIG.ABRT signal will run default handler
496 const sigabrtmask: linux.sigset_t = [_]u32{0} ** 31 ++ [_]u32{1 << (SIG.ABRT - 1)};
497 sigprocmask(SIG.UNBLOCK, &sigabrtmask, null); // [32]u32
498
499 // Beyond this point should be unreachable
500
501 // abnormal termination without using signal handler
502 const nullptr: *allowzero volatile u8 = @intToPtr(*allowzero volatile u8, 0);
503 nullptr.* = 0;
504
505 // try SIGKILL, which is no abnormal termination as defined by POSIX and ISO C
461 raise(SIG.KILL) catch {};506 raise(SIG.KILL) catch {};
507
508 // pid 1 might not be signalled in some containers
462 exit(127);509 exit(127);
463 }510 }
464 if (builtin.os.tag == .uefi) {511 if (builtin.os.tag == .uefi) {
...@@ -485,13 +532,13 @@ pub fn raise(sig: u8) RaiseError!void {...@@ -485,13 +532,13 @@ pub fn raise(sig: u8) RaiseError!void {
485 if (builtin.os.tag == .linux) {532 if (builtin.os.tag == .linux) {
486 var set: sigset_t = undefined;533 var set: sigset_t = undefined;
487 // block application signals534 // block application signals
488 _ = linux.sigprocmask(SIG.BLOCK, &linux.app_mask, &set);535 sigprocmask(SIG.BLOCK, &linux.app_mask, &set);
489536
490 const tid = linux.gettid();537 const tid = linux.gettid();
491 const rc = linux.tkill(tid, sig);538 const rc = linux.tkill(tid, sig);
492539
493 // restore signal mask540 // restore signal mask
494 _ = linux.sigprocmask(SIG.SETMASK, &set, null);541 sigprocmask(SIG.SETMASK, &set, null);
495542
496 switch (errno(rc)) {543 switch (errno(rc)) {
497 .SUCCESS => return,544 .SUCCESS => return,
...@@ -5441,6 +5488,17 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact...@@ -5441,6 +5488,17 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact
5441 }5488 }
5442}5489}
54435490
5491/// Set the thread signal mask
5492/// Invalid masks are checked in Debug and ReleaseFast
5493pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?*sigset_t) void {
5494 switch (errno(system.sigprocmask(flags, set, oldset))) {
5495 .SUCCESS => return,
5496 .FAULT => unreachable,
5497 .INVAL => unreachable, // main purpose: debug InvalidValue error
5498 else => unreachable,
5499 }
5500}
5501
5444pub const FutimensError = error{5502pub const FutimensError = error{
5445 /// times is NULL, or both tv_nsec values are UTIME_NOW, and either:5503 /// times is NULL, or both tv_nsec values are UTIME_NOW, and either:
5446 /// * the effective user ID of the caller does not match the owner5504 /// * the effective user ID of the caller does not match the owner