From: Lennart Poettering Date: Thu, 22 Jun 2023 08:21:32 +0000 (+0200) Subject: basic: add comments about raw_clone() calls not supporting threads/malloc in child X-Git-Tag: v254-rc1~133^2~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=01ab446c35816ac17d63cf3b99367b8016856d5b;p=thirdparty%2Fsystemd.git basic: add comments about raw_clone() calls not supporting threads/malloc in child --- diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 920074815e9..41432a85658 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -141,6 +141,11 @@ int must_be_root(void); pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata); +/* 💣 Note that FORK_NEW_USERNS + FORK_NEW_MOUNTNS should not be called in threaded programs, because they + * cause us to use raw_clone() which does not synchronize the glibc malloc() locks, and thus will cause + * deadlocks if the parent uses threads and the child does memory allocations. Hence: if the parent is + * threaded these flags may not be used. These flags cannot be used if the parent uses threads or the child + * uses malloc(). 💣 */ 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 */ @@ -150,13 +155,13 @@ typedef enum ForkFlags { 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 */ + 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 */ + 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. */ } ForkFlags; diff --git a/src/basic/raw-clone.h b/src/basic/raw-clone.h index a3b768f8261..6de67ab752e 100644 --- a/src/basic/raw-clone.h +++ b/src/basic/raw-clone.h @@ -27,6 +27,11 @@ * Additionally, as this function does not pass the ptid, newtls and ctid parameters to the kernel, flags must not * contain CLONE_PARENT_SETTID, CLONE_CHILD_SETTID, CLONE_CHILD_CLEARTID or CLONE_SETTLS. * + * WARNING: 💣 this call (just like glibc's own clone() wrapper) will not synchronize on glibc's malloc + * locks, which means they will be in an undefined state in the child if the parent is + * threaded. This means: the parent must either never use threads, or the child cannot use memory + * allocation itself. This is a major pitfall, hence be careful! 💣 + * * Returns: 0 in the child process and the child process id in the parent. */ static inline pid_t raw_clone(unsigned long flags) {