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>
This commit is contained in:
Denys Vlasenko
2021-06-05 16:20:05 +02:00
parent d3e1090308
commit 5dadd497ff
6 changed files with 27 additions and 18 deletions

View File

@@ -149,11 +149,15 @@ static void warn_cannot(const char *m)
warn2_cannot(m, "");
}
/* SIGCHLD/TERM handlers are reentrancy-safe because they are unmasked
* only over poll() call, not over memory allocations
* or printouts. Do not need to save/restore errno either,
* as poll() error is not checked there.
*/
static void s_child(int sig_no UNUSED_PARAM)
{
write(selfpipe.wr, "", 1);
}
static void s_term(int sig_no UNUSED_PARAM)
{
sigterm = 1;
@@ -380,14 +384,14 @@ static void startservice(struct svdir *s)
xdup2(logpipe.wr, 1);
}
}
/* Non-ignored signals revert to SIG_DFL on exec anyway.
* But we can get signals BEFORE execl(), this is unlikely
* but wouldn't be good...
/* Non-ignored signals revert to SIG_DFL on exec.
* But we can get signals BEFORE execl(), unlikely as that may be.
* SIGCHLD is safe (would merely write to selfpipe),
* but SIGTERM would set sigterm = 1 (with vfork, we affect parent).
* Avoid that.
*/
/*bb_signals(0
+ (1 << SIGCHLD)
+ (1 << SIGTERM)
, SIG_DFL);*/
/*signal(SIGCHLD, SIG_DFL);*/
signal(SIGTERM, SIG_DFL);
sig_unblock(SIGCHLD);
sig_unblock(SIGTERM);
execv(arg[0], (char**) arg);
@@ -514,9 +518,13 @@ int runsv_main(int argc UNUSED_PARAM, char **argv)
ndelay_on(selfpipe.wr);
sig_block(SIGCHLD);
bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
sig_block(SIGTERM);
bb_signals_recursive_norestart(1 << SIGTERM, s_term);
/* No particular reason why we don't set SA_RESTART
* (poll() wouldn't restart regardless of that flag),
* we just follow what runit-2.1.2 does:
*/
bb_signals_norestart(1 << SIGCHLD, s_child);
bb_signals_norestart(1 << SIGTERM, s_term);
xchdir(dir);
/* bss: svd[0].pid = 0; */
@@ -628,6 +636,7 @@ int runsv_main(int argc UNUSED_PARAM, char **argv)
sig_unblock(SIGTERM);
sig_unblock(SIGCHLD);
poll(x, 2 + haslog, 3600*1000);
/* NB: signal handlers can trash errno of poll() */
sig_block(SIGTERM);
sig_block(SIGCHLD);

View File

@@ -1111,10 +1111,10 @@ int svlogd_main(int argc, char **argv)
sigaddset(&blocked_sigset, SIGALRM);
sigaddset(&blocked_sigset, SIGHUP);
sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
bb_signals_recursive_norestart(1 << SIGTERM, sig_term_handler);
bb_signals_recursive_norestart(1 << SIGCHLD, sig_child_handler);
bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler);
bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler);
bb_signals_norestart(1 << SIGTERM, sig_term_handler);
bb_signals_norestart(1 << SIGCHLD, sig_child_handler);
bb_signals_norestart(1 << SIGALRM, sig_alarm_handler);
bb_signals_norestart(1 << SIGHUP, sig_hangup_handler);
/* Without timestamps, we don't have to print each line
* separately, so we can look for _last_ newline, not first,