From: Mike Yuan Date: Mon, 15 Dec 2025 15:07:17 +0000 (+0100) Subject: process-util: synchronously wait for namespace_enter() before returning X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=73c40aa1f661e34251fe77bc38a6a87d762572a9;p=thirdparty%2Fsystemd.git process-util: synchronously wait for namespace_enter() before returning Additionally, report actual errors using errno pipe too. --- diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 0a87c5ec770..f25f4b81e51 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -1897,7 +1897,9 @@ int namespace_fork_full( int root_fd, PidRef *ret) { - int r; + _cleanup_(pidref_done_sigkill_wait) PidRef pidref_outer = PIDREF_NULL; + _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR; + int r, prio = FLAGS_SET(flags, FORK_LOG) ? LOG_ERR : LOG_DEBUG; /* This is much like safe_fork(), but forks twice, and joins the specified namespaces in the middle * process. This ensures that we are fully a member of the destination namespace, with pidns an all, so that @@ -1912,11 +1914,23 @@ int namespace_fork_full( assert((flags & (FORK_DETACH|FORK_FREEZE)) == 0); assert(!FLAGS_SET(flags, FORK_ALLOW_DLOPEN)); /* never allow loading shared library from another ns */ + /* We want read() to block as a synchronization point */ + assert_cc(sizeof(int) <= PIPE_BUF); + if (pipe2(errno_pipe_fd, O_CLOEXEC) < 0) + return log_full_errno(prio, errno, "Failed to create pipe: %m"); + r = pidref_safe_fork_full( outer_name, /* stdio_fds = */ NULL, /* except_fds = */ NULL, /* n_except_fds = */ 0, (flags|FORK_DEATHSIG_SIGKILL) & ~(FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE|FORK_NEW_USERNS|FORK_NEW_NETNS|FORK_NEW_PIDNS|FORK_CLOSE_ALL_FDS|FORK_PACK_FDS|FORK_CLOEXEC_OFF|FORK_RLIMIT_NOFILE_SAFE), - ret); + &pidref_outer); + if (r == -EPROTO && FLAGS_SET(flags, FORK_WAIT)) { + errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + + int k = read_errno(errno_pipe_fd[0]); + if (k < 0 && k != -EIO) + return k; + } if (r < 0) return r; if (r == 0) { @@ -1924,10 +1938,12 @@ int namespace_fork_full( /* Child */ + errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); + r = namespace_enter(pidns_fd, mntns_fd, netns_fd, userns_fd, root_fd); if (r < 0) { - log_full_errno(FLAGS_SET(flags, FORK_LOG) ? LOG_ERR : LOG_DEBUG, r, "Failed to join namespace: %m"); - _exit(EXIT_FAILURE); + log_full_errno(prio, r, "Failed to join namespace: %m"); + report_errno_and_exit(errno_pipe_fd[1], r); } /* We mask a few flags here that either make no sense for the grandchild, or that we don't have to do again */ @@ -1938,9 +1954,18 @@ int namespace_fork_full( flags & ~(FORK_WAIT|FORK_RESET_SIGNALS|FORK_REARRANGE_STDIO|FORK_FLUSH_STDIO|FORK_STDOUT_TO_STDERR), &pidref_inner); if (r < 0) - _exit(EXIT_FAILURE); + report_errno_and_exit(errno_pipe_fd[1], r); if (r == 0) { /* Child */ + + if (!FLAGS_SET(flags, FORK_CLOSE_ALL_FDS)) { + errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + pidref_done(&pidref_outer); + } else { + errno_pipe_fd[1] = -EBADF; + pidref_outer = PIDREF_NULL; + } + if (ret) *ret = TAKE_PIDREF(pidref_inner); return 0; @@ -1961,6 +1986,17 @@ int namespace_fork_full( _exit(r); } + errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + + r = read_errno(errno_pipe_fd[0]); + if (r < 0) + return r; /* the child logs about failures on its own, no need to duplicate here */ + + if (ret) + *ret = TAKE_PIDREF(pidref_outer); + else + pidref_done(&pidref_outer); /* disarm sigkill_wait */ + return 1; }