From 9520e361315bd07577286a9d0b5a23f114ea6313 Mon Sep 17 00:00:00 2001 From: Bhavik Sachdev Date: Wed, 9 Oct 2024 09:50:28 +0100 Subject: [PATCH] pidfd: block SIGCHLD during tmp process creation This patch blocks SIGCHLD during temporary process creation to prevent a race condition between kill() and waitpid() where sigchld_handler() causes `criu restore` to fail with an error. Fixes: #2490 Signed-off-by: Bhavik Sachdev Signed-off-by: Radostin Stoyanov --- criu/pidfd.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/criu/pidfd.c b/criu/pidfd.c index fdf5dec60e..eb46dd6c4c 100644 --- a/criu/pidfd.c +++ b/criu/pidfd.c @@ -27,6 +27,7 @@ struct dead_pidfd { unsigned int ino; int pid; size_t count; + sigset_t oldmask; mutex_t pidfd_lock; struct hlist_node hash; }; @@ -158,6 +159,9 @@ static int free_dead_pidfd(struct dead_pidfd *dead) goto err; } + /* Restore the original signal mask after tmp process has terminated */ + sigprocmask(SIG_SETMASK, &dead->oldmask, NULL); + if (!WIFSIGNALED(status)) { pr_err("Expected temporary process to be terminated by a signal\n"); goto err; @@ -180,6 +184,7 @@ static int open_one_pidfd(struct file_desc *d, int *new_fd) { struct pidfd_info *info; struct dead_pidfd *dead = NULL; + sigset_t blockmask; int pidfd; info = container_of(d, struct pidfd_info, d); @@ -199,6 +204,14 @@ static int open_one_pidfd(struct file_desc *d, int *new_fd) BUG_ON(dead->count == 0); dead->count--; if (dead->pid == -1) { + /* Block SIGCHLD to prevent interfereing from sigchld_handler() + * and to properly handle the tmp process termination without + * a race condition. A similar approach is used in cr_system(). + */ + sigemptyset(&blockmask); + sigaddset(&blockmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &blockmask, &dead->oldmask); + dead->pid = create_tmp_process(); if (dead->pid < 0) { mutex_unlock(&dead->pidfd_lock); @@ -270,6 +283,10 @@ static int collect_one_pidfd(void *obj, ProtobufCMessage *msg, struct cr_img *i) dead->ino = info->pidfe->ino; dead->count = 1; dead->pid = -1; + + /* Initialize oldmask to an empty signal set */ + sigemptyset(&dead->oldmask); + mutex_init(&dead->pidfd_lock); mutex_lock(dead_pidfd_hash_lock);