Improve quality of raise(), abort(), and tkill()

This change fixes a nasty bug where SIG_IGN and SIG_DFL weren't working
as advertised on BSDs. This change also fixes the tkill() definition on
MacOS so it maps to __pthread_kill().
This commit is contained in:
Justine Tunney 2022-09-03 18:12:01 -07:00
parent c5659b93f8
commit c5c4dfcd21
12 changed files with 293 additions and 63 deletions

View file

@ -17,32 +17,45 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/getconsolectrlevent.internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/nt/console.h"
#include "libc/nt/errors.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h"
#include "libc/dce.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
#include "libc/thread/xnu.internal.h"
static textwindows inline bool HasWorkingConsole(void) {
return !!(__ntconsolemode[0] | __ntconsolemode[1] | __ntconsolemode[2]);
}
static noubsan void RaiseSigFpe(void) {
volatile int x = 0;
x = 1 / x;
}
/**
* Sends signal to this thread.
* Sends signal to self.
*
* This is basically the same as:
*
* tkill(gettid(), sig);
*
* Note `SIG_DFL` still results in process death for most signals.
*
* This function is not entirely equivalent to kill() or tkill(). For
* example, we raise `SIGTRAP` and `SIGFPE` the natural way, since that
* helps us support Windows. So if the raised signal has a signal
* handler, then the reported `si_code` might not be `SI_TKILL`.
*
* On Windows, if a signal results in the termination of the process
* then we use the convention `_Exit(128 + sig)` to notify the parent of
* the signal number.
*
* @param sig can be SIGALRM, SIGINT, SIGTERM, SIGKILL, etc.
* @return 0 on success or -1 w/ errno
* @return 0 if signal was delivered and returned, or -1 w/ errno
* @asyncsignalsafe
*/
int raise(int sig) {
@ -52,28 +65,12 @@ int raise(int sig) {
DebugBreak();
rc = 0;
} else if (sig == SIGFPE) {
volatile int x = 0;
x = 1 / x;
RaiseSigFpe();
rc = 0;
} else if (!IsWindows()) {
rc = sys_tkill(gettid(), sig, 0);
} else {
if (HasWorkingConsole() && (event = GetConsoleCtrlEvent(sig)) != -1) {
// XXX: MSDN says "If this parameter is zero, the signal is
// generated in all processes that share the console of the
// calling process." which seems to imply multiple process
// groups potentially. We just shouldn't use this because it
// doesn't make any sense and it's so evil.
if (GenerateConsoleCtrlEvent(event, 0)) {
SleepEx(100, true);
__sig_check(false);
rc = 0;
} else {
rc = __winerr();
}
} else {
rc = __sig_raise(sig, SI_USER);
}
rc = __sig_raise(sig, SI_TKILL);
}
STRACE("...raise(%G) → %d% m", sig, rc);
return rc;

View file

@ -182,8 +182,11 @@ static int __sigaction(int sig, const struct sigaction *act,
ap = ©
if (IsXnu()) {
ap->sa_restorer = (void *)&__sigenter_xnu;
ap->sa_handler = (void *)&__sigenter_xnu;
if (rva < kSigactionMinRva) {
ap->sa_sigaction = (void *)(intptr_t)rva;
} else {
ap->sa_sigaction = (void *)&__sigenter_xnu;
}
// mitigate Rosetta signal handling strangeness
// https://github.com/jart/cosmopolitan/issues/455
ap->sa_flags |= SA_SIGINFO;
@ -193,11 +196,23 @@ static int __sigaction(int sig, const struct sigaction *act,
ap->sa_restorer = &__restore_rt;
}
} else if (IsNetbsd()) {
ap->sa_sigaction = (sigaction_f)__sigenter_netbsd;
if (rva < kSigactionMinRva) {
ap->sa_sigaction = (void *)(intptr_t)rva;
} else {
ap->sa_sigaction = (sigaction_f)__sigenter_netbsd;
}
} else if (IsFreebsd()) {
ap->sa_sigaction = (sigaction_f)__sigenter_freebsd;
if (rva < kSigactionMinRva) {
ap->sa_sigaction = (void *)(intptr_t)rva;
} else {
ap->sa_sigaction = (sigaction_f)__sigenter_freebsd;
}
} else if (IsOpenbsd()) {
ap->sa_sigaction = (sigaction_f)__sigenter_openbsd;
if (rva < kSigactionMinRva) {
ap->sa_sigaction = (void *)(intptr_t)rva;
} else {
ap->sa_sigaction = (sigaction_f)__sigenter_openbsd;
}
} else {
return enosys();
}

View file

@ -17,30 +17,29 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/sigset.h"
#include "libc/dce.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/sig.h"
/**
* Terminates program abnormally.
*
* This function first tries to trigger your SIGABRT handler. If
* there isn't one or execution resumes, then abort() terminates
* the program using an escalating variety methods of increasing
* brutality.
* This function first tries to trigger your SIGABRT handler. If the
* signal handler returns, then `signal(SIGABRT, SIG_DFL)` is called
* before SIGABRT is raised again.
*
* @asyncsignalsafe
* @noreturn
*/
privileged void abort(void) {
sigset_t sm;
sigfillset(&sm);
sigdelset(&sm, SIGABRT);
sigprocmask(SIG_SETMASK, &sm, 0);
wontreturn void abort(void) {
sigset_t m;
sigemptyset(&m);
sigaddset(&m, SIGABRT);
sigprocmask(SIG_UNBLOCK, &m, 0);
raise(SIGABRT);
__restorewintty();
_Exit(128 + SIGABRT);
signal(SIGABRT, SIG_DFL);
raise(SIGABRT);
asm("hlt");
unreachable;
}

View file

@ -65,7 +65,7 @@ void _exit(int) libcesque wontreturn;
void _Exit(int) libcesque wontreturn;
void _Exit1(int) libcesque wontreturn;
void quick_exit(int) wontreturn;
void abort(void) wontreturn noinstrument;
void abort(void) wontreturn;
int __cxa_atexit(void *, void *, void *) libcesque;
int atfork(void *, void *) libcesque;
int atexit(void (*)(void)) libcesque;

View file

@ -1,2 +1,2 @@
.include "o/libc/sysv/macros.internal.inc"
.scall sys_tkill,0x13e0771b121690c8,globl,hidden
.scall sys_tkill,0x13e0771b121480c8,globl,hidden

View file

@ -533,7 +533,7 @@ syscon sicode SI_TKILL -6 0x80000000 0x010007 -1 -5 -6 # sent by tk
syscon sicode SI_MESGQ -3 0x010005 0x010005 0x80000000 -4 -3 # sent by mq_notify(2); lool
syscon sicode SI_ASYNCIO -4 0x010004 0x010004 0x80000000 -3 -4 # aio completion; no thank you
syscon sicode SI_ASYNCNL -60 0x80000000 0x80000000 0x80000000 0x80000000 0x80000000 # aio completion for dns; the horror
syscon sicode SI_KERNEL 0x80 0x80000000 0x010006 0x80000000 0x80000000 0x80 # wut; openbsd defines as si_code>0
syscon sicode SI_KERNEL 128 0x80000000 0x010006 0x80000000 0x80000000 0x80 # wut; openbsd defines as si_code>0
syscon sicode SI_NOINFO 32767 0x80000000 0 32767 32767 32767 # no signal specific info available
syscon sicode CLD_EXITED 1 1 1 1 1 1 # SIGCHLD; child exited; unix consensus
syscon sicode CLD_KILLED 2 2 2 2 2 2 # SIGCHLD; child terminated w/o core; unix consensus

View file

@ -98,7 +98,7 @@ scall __sys_wait4 0x1c100b007200703d globl hidden
scall sys_kill 0x02507a025202503e globl hidden # kill(pid, sig, 1) b/c xnu
scall sys_killpg 0x092fff092fffffff globl hidden
scall sys_clone 0x11fffffffffff038 globl hidden
scall sys_tkill 0x13e0771b121690c8 globl hidden # thr_kill() on freebsd; _lwp_kill() on netbsd; thrkill() on openbsd where arg3 should be 0; bsdthread_terminate() on XNU which only has 1 arg
scall sys_tkill 0x13e0771b121480c8 globl hidden # thr_kill() on freebsd; _lwp_kill() on netbsd; thrkill() on openbsd where arg3 should be 0; __pthread_kill() on XNU
scall sys_futex 0x0a6053fffffff0ca globl hidden # raises SIGSYS on NetBSD
scall set_robust_list 0x0a7ffffffffff111 globl
scall get_robust_list 0x0a8ffffffffff112 globl

View file

@ -12,6 +12,7 @@ int bsdthread_create(void *func, void *func_arg, void *stack, void *pthread,
uint32_t flags);
int bsdthread_terminate(void *stackaddr, size_t freesize, uint32_t port,
uint32_t sem);
int __pthread_kill(uint32_t port, int sig);
int bsdthread_register(
void (*threadstart)(void *pthread, int machport, void *(*func)(void *),
void *arg, intptr_t *, unsigned),