process-util: add new FORK_DEATHSIG_SIGKILL flag, rename FORK_DEATHSIG → FORK_DEATHSIG_SIGTERM
Sometimes it makes sense to hard kill a client if we die. Let's hence add a third FORK_DEATHSIG flag for this purpose: FORK_DEATHSIG_SIGKILL. To make things less confusing this also renames FORK_DEATHSIG to FORK_DEATHSIG_SIGTERM to make clear it sends SIGTERM. We already had FORK_DEATHSIG_SIGINT, hence this makes things nicely symmetric. A bunch of users are switched over for FORK_DEATHSIG_SIGKILL where we know it's safe to abort things abruptly. This should make some kernel cases more robust, since we cannot get confused by signal masks or such. While we are at it, also fix a bunch of bugs where we didn't take FORK_DEATHSIG_SIGINT into account in safe_fork()
This commit is contained in:
@@ -219,7 +219,7 @@ int userns_acquire(const char *uid_map, const char *gid_map) {
|
||||
* and then kills the process again. This way we have a userns fd that is not bound to any
|
||||
* process. We can use that for file system mounts and similar. */
|
||||
|
||||
r = safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_NEW_USERNS, &pid);
|
||||
r = safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS, &pid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
|
||||
@@ -1380,6 +1380,12 @@ pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata) {
|
||||
return pid;
|
||||
}
|
||||
|
||||
static int fork_flags_to_signal(ForkFlags flags) {
|
||||
return (flags & FORK_DEATHSIG_SIGTERM) ? SIGTERM :
|
||||
(flags & FORK_DEATHSIG_SIGINT) ? SIGINT :
|
||||
SIGKILL;
|
||||
}
|
||||
|
||||
int safe_fork_full(
|
||||
const char *name,
|
||||
const int stdio_fds[3],
|
||||
@@ -1409,9 +1415,10 @@ int safe_fork_full(
|
||||
fflush(stderr); /* This one shouldn't be necessary, stderr should be unbuffered anyway, but let's better be safe than sorry */
|
||||
}
|
||||
|
||||
if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) {
|
||||
/* We temporarily block all signals, so that the new child has them blocked initially. This way, we can
|
||||
* be sure that SIGTERMs are not lost we might send to the child. */
|
||||
if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT)) {
|
||||
/* We temporarily block all signals, so that the new child has them blocked initially. This
|
||||
* way, we can be sure that SIGTERMs are not lost we might send to the child. (Note that for
|
||||
* FORK_DEATHSIG_SIGKILL we don't bother, since it cannot be blocked anyway.) */
|
||||
|
||||
assert_se(sigfillset(&ss) >= 0);
|
||||
block_signals = block_all = true;
|
||||
@@ -1512,8 +1519,8 @@ int safe_fork_full(
|
||||
r, "Failed to rename process, ignoring: %m");
|
||||
}
|
||||
|
||||
if (flags & (FORK_DEATHSIG|FORK_DEATHSIG_SIGINT))
|
||||
if (prctl(PR_SET_PDEATHSIG, (flags & FORK_DEATHSIG_SIGINT) ? SIGINT : SIGTERM) < 0) {
|
||||
if (flags & (FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGKILL))
|
||||
if (prctl(PR_SET_PDEATHSIG, fork_flags_to_signal(flags)) < 0) {
|
||||
log_full_errno(prio, errno, "Failed to set death signal: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -1538,7 +1545,7 @@ int safe_fork_full(
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & FORK_DEATHSIG) {
|
||||
if (flags & (FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGKILL|FORK_DEATHSIG_SIGINT)) {
|
||||
pid_t ppid;
|
||||
/* Let's see if the parent PID is still the one we started from? If not, then the parent
|
||||
* already died by the time we set PR_SET_PDEATHSIG, hence let's emulate the effect */
|
||||
@@ -1547,8 +1554,9 @@ int safe_fork_full(
|
||||
if (ppid == 0)
|
||||
/* Parent is in a different PID namespace. */;
|
||||
else if (ppid != original_pid) {
|
||||
log_debug("Parent died early, raising SIGTERM.");
|
||||
(void) raise(SIGTERM);
|
||||
int sig = fork_flags_to_signal(flags);
|
||||
log_debug("Parent died early, raising %s.", signal_to_string(sig));
|
||||
(void) raise(sig);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
@@ -1664,7 +1672,7 @@ int namespace_fork(
|
||||
r = safe_fork_full(outer_name,
|
||||
NULL,
|
||||
except_fds, n_except_fds,
|
||||
(flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
|
||||
(flags|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGKILL) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
|
||||
+16
-15
@@ -160,22 +160,23 @@ pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata);
|
||||
typedef enum ForkFlags {
|
||||
FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */
|
||||
FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */
|
||||
FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child to SIGTERM */
|
||||
FORK_DEATHSIG_SIGTERM = 1 << 2, /* Set PR_DEATHSIG in the child to SIGTERM */
|
||||
FORK_DEATHSIG_SIGINT = 1 << 3, /* Set PR_DEATHSIG in the child to SIGINT */
|
||||
FORK_REARRANGE_STDIO = 1 << 4, /* Connect 0,1,2 to specified fds or /dev/null */
|
||||
FORK_REOPEN_LOG = 1 << 5, /* Reopen log connection */
|
||||
FORK_LOG = 1 << 6, /* Log above LOG_DEBUG log level about failures */
|
||||
FORK_WAIT = 1 << 7, /* Wait until child exited */
|
||||
FORK_NEW_MOUNTNS = 1 << 8, /* Run child in its own mount namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
|
||||
FORK_MOUNTNS_SLAVE = 1 << 9, /* Make child's mount namespace MS_SLAVE */
|
||||
FORK_PRIVATE_TMP = 1 << 10, /* Mount new /tmp/ in the child (combine with FORK_NEW_MOUNTNS!) */
|
||||
FORK_RLIMIT_NOFILE_SAFE = 1 << 11, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
|
||||
FORK_STDOUT_TO_STDERR = 1 << 12, /* Make stdout a copy of stderr */
|
||||
FORK_FLUSH_STDIO = 1 << 13, /* fflush() stdout (and stderr) before forking */
|
||||
FORK_NEW_USERNS = 1 << 14, /* Run child in its own user namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
|
||||
FORK_CLOEXEC_OFF = 1 << 15, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */
|
||||
FORK_KEEP_NOTIFY_SOCKET = 1 << 16, /* Unless this specified, $NOTIFY_SOCKET will be unset. */
|
||||
FORK_DETACH = 1 << 17, /* Double fork if needed to ensure PID1/subreaper is parent */
|
||||
FORK_DEATHSIG_SIGKILL = 1 << 4, /* Set PR_DEATHSIG in the child to SIGKILL */
|
||||
FORK_REARRANGE_STDIO = 1 << 5, /* Connect 0,1,2 to specified fds or /dev/null */
|
||||
FORK_REOPEN_LOG = 1 << 6, /* Reopen log connection */
|
||||
FORK_LOG = 1 << 7, /* Log above LOG_DEBUG log level about failures */
|
||||
FORK_WAIT = 1 << 8, /* Wait until child exited */
|
||||
FORK_NEW_MOUNTNS = 1 << 9, /* Run child in its own mount namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
|
||||
FORK_MOUNTNS_SLAVE = 1 << 10, /* Make child's mount namespace MS_SLAVE */
|
||||
FORK_PRIVATE_TMP = 1 << 11, /* Mount new /tmp/ in the child (combine with FORK_NEW_MOUNTNS!) */
|
||||
FORK_RLIMIT_NOFILE_SAFE = 1 << 12, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
|
||||
FORK_STDOUT_TO_STDERR = 1 << 13, /* Make stdout a copy of stderr */
|
||||
FORK_FLUSH_STDIO = 1 << 14, /* fflush() stdout (and stderr) before forking */
|
||||
FORK_NEW_USERNS = 1 << 15, /* Run child in its own user namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
|
||||
FORK_CLOEXEC_OFF = 1 << 16, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */
|
||||
FORK_KEEP_NOTIFY_SOCKET = 1 << 17, /* Unless this specified, $NOTIFY_SOCKET will be unset. */
|
||||
FORK_DETACH = 1 << 18, /* Double fork if needed to ensure PID1/subreaper is parent */
|
||||
} ForkFlags;
|
||||
|
||||
int safe_fork_full(
|
||||
|
||||
@@ -1204,7 +1204,7 @@ int openpt_allocate_in_namespace(pid_t pid, int flags, char **ret_slave) {
|
||||
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
|
||||
return -errno;
|
||||
|
||||
r = namespace_fork("(sd-openptns)", "(sd-openpt)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
|
||||
r = namespace_fork("(sd-openptns)", "(sd-openpt)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
|
||||
pidnsfd, mntnsfd, -1, usernsfd, rootfd, &child);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -1255,7 +1255,7 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
|
||||
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
|
||||
return -errno;
|
||||
|
||||
r = namespace_fork("(sd-terminalns)", "(sd-terminal)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
|
||||
r = namespace_fork("(sd-terminalns)", "(sd-terminal)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
|
||||
pidnsfd, mntnsfd, -1, usernsfd, rootfd, &child);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1044,7 +1044,7 @@ int parse_timestamp(const char *t, usec_t *ret) {
|
||||
if (shared == MAP_FAILED)
|
||||
return negative_errno();
|
||||
|
||||
r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL);
|
||||
r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_WAIT, NULL);
|
||||
if (r < 0) {
|
||||
(void) munmap(shared, sizeof *shared);
|
||||
return r;
|
||||
|
||||
Reference in New Issue
Block a user