Skip to content

Commit c5c4dfc

Browse files
committed
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().
1 parent c5659b9 commit c5c4dfc

File tree

12 files changed

+293
-63
lines changed

12 files changed

+293
-63
lines changed

build/rules.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ o/$(MODE)/%: o/$(MODE)/%.com o/$(MODE)/tool/build/cp.com o/$(MODE)/tool/build/as
109109
# May be specified in your ~/.cosmo.mk file. You can also use this to
110110
# enable things like function tracing. For example:
111111
#
112-
# TESTARGS = --ftrace
112+
# TESTARGS = ----ftrace
113113
# .PLEDGE += prot_exec
114114
#
115115
# You could then run a command like:

libc/calls/raise.c

Lines changed: 27 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,45 @@
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/calls/calls.h"
20-
#include "libc/calls/getconsolectrlevent.internal.h"
2120
#include "libc/calls/sig.internal.h"
2221
#include "libc/calls/strace.internal.h"
23-
#include "libc/calls/struct/sigset.h"
2422
#include "libc/calls/syscall-sysv.internal.h"
25-
#include "libc/calls/syscall_support-nt.internal.h"
26-
#include "libc/nt/console.h"
27-
#include "libc/nt/errors.h"
28-
#include "libc/nt/process.h"
29-
#include "libc/nt/runtime.h"
30-
#include "libc/nt/synchronization.h"
23+
#include "libc/dce.h"
24+
#include "libc/nexgen32e/threaded.h"
3125
#include "libc/runtime/internal.h"
32-
#include "libc/runtime/runtime.h"
33-
#include "libc/str/str.h"
3426
#include "libc/sysv/consts/sicode.h"
3527
#include "libc/sysv/consts/sig.h"
28+
#include "libc/thread/xnu.internal.h"
3629

3730
static textwindows inline bool HasWorkingConsole(void) {
3831
return !!(__ntconsolemode[0] | __ntconsolemode[1] | __ntconsolemode[2]);
3932
}
4033

34+
static noubsan void RaiseSigFpe(void) {
35+
volatile int x = 0;
36+
x = 1 / x;
37+
}
38+
4139
/**
42-
* Sends signal to this thread.
40+
* Sends signal to self.
41+
*
42+
* This is basically the same as:
43+
*
44+
* tkill(gettid(), sig);
45+
*
46+
* Note `SIG_DFL` still results in process death for most signals.
47+
*
48+
* This function is not entirely equivalent to kill() or tkill(). For
49+
* example, we raise `SIGTRAP` and `SIGFPE` the natural way, since that
50+
* helps us support Windows. So if the raised signal has a signal
51+
* handler, then the reported `si_code` might not be `SI_TKILL`.
52+
*
53+
* On Windows, if a signal results in the termination of the process
54+
* then we use the convention `_Exit(128 + sig)` to notify the parent of
55+
* the signal number.
4356
*
4457
* @param sig can be SIGALRM, SIGINT, SIGTERM, SIGKILL, etc.
45-
* @return 0 on success or -1 w/ errno
58+
* @return 0 if signal was delivered and returned, or -1 w/ errno
4659
* @asyncsignalsafe
4760
*/
4861
int raise(int sig) {
@@ -52,28 +65,12 @@ int raise(int sig) {
5265
DebugBreak();
5366
rc = 0;
5467
} else if (sig == SIGFPE) {
55-
volatile int x = 0;
56-
x = 1 / x;
68+
RaiseSigFpe();
5769
rc = 0;
5870
} else if (!IsWindows()) {
5971
rc = sys_tkill(gettid(), sig, 0);
6072
} else {
61-
if (HasWorkingConsole() && (event = GetConsoleCtrlEvent(sig)) != -1) {
62-
// XXX: MSDN says "If this parameter is zero, the signal is
63-
// generated in all processes that share the console of the
64-
// calling process." which seems to imply multiple process
65-
// groups potentially. We just shouldn't use this because it
66-
// doesn't make any sense and it's so evil.
67-
if (GenerateConsoleCtrlEvent(event, 0)) {
68-
SleepEx(100, true);
69-
__sig_check(false);
70-
rc = 0;
71-
} else {
72-
rc = __winerr();
73-
}
74-
} else {
75-
rc = __sig_raise(sig, SI_USER);
76-
}
73+
rc = __sig_raise(sig, SI_TKILL);
7774
}
7875
STRACE("...raise(%G) → %d% m", sig, rc);
7976
return rc;

libc/calls/sigaction.c

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,11 @@ static int __sigaction(int sig, const struct sigaction *act,
182182
ap = ©
183183
if (IsXnu()) {
184184
ap->sa_restorer = (void *)&__sigenter_xnu;
185-
ap->sa_handler = (void *)&__sigenter_xnu;
186-
185+
if (rva < kSigactionMinRva) {
186+
ap->sa_sigaction = (void *)(intptr_t)rva;
187+
} else {
188+
ap->sa_sigaction = (void *)&__sigenter_xnu;
189+
}
187190
// mitigate Rosetta signal handling strangeness
188191
// https://github.com/jart/cosmopolitan/issues/455
189192
ap->sa_flags |= SA_SIGINFO;
@@ -193,11 +196,23 @@ static int __sigaction(int sig, const struct sigaction *act,
193196
ap->sa_restorer = &__restore_rt;
194197
}
195198
} else if (IsNetbsd()) {
196-
ap->sa_sigaction = (sigaction_f)__sigenter_netbsd;
199+
if (rva < kSigactionMinRva) {
200+
ap->sa_sigaction = (void *)(intptr_t)rva;
201+
} else {
202+
ap->sa_sigaction = (sigaction_f)__sigenter_netbsd;
203+
}
197204
} else if (IsFreebsd()) {
198-
ap->sa_sigaction = (sigaction_f)__sigenter_freebsd;
205+
if (rva < kSigactionMinRva) {
206+
ap->sa_sigaction = (void *)(intptr_t)rva;
207+
} else {
208+
ap->sa_sigaction = (sigaction_f)__sigenter_freebsd;
209+
}
199210
} else if (IsOpenbsd()) {
200-
ap->sa_sigaction = (sigaction_f)__sigenter_openbsd;
211+
if (rva < kSigactionMinRva) {
212+
ap->sa_sigaction = (void *)(intptr_t)rva;
213+
} else {
214+
ap->sa_sigaction = (sigaction_f)__sigenter_openbsd;
215+
}
201216
} else {
202217
return enosys();
203218
}

libc/runtime/abort.greg.c renamed to libc/runtime/abort.c

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,29 @@
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/calls/calls.h"
20-
#include "libc/calls/sig.internal.h"
20+
#include "libc/calls/struct/sigaction.h"
2121
#include "libc/calls/struct/sigset.h"
22-
#include "libc/dce.h"
23-
#include "libc/runtime/internal.h"
2422
#include "libc/runtime/runtime.h"
2523
#include "libc/sysv/consts/sig.h"
2624

2725
/**
2826
* Terminates program abnormally.
2927
*
30-
* This function first tries to trigger your SIGABRT handler. If
31-
* there isn't one or execution resumes, then abort() terminates
32-
* the program using an escalating variety methods of increasing
33-
* brutality.
28+
* This function first tries to trigger your SIGABRT handler. If the
29+
* signal handler returns, then `signal(SIGABRT, SIG_DFL)` is called
30+
* before SIGABRT is raised again.
3431
*
3532
* @asyncsignalsafe
3633
* @noreturn
3734
*/
38-
privileged void abort(void) {
39-
sigset_t sm;
40-
sigfillset(&sm);
41-
sigdelset(&sm, SIGABRT);
42-
sigprocmask(SIG_SETMASK, &sm, 0);
35+
wontreturn void abort(void) {
36+
sigset_t m;
37+
sigemptyset(&m);
38+
sigaddset(&m, SIGABRT);
39+
sigprocmask(SIG_UNBLOCK, &m, 0);
4340
raise(SIGABRT);
44-
__restorewintty();
45-
_Exit(128 + SIGABRT);
41+
signal(SIGABRT, SIG_DFL);
42+
raise(SIGABRT);
43+
asm("hlt");
44+
unreachable;
4645
}

libc/runtime/runtime.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ void _exit(int) libcesque wontreturn;
6565
void _Exit(int) libcesque wontreturn;
6666
void _Exit1(int) libcesque wontreturn;
6767
void quick_exit(int) wontreturn;
68-
void abort(void) wontreturn noinstrument;
68+
void abort(void) wontreturn;
6969
int __cxa_atexit(void *, void *, void *) libcesque;
7070
int atfork(void *, void *) libcesque;
7171
int atexit(void (*)(void)) libcesque;

libc/sysv/calls/sys_tkill.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
.include "o/libc/sysv/macros.internal.inc"
2-
.scall sys_tkill,0x13e0771b121690c8,globl,hidden
2+
.scall sys_tkill,0x13e0771b121480c8,globl,hidden

libc/sysv/consts.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ syscon sicode SI_TKILL -6 0x80000000 0x010007 -1 -5 -6 # sent by tk
533533
syscon sicode SI_MESGQ -3 0x010005 0x010005 0x80000000 -4 -3 # sent by mq_notify(2); lool
534534
syscon sicode SI_ASYNCIO -4 0x010004 0x010004 0x80000000 -3 -4 # aio completion; no thank you
535535
syscon sicode SI_ASYNCNL -60 0x80000000 0x80000000 0x80000000 0x80000000 0x80000000 # aio completion for dns; the horror
536-
syscon sicode SI_KERNEL 0x80 0x80000000 0x010006 0x80000000 0x80000000 0x80 # wut; openbsd defines as si_code>0
536+
syscon sicode SI_KERNEL 128 0x80000000 0x010006 0x80000000 0x80000000 0x80 # wut; openbsd defines as si_code>0
537537
syscon sicode SI_NOINFO 32767 0x80000000 0 32767 32767 32767 # no signal specific info available
538538
syscon sicode CLD_EXITED 1 1 1 1 1 1 # SIGCHLD; child exited; unix consensus
539539
syscon sicode CLD_KILLED 2 2 2 2 2 2 # SIGCHLD; child terminated w/o core; unix consensus

libc/sysv/syscalls.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ scall __sys_wait4 0x1c100b007200703d globl hidden
9898
scall sys_kill 0x02507a025202503e globl hidden # kill(pid, sig, 1) b/c xnu
9999
scall sys_killpg 0x092fff092fffffff globl hidden
100100
scall sys_clone 0x11fffffffffff038 globl hidden
101-
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
101+
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
102102
scall sys_futex 0x0a6053fffffff0ca globl hidden # raises SIGSYS on NetBSD
103103
scall set_robust_list 0x0a7ffffffffff111 globl
104104
scall get_robust_list 0x0a8ffffffffff112 globl

libc/thread/xnu.internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ int bsdthread_create(void *func, void *func_arg, void *stack, void *pthread,
1212
uint32_t flags);
1313
int bsdthread_terminate(void *stackaddr, size_t freesize, uint32_t port,
1414
uint32_t sem);
15+
int __pthread_kill(uint32_t port, int sig);
1516
int bsdthread_register(
1617
void (*threadstart)(void *pthread, int machport, void *(*func)(void *),
1718
void *arg, intptr_t *, unsigned),

test/libc/calls/raise_test.c

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
2+
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
3+
╞══════════════════════════════════════════════════════════════════════════════╡
4+
│ Copyright 2022 Justine Alexandra Roberts Tunney │
5+
│ │
6+
│ Permission to use, copy, modify, and/or distribute this software for │
7+
│ any purpose with or without fee is hereby granted, provided that the │
8+
│ above copyright notice and this permission notice appear in all copies. │
9+
│ │
10+
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
11+
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
12+
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
13+
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
14+
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
15+
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
16+
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
17+
│ PERFORMANCE OF THIS SOFTWARE. │
18+
╚─────────────────────────────────────────────────────────────────────────────*/
19+
#include "libc/calls/calls.h"
20+
#include "libc/calls/struct/sigaction.h"
21+
#include "libc/calls/struct/siginfo.h"
22+
#include "libc/dce.h"
23+
#include "libc/sysv/consts/sa.h"
24+
#include "libc/sysv/consts/sicode.h"
25+
#include "libc/sysv/consts/sig.h"
26+
#include "libc/testlib/subprocess.h"
27+
#include "libc/testlib/testlib.h"
28+
#include "libc/thread/spawn.h"
29+
30+
////////////////////////////////////////////////////////////////////////////////
31+
// SIGTRAP
32+
33+
TEST(raise, trap_sysv) {
34+
if (IsWindows()) return;
35+
signal(SIGTRAP, SIG_DFL);
36+
SPAWN(fork);
37+
raise(SIGTRAP);
38+
TERMS(SIGTRAP);
39+
}
40+
41+
TEST(raise, trap_windows) {
42+
if (!IsWindows()) return;
43+
signal(SIGTRAP, SIG_DFL);
44+
SPAWN(fork);
45+
raise(SIGTRAP);
46+
EXITS(128 + SIGTRAP);
47+
}
48+
49+
////////////////////////////////////////////////////////////////////////////////
50+
// SIGFPE
51+
52+
TEST(raise, fpe_sysv) {
53+
if (IsWindows()) return;
54+
signal(SIGFPE, SIG_DFL);
55+
SPAWN(fork);
56+
raise(SIGFPE);
57+
TERMS(SIGFPE);
58+
}
59+
60+
TEST(raise, fpe_windows) {
61+
if (!IsWindows()) return;
62+
signal(SIGFPE, SIG_DFL);
63+
SPAWN(fork);
64+
raise(SIGFPE);
65+
EXITS(128 + SIGFPE);
66+
}
67+
68+
////////////////////////////////////////////////////////////////////////////////
69+
// SIGUSR1
70+
71+
TEST(raise, usr1_sysv) {
72+
if (IsWindows()) return;
73+
SPAWN(fork);
74+
raise(SIGUSR1);
75+
TERMS(SIGUSR1);
76+
}
77+
78+
TEST(raise, usr1_windows) {
79+
if (!IsWindows()) return;
80+
SPAWN(fork);
81+
raise(SIGUSR1);
82+
EXITS(128 + SIGUSR1);
83+
}
84+
85+
////////////////////////////////////////////////////////////////////////////////
86+
// THREADS
87+
88+
int threadid;
89+
90+
void WorkerQuit(int sig, siginfo_t *si, void *ctx) {
91+
ASSERT_EQ(SIGILL, sig);
92+
if (!IsXnu() && !IsOpenbsd()) {
93+
ASSERT_EQ(SI_TKILL, si->si_code);
94+
}
95+
ASSERT_EQ(threadid, gettid());
96+
}
97+
98+
int Worker(void *arg, int tid) {
99+
struct sigaction sa = {.sa_sigaction = WorkerQuit, .sa_flags = SA_SIGINFO};
100+
ASSERT_EQ(0, sigaction(SIGILL, &sa, 0));
101+
threadid = tid;
102+
ASSERT_EQ(0, raise(SIGILL));
103+
return 0;
104+
}
105+
106+
TEST(raise, threaded) {
107+
signal(SIGILL, SIG_DFL);
108+
struct spawn worker;
109+
ASSERT_SYS(0, 0, _spawn(Worker, 0, &worker));
110+
ASSERT_SYS(0, 0, _join(&worker));
111+
}

0 commit comments

Comments
 (0)