]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
process-util: synchronously wait for namespace_enter() before returning 40157/head
authorMike Yuan <me@yhndnzj.com>
Mon, 15 Dec 2025 15:07:17 +0000 (16:07 +0100)
committerMike Yuan <me@yhndnzj.com>
Sat, 20 Dec 2025 13:27:46 +0000 (14:27 +0100)
Additionally, report actual errors using errno pipe too.

src/basic/process-util.c

index 0a87c5ec770ffac0fee3cef5f91fb93354bd41be..f25f4b81e51d2e977444886b6b13d59d1ad49044 100644 (file)
@@ -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;
 }