diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2021-06-05 16:20:05 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2021-06-05 16:20:05 +0200 |
commit | 5dadd497ffd9835a2860cf89ad781d1b513803dc (patch) | |
tree | 7ad8eb72b3cd1b5a3d94cd6d2ead9e86f2779c0a | |
parent | d3e1090308b6d3c55e01a2000a743b73605ddd7f (diff) | |
download | busybox-w32-5dadd497ffd9835a2860cf89ad781d1b513803dc.tar.gz busybox-w32-5dadd497ffd9835a2860cf89ad781d1b513803dc.tar.bz2 busybox-w32-5dadd497ffd9835a2860cf89ad781d1b513803dc.zip |
runsv: robustify signal handling - SIGTERM to child between vfork and exec could mess things up
While at it, rename bb_signals_recursive_norestart() to bb_signals_norestart():
"recursive" was implying we are setting SA_NODEFER allowing signal handler
to be entered recursively, but we do not do that.
function old new delta
bb_signals_norestart - 70 +70
startservice 380 394 +14
bb_signals_recursive_norestart 70 - -70
------------------------------------------------------------------------------
(add/remove: 1/1 grow/shrink: 1/0 up/down: 84/-70) Total: 14 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | console-tools/showkey.c | 2 | ||||
-rw-r--r-- | include/libbb.h | 2 | ||||
-rw-r--r-- | libbb/signals.c | 2 | ||||
-rw-r--r-- | runit/runsv.c | 29 | ||||
-rw-r--r-- | runit/svlogd.c | 8 | ||||
-rw-r--r-- | sysklogd/klogd.c | 2 |
6 files changed, 27 insertions, 18 deletions
diff --git a/console-tools/showkey.c b/console-tools/showkey.c index 4d7a9b9e5..84eb38a0a 100644 --- a/console-tools/showkey.c +++ b/console-tools/showkey.c | |||
@@ -106,7 +106,7 @@ int showkey_main(int argc UNUSED_PARAM, char **argv) | |||
106 | xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)((option_mask32 & OPT_k) ? K_MEDIUMRAW : K_RAW)); | 106 | xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)((option_mask32 & OPT_k) ? K_MEDIUMRAW : K_RAW)); |
107 | 107 | ||
108 | // we should exit on any signal; signals should interrupt read | 108 | // we should exit on any signal; signals should interrupt read |
109 | bb_signals_recursive_norestart(BB_FATAL_SIGS, record_signo); | 109 | bb_signals_norestart(BB_FATAL_SIGS, record_signo); |
110 | 110 | ||
111 | // inform user that program ends after time of inactivity | 111 | // inform user that program ends after time of inactivity |
112 | printf(press_keys, "10s after last keypress"); | 112 | printf(press_keys, "10s after last keypress"); |
diff --git a/include/libbb.h b/include/libbb.h index 4c9c83bd1..a3f76a206 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -593,7 +593,7 @@ void bb_signals(int sigs, void (*f)(int)) FAST_FUNC; | |||
593 | /* Unlike signal() and bb_signals, sets handler with sigaction() | 593 | /* Unlike signal() and bb_signals, sets handler with sigaction() |
594 | * and in a way that while signal handler is run, no other signals | 594 | * and in a way that while signal handler is run, no other signals |
595 | * will be blocked; syscalls will not be restarted: */ | 595 | * will be blocked; syscalls will not be restarted: */ |
596 | void bb_signals_recursive_norestart(int sigs, void (*f)(int)) FAST_FUNC; | 596 | void bb_signals_norestart(int sigs, void (*f)(int)) FAST_FUNC; |
597 | /* syscalls like read() will be interrupted with EINTR: */ | 597 | /* syscalls like read() will be interrupted with EINTR: */ |
598 | void signal_no_SA_RESTART_empty_mask(int sig, void (*handler)(int)) FAST_FUNC; | 598 | void signal_no_SA_RESTART_empty_mask(int sig, void (*handler)(int)) FAST_FUNC; |
599 | /* syscalls like read() won't be interrupted (though select/poll will be): */ | 599 | /* syscalls like read() won't be interrupted (though select/poll will be): */ |
diff --git a/libbb/signals.c b/libbb/signals.c index d3d84ef6a..0bebc847d 100644 --- a/libbb/signals.c +++ b/libbb/signals.c | |||
@@ -56,7 +56,7 @@ void FAST_FUNC bb_signals(int sigs, void (*f)(int)) | |||
56 | } | 56 | } |
57 | } | 57 | } |
58 | 58 | ||
59 | void FAST_FUNC bb_signals_recursive_norestart(int sigs, void (*f)(int)) | 59 | void FAST_FUNC bb_signals_norestart(int sigs, void (*f)(int)) |
60 | { | 60 | { |
61 | int sig_no = 0; | 61 | int sig_no = 0; |
62 | int bit = 1; | 62 | int bit = 1; |
diff --git a/runit/runsv.c b/runit/runsv.c index 61ea240ff..7fad563f5 100644 --- a/runit/runsv.c +++ b/runit/runsv.c | |||
@@ -149,11 +149,15 @@ static void warn_cannot(const char *m) | |||
149 | warn2_cannot(m, ""); | 149 | warn2_cannot(m, ""); |
150 | } | 150 | } |
151 | 151 | ||
152 | /* SIGCHLD/TERM handlers are reentrancy-safe because they are unmasked | ||
153 | * only over poll() call, not over memory allocations | ||
154 | * or printouts. Do not need to save/restore errno either, | ||
155 | * as poll() error is not checked there. | ||
156 | */ | ||
152 | static void s_child(int sig_no UNUSED_PARAM) | 157 | static void s_child(int sig_no UNUSED_PARAM) |
153 | { | 158 | { |
154 | write(selfpipe.wr, "", 1); | 159 | write(selfpipe.wr, "", 1); |
155 | } | 160 | } |
156 | |||
157 | static void s_term(int sig_no UNUSED_PARAM) | 161 | static void s_term(int sig_no UNUSED_PARAM) |
158 | { | 162 | { |
159 | sigterm = 1; | 163 | sigterm = 1; |
@@ -380,14 +384,14 @@ static void startservice(struct svdir *s) | |||
380 | xdup2(logpipe.wr, 1); | 384 | xdup2(logpipe.wr, 1); |
381 | } | 385 | } |
382 | } | 386 | } |
383 | /* Non-ignored signals revert to SIG_DFL on exec anyway. | 387 | /* Non-ignored signals revert to SIG_DFL on exec. |
384 | * But we can get signals BEFORE execl(), this is unlikely | 388 | * But we can get signals BEFORE execl(), unlikely as that may be. |
385 | * but wouldn't be good... | 389 | * SIGCHLD is safe (would merely write to selfpipe), |
390 | * but SIGTERM would set sigterm = 1 (with vfork, we affect parent). | ||
391 | * Avoid that. | ||
386 | */ | 392 | */ |
387 | /*bb_signals(0 | 393 | /*signal(SIGCHLD, SIG_DFL);*/ |
388 | + (1 << SIGCHLD) | 394 | signal(SIGTERM, SIG_DFL); |
389 | + (1 << SIGTERM) | ||
390 | , SIG_DFL);*/ | ||
391 | sig_unblock(SIGCHLD); | 395 | sig_unblock(SIGCHLD); |
392 | sig_unblock(SIGTERM); | 396 | sig_unblock(SIGTERM); |
393 | execv(arg[0], (char**) arg); | 397 | execv(arg[0], (char**) arg); |
@@ -514,9 +518,13 @@ int runsv_main(int argc UNUSED_PARAM, char **argv) | |||
514 | ndelay_on(selfpipe.wr); | 518 | ndelay_on(selfpipe.wr); |
515 | 519 | ||
516 | sig_block(SIGCHLD); | 520 | sig_block(SIGCHLD); |
517 | bb_signals_recursive_norestart(1 << SIGCHLD, s_child); | ||
518 | sig_block(SIGTERM); | 521 | sig_block(SIGTERM); |
519 | bb_signals_recursive_norestart(1 << SIGTERM, s_term); | 522 | /* No particular reason why we don't set SA_RESTART |
523 | * (poll() wouldn't restart regardless of that flag), | ||
524 | * we just follow what runit-2.1.2 does: | ||
525 | */ | ||
526 | bb_signals_norestart(1 << SIGCHLD, s_child); | ||
527 | bb_signals_norestart(1 << SIGTERM, s_term); | ||
520 | 528 | ||
521 | xchdir(dir); | 529 | xchdir(dir); |
522 | /* bss: svd[0].pid = 0; */ | 530 | /* bss: svd[0].pid = 0; */ |
@@ -628,6 +636,7 @@ int runsv_main(int argc UNUSED_PARAM, char **argv) | |||
628 | sig_unblock(SIGTERM); | 636 | sig_unblock(SIGTERM); |
629 | sig_unblock(SIGCHLD); | 637 | sig_unblock(SIGCHLD); |
630 | poll(x, 2 + haslog, 3600*1000); | 638 | poll(x, 2 + haslog, 3600*1000); |
639 | /* NB: signal handlers can trash errno of poll() */ | ||
631 | sig_block(SIGTERM); | 640 | sig_block(SIGTERM); |
632 | sig_block(SIGCHLD); | 641 | sig_block(SIGCHLD); |
633 | 642 | ||
diff --git a/runit/svlogd.c b/runit/svlogd.c index 4490492e3..02c305696 100644 --- a/runit/svlogd.c +++ b/runit/svlogd.c | |||
@@ -1111,10 +1111,10 @@ int svlogd_main(int argc, char **argv) | |||
1111 | sigaddset(&blocked_sigset, SIGALRM); | 1111 | sigaddset(&blocked_sigset, SIGALRM); |
1112 | sigaddset(&blocked_sigset, SIGHUP); | 1112 | sigaddset(&blocked_sigset, SIGHUP); |
1113 | sigprocmask(SIG_BLOCK, &blocked_sigset, NULL); | 1113 | sigprocmask(SIG_BLOCK, &blocked_sigset, NULL); |
1114 | bb_signals_recursive_norestart(1 << SIGTERM, sig_term_handler); | 1114 | bb_signals_norestart(1 << SIGTERM, sig_term_handler); |
1115 | bb_signals_recursive_norestart(1 << SIGCHLD, sig_child_handler); | 1115 | bb_signals_norestart(1 << SIGCHLD, sig_child_handler); |
1116 | bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler); | 1116 | bb_signals_norestart(1 << SIGALRM, sig_alarm_handler); |
1117 | bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler); | 1117 | bb_signals_norestart(1 << SIGHUP, sig_hangup_handler); |
1118 | 1118 | ||
1119 | /* Without timestamps, we don't have to print each line | 1119 | /* Without timestamps, we don't have to print each line |
1120 | * separately, so we can look for _last_ newline, not first, | 1120 | * separately, so we can look for _last_ newline, not first, |
diff --git a/sysklogd/klogd.c b/sysklogd/klogd.c index 82596bc0b..df0edee0a 100644 --- a/sysklogd/klogd.c +++ b/sysklogd/klogd.c | |||
@@ -226,7 +226,7 @@ int klogd_main(int argc UNUSED_PARAM, char **argv) | |||
226 | 226 | ||
227 | signal(SIGHUP, SIG_IGN); | 227 | signal(SIGHUP, SIG_IGN); |
228 | /* We want klogd_read to not be restarted, thus _norestart: */ | 228 | /* We want klogd_read to not be restarted, thus _norestart: */ |
229 | bb_signals_recursive_norestart(BB_FATAL_SIGS, record_signo); | 229 | bb_signals_norestart(BB_FATAL_SIGS, record_signo); |
230 | 230 | ||
231 | syslog(LOG_NOTICE, "klogd started: %s", bb_banner); | 231 | syslog(LOG_NOTICE, "klogd started: %s", bb_banner); |
232 | 232 | ||