]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
unshare: Fix PID and TIME namespace persistence
authormichael-dev <michael-dev@fami-braun.de>
Wed, 15 Apr 2020 21:16:53 +0000 (23:16 +0200)
committerKarel Zak <kzak@redhat.com>
Thu, 30 Apr 2020 08:59:38 +0000 (10:59 +0200)
After unshare(...) is called, /proc/self/ns/pid does not change.
Instead, only /proc/self/ns/pid_for_children is affected. So bind-mounting
/proc/self/ns/pid results in the original namespace getting bind-mounted.

Fix this by instead bind-mounting ns/pid_for_children.

[kzak@redhat.com: - add ns/time_for_children
                  - remove C++ comments
                  - resolve commit conflicts]

Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
Signed-off-by: Karel Zak <kzak@redhat.com>
sys-utils/unshare.1
sys-utils/unshare.c

index 2e8d76c727c45481d02b0675dac7d40c34df527e..a58821921cc5be0661a12ccf913c78c589ac4c69 100644 (file)
@@ -20,6 +20,10 @@ Once a persistent \%namespace is no longer needed, it can be unpersisted with
 .BR umount (8).
 See the \fBEXAMPLES\fR section for more details.
 .PP
+.B unshare
+since util-linux version 2.36 uses /\fIproc/[pid]/ns/pid_for_children\fP and \fI/proc/[pid]/ns/time_for_children\fP
+files for persistent PID and TIME namespaces. This change requires Linux kernel 4.17 or newer.
+.PP
 The namespaces to be unshared are indicated via options.  Unshareable namespaces are:
 .TP
 .B mount namespace
index 6211aacb5c21ee15629a07cc0d79498f2e6e36d8..7005acfc45fbe3689af6599adf228411e492b33b 100644 (file)
@@ -64,9 +64,9 @@ static struct namespace_file {
        { .type = CLONE_NEWIPC,   .name = "ns/ipc"  },
        { .type = CLONE_NEWUTS,   .name = "ns/uts"  },
        { .type = CLONE_NEWNET,   .name = "ns/net"  },
-       { .type = CLONE_NEWPID,   .name = "ns/pid },
+       { .type = CLONE_NEWPID,   .name = "ns/pid_for_children" },
        { .type = CLONE_NEWNS,    .name = "ns/mnt"  },
-       { .type = CLONE_NEWTIME,  .name = "ns/time },
+       { .type = CLONE_NEWTIME,  .name = "ns/time_for_children" },
        { .name = NULL }
 };
 
@@ -400,6 +400,7 @@ int main(int argc, char *argv[])
        const char *procmnt = NULL;
        const char *newroot = NULL;
        const char *newdir = NULL;
+       pid_t pid_bind = 0;
        pid_t pid = 0;
        int fds[2];
        int status;
@@ -542,13 +543,37 @@ int main(int argc, char *argv[])
                        "unsharing of a time namespace (-t)"));
 
        if (npersists && (unshare_flags & CLONE_NEWNS))
-               bind_ns_files_from_child(&pid, fds);
+               bind_ns_files_from_child(&pid_bind, fds);
 
        if (-1 == unshare(unshare_flags))
                err(EXIT_FAILURE, _("unshare failed"));
 
-       if (npersists) {
-               if (pid && (unshare_flags & CLONE_NEWNS)) {
+       if (force_boottime)
+               settime(boottime, CLOCK_BOOTTIME);
+
+       if (force_monotonic)
+               settime(monotonic, CLOCK_MONOTONIC);
+
+       if (forkit) {
+               /* force child forking before mountspace binding
+                * so pid_for_children is populated */
+               pid = fork();
+
+               switch(pid) {
+               case -1:
+                       err(EXIT_FAILURE, _("fork failed"));
+               case 0: /* child */
+                       if (pid_bind && (unshare_flags & CLONE_NEWNS))
+                               close(fds[1]);
+                       break;
+               default: /* parent */
+                       break;
+               }
+       }
+
+       if (npersists && (pid || !forkit)) {
+               /* run in parent */
+               if (pid_bind && (unshare_flags & CLONE_NEWNS)) {
                        int rc;
                        char ch = PIPE_SYNC_BYTE;
 
@@ -559,7 +584,7 @@ int main(int argc, char *argv[])
 
                        /* wait for bind_ns_files_from_child() */
                        do {
-                               rc = waitpid(pid, &status, 0);
+                               rc = waitpid(pid_bind, &status, 0);
                                if (rc < 0) {
                                        if (errno == EINTR)
                                                continue;
@@ -574,29 +599,14 @@ int main(int argc, char *argv[])
                        bind_ns_files(getpid());
        }
 
-       if (force_boottime)
-               settime(boottime, CLOCK_BOOTTIME);
-
-       if (force_monotonic)
-               settime(monotonic, CLOCK_MONOTONIC);
-
-       if (forkit) {
-               pid = fork();
-
-               switch(pid) {
-               case -1:
-                       err(EXIT_FAILURE, _("fork failed"));
-               case 0: /* child */
-                       break;
-               default: /* parent */
-                       if (waitpid(pid, &status, 0) == -1)
-                               err(EXIT_FAILURE, _("waitpid failed"));
-                       if (WIFEXITED(status))
-                               return WEXITSTATUS(status);
-                       if (WIFSIGNALED(status))
-                               kill(getpid(), WTERMSIG(status));
-                       err(EXIT_FAILURE, _("child exit failed"));
-               }
+       if (pid) {
+               if (waitpid(pid, &status, 0) == -1)
+                       err(EXIT_FAILURE, _("waitpid failed"));
+               if (WIFEXITED(status))
+                       return WEXITSTATUS(status);
+               if (WIFSIGNALED(status))
+                       kill(getpid(), WTERMSIG(status));
+               err(EXIT_FAILURE, _("child exit failed"));
        }
 
        if (kill_child_signo != 0 && prctl(PR_SET_PDEATHSIG, kill_child_signo) < 0)