Skip to content

Commit aab180a

Browse files
authored
Implement default signal handlers (#20257)
This implements best-effort attempt to implement default signal handlers as per https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html: - Terminate action is mapped to `_Exit` aka `wasi_proc_exit` - variant of exit that doesn't run `atexit` handlers. - Abort action (terminate with core dump) is mapped to `__builtin_trap` aka Wasm `unreachable` instruction. - Process pause/continue actions are ignored as it's unclear what that would do in Wasm (maybe use Asyncify in the future?). Fixes #20254
1 parent 1960782 commit aab180a

File tree

5 files changed

+91
-9
lines changed

5 files changed

+91
-9
lines changed

system/lib/libc/raise.c

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,39 @@ extern sigset_t __sig_pending;
1717

1818
bool __sig_is_blocked(int sig);
1919

20+
// Default handler actions ~auto-generated from https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html.
21+
// Note that stop and continue actions are not supported and treated as ignored.
22+
23+
typedef enum {
24+
ACTION_IGNORE = 0,
25+
ACTION_TERMINATE,
26+
ACTION_ABORT,
27+
} default_action_t;
28+
29+
static default_action_t default_actions[_NSIG] = {
30+
[SIGABRT] = ACTION_ABORT,
31+
[SIGALRM] = ACTION_TERMINATE,
32+
[SIGBUS] = ACTION_ABORT,
33+
[SIGFPE] = ACTION_ABORT,
34+
[SIGHUP] = ACTION_TERMINATE,
35+
[SIGILL] = ACTION_ABORT,
36+
[SIGINT] = ACTION_TERMINATE,
37+
[SIGKILL] = ACTION_TERMINATE,
38+
[SIGPIPE] = ACTION_TERMINATE,
39+
[SIGQUIT] = ACTION_ABORT,
40+
[SIGSEGV] = ACTION_ABORT,
41+
[SIGTERM] = ACTION_TERMINATE,
42+
[SIGUSR1] = ACTION_TERMINATE,
43+
[SIGUSR2] = ACTION_TERMINATE,
44+
[SIGPOLL] = ACTION_TERMINATE,
45+
[SIGPROF] = ACTION_TERMINATE,
46+
[SIGSYS] = ACTION_ABORT,
47+
[SIGTRAP] = ACTION_ABORT,
48+
[SIGVTALRM] = ACTION_TERMINATE,
49+
[SIGXCPU] = ACTION_ABORT,
50+
[SIGXFSZ] = ACTION_ABORT,
51+
};
52+
2053
int raise(int sig) {
2154
if (__sig_is_blocked(sig)) {
2255
sigaddset(&__sig_pending, sig);
@@ -26,14 +59,25 @@ int raise(int sig) {
2659
siginfo_t t = {0};
2760
__sig_actions[sig].sa_sigaction(sig, &t, NULL);
2861
} else {
29-
if (__sig_actions[sig].sa_handler == SIG_DFL || __sig_actions[sig].sa_handler == SIG_IGN) {
30-
31-
return 0;
62+
void (*handler)(int) = __sig_actions[sig].sa_handler;
63+
if (handler == SIG_DFL) {
64+
switch (default_actions[sig]) {
65+
case ACTION_IGNORE:
66+
break;
67+
case ACTION_TERMINATE:
68+
// Intentionally exiting via a function that doesn't call atexit handlers.
69+
_Exit(128 + sig);
70+
case ACTION_ABORT:
71+
// TODO: should this integrate with `Module['onAbort'] once `abort` is migrated
72+
// to raise SIGABRT as per spec?
73+
__builtin_trap();
74+
}
75+
} else if (handler != SIG_IGN) {
76+
// Avoid a direct call to the handler, and instead call via JS so we can
77+
// avoid strict signature checking.
78+
// https://github.com/emscripten-core/posixtestsuite/issues/6
79+
__call_sighandler(__sig_actions[sig].sa_handler, sig);
3280
}
33-
// Avoid a direct call to the handler, and instead call via JS so we can
34-
// avoid strict signature checking.
35-
// https://github.com/emscripten-core/posixtestsuite/issues/6
36-
__call_sighandler(__sig_actions[sig].sa_handler, sig);
3781
}
3882
return 0;
3983
}

test/other/test_standalone_syscalls.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ int main() {
3737
// check we can call this, but the test doesn't check the output
3838
emscripten_get_now();
3939

40-
// This doesn't do anything because we have not handler registered, but it
40+
// This doesn't do anything because we have no handler registered, but it
4141
// verifies that `raise` can be included in the build without any non-standard
4242
// imports being generated.
43-
raise(SIGUSR1);
43+
raise(SIGCHLD);
4444
return 0;
4545
}

test/test_core.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import random
1111
import re
1212
import shutil
13+
import signal
1314
import sys
1415
import time
1516
import unittest
@@ -6179,6 +6180,19 @@ def test_sigalrm(self):
61796180
def test_signals(self):
61806181
self.do_core_test(test_file('test_signals.c'))
61816182

6183+
@parameterized({
6184+
'sigint': (signal.SIGINT, 128 + signal.SIGINT, True),
6185+
'sigabrt': (signal.SIGABRT, 7, False)
6186+
})
6187+
def test_sigaction_default(self, signal, exit_code, assert_identical):
6188+
self.set_setting('EXIT_RUNTIME')
6189+
self.do_core_test(
6190+
test_file('test_sigaction_default.c'),
6191+
args=[str(int(signal))],
6192+
assert_identical=assert_identical,
6193+
assert_returncode=exit_code
6194+
)
6195+
61826196
@no_windows('https://github.com/emscripten-core/emscripten/issues/8882')
61836197
@requires_node
61846198
def test_unistd_access(self):

test/test_sigaction_default.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2023 The Emscripten Authors. All rights reserved.
3+
* Emscripten is available under two separate licenses, the MIT license and the
4+
* University of Illinois/NCSA Open Source License. Both these licenses can be
5+
* found in the LICENSE file.
6+
*/
7+
8+
#include <stdio.h>
9+
#include <stdlib.h>
10+
#include <signal.h>
11+
#include <assert.h>
12+
13+
static void cleanExit() {
14+
puts("3");
15+
}
16+
17+
int main(int argc, char **argv) {
18+
atexit(cleanExit);
19+
puts("1");
20+
raise(atoi(argv[1]));
21+
puts("2");
22+
return 0;
23+
}

test/test_sigaction_default.out

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1

0 commit comments

Comments
 (0)