1
0
mirror of https://github.com/systemd/systemd synced 2024-07-03 08:29:25 +00:00

async: make sure to set argv[0][0] to '@' to avoid being killed in killing spree

Prompted by: #32223
This commit is contained in:
Lennart Poettering 2024-04-11 15:57:01 +02:00
parent fecea05e15
commit 319c8e3189
3 changed files with 40 additions and 5 deletions

View File

@ -1457,11 +1457,20 @@ int safe_fork_full(
sigset_t saved_ss, ss;
_unused_ _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL;
bool block_signals = false, block_all = false, intermediary = false;
_cleanup_free_ char *prefixed_name = NULL;
int prio, r;
assert(!FLAGS_SET(flags, FORK_DETACH) || !ret_pid);
assert(!FLAGS_SET(flags, FORK_DETACH|FORK_WAIT));
if (name && FLAGS_SET(flags, FORK_ARGV00_AT)) {
/* Optionally, ensure argv[0][0] is '@', in order to implement https://systemd.io/ROOT_STORAGE_DAEMONS */
prefixed_name = strjoin("@", name);
if (!prefixed_name)
return -ENOMEM;
name = prefixed_name;
}
/* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always
* returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */

View File

@ -186,6 +186,7 @@ typedef enum ForkFlags {
FORK_DETACH = 1 << 18, /* Double fork if needed to ensure PID1/subreaper is parent */
FORK_NEW_NETNS = 1 << 19, /* Run child in its own network namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
FORK_PACK_FDS = 1 << 20, /* Rearrange the passed FDs to be FD 3,4,5,etc. Updates the array in place (combine with FORK_CLOSE_ALL_FDS!) */
FORK_ARGV00_AT = 1 << 21, /* Prefix supplied name with '@', to exclude child process from final killing spree */
} ForkFlags;
int safe_fork_full(

View File

@ -6,6 +6,7 @@
#include <sys/wait.h>
#include <unistd.h>
#include "argv-util.h"
#include "async.h"
#include "errno-util.h"
#include "fd-util.h"
@ -22,7 +23,14 @@ int asynchronous_sync(pid_t *ret_pid) {
* original process ever, and a thread would do that as the process can't exit with threads hanging in blocking
* syscalls. */
r = safe_fork("(sd-sync)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|(ret_pid ? 0 : FORK_DETACH), ret_pid);
/* Note that we set argv[0][0] = '@' here if we are called from PID 1, which tells the
* switch-root/soft-reboot/shutdown killing spree to leave this process around. After all the killing
* is likely not going to work anyway, and given the limited scope of the child it's really not worth
* killing it. */
r = safe_fork("(sd-sync)",
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|(ret_pid ? 0 : FORK_DETACH)|(getpid_cached() == 1 ? FORK_ARGV00_AT : 0),
ret_pid);
if (r < 0)
return r;
if (r == 0) {
@ -41,12 +49,23 @@ int asynchronous_sync(pid_t *ret_pid) {
static int close_func(void *p) {
unsigned v = PTR_TO_UINT(p);
(void) prctl(PR_SET_NAME, (unsigned long*) "(sd-close)");
/* Note: 💣 This function is invoked in a child process created via glibc's clone() wrapper. In such
* children memory allocation is not allowed, since glibc does not release malloc mutexes in
* clone() 💣 */
/* This is a poor man's version of rename_process(). We don't use the real thing to avoid any memory
* allocations. We set argv[0][0] to '@' if invoked by PID 1 to exclude us from the
* switch-root/soft-reboot/shutdown killing spree, since it would likely fail anyway, and with a
* process of such minimal scope is not really worh the effort anyway. */
const char *comm;
if (getppid() == 1) {
if (saved_argc >= 1 && !isempty(saved_argv[0]))
saved_argv[0][0] = '@';
comm = "@(sd-close)";
} else
comm = "(sd-close)";
(void) prctl(PR_SET_NAME, comm);
if (v & NEED_DOUBLE_FORK) {
pid_t pid;
@ -117,9 +136,15 @@ int asynchronous_rm_rf(const char *p, RemoveFlags flags) {
assert(p);
/* Forks off a child that destroys the specified path. This will be best effort only, i.e. the child
* will attempt to do its thing, but we won't wait for it or check its success. */
* will attempt to do its thing, but we won't wait for it or check its success.
*
* We do set argv[0][0] = '@' here however (if we are invoked as PID 1), to ensure that the this is
* excluded from the switch-root/soft-reboot/poweroff killing spree, and can definitely complete it's
* job. */
r = safe_fork("(sd-rmrf)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DETACH, NULL);
r = safe_fork("(sd-rmrf)",
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DETACH|(getpid_cached() == 1 ? FORK_ARGV00_AT : 0),
/* ret_pid= */ NULL);
if (r != 0)
return r;