From 8fd65351a7d1e40bbd2bd471b02e85937c9b2f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Thu, 22 Apr 2021 17:48:53 +0200 Subject: [PATCH] zed: protect against wait4()/fork() races to the launched process tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As soon as wait4() returns, fork() can immediately return with the same PID, and race to lock _launched_processes_lock, then try to add the new (duplicate) PID to _launched_processes, which asserts By locking before wait4(), we ensure, that, given that same unfortunate scheduling, _launched_processes_lock cannot be locked by the spawner before we pop the process in the reaper, and only afterward will it be added This moves where the reaper idles when there are children from the wait4() to the pause(), locking for the duration of that single syscall in both the no-children and running-children cases; the impact of this is one to two syscalls (depending on _launched_processes_lock state) per loop Reviewed-by: Brian Behlendorf Reviewed-by: Don Brady Signed-off-by: Ahelenia ZiemiaƄska Closes #11924 Closes #11928 --- cmd/zed/zed_exec.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cmd/zed/zed_exec.c b/cmd/zed/zed_exec.c index 8c8452ca7..9b0775a74 100644 --- a/cmd/zed/zed_exec.c +++ b/cmd/zed/zed_exec.c @@ -205,10 +205,12 @@ _reap_children(void *arg) (void) sigaction(SIGCHLD, &sa, NULL); for (_reap_children_stop = B_FALSE; !_reap_children_stop; ) { - pid = wait4(0, &status, 0, &usage); + (void) pthread_mutex_lock(&_launched_processes_lock); + pid = wait4(0, &status, WNOHANG, &usage); - if (pid == (pid_t)-1) { - if (errno == ECHILD) + if (pid == 0 || pid == (pid_t)-1) { + (void) pthread_mutex_unlock(&_launched_processes_lock); + if (pid == 0 || errno == ECHILD) pause(); else if (errno != EINTR) zed_log_msg(LOG_WARNING, @@ -217,7 +219,6 @@ _reap_children(void *arg) } else { memset(&node, 0, sizeof (node)); node.pid = pid; - (void) pthread_mutex_lock(&_launched_processes_lock); pnode = avl_find(&_launched_processes, &node, NULL); if (pnode) { memcpy(&node, pnode, sizeof (node));