]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
attach: correctly handle namespace inheritance
authorChristian Brauner <christian.brauner@ubuntu.com>
Sun, 29 Oct 2017 11:19:43 +0000 (12:19 +0100)
committerChristian Brauner <christian.brauner@ubuntu.com>
Thu, 9 Nov 2017 00:15:44 +0000 (01:15 +0100)
When attaching to a container's namespaces we did not handle the case where we
inherited namespaces correctly. In essence, liblxc on start records the
namespaces the container was created with in the handler. But it only records
the clone flags that were passed to clone() and doesn't record the namespaces
we e.g. inherited from other containers. This means that attach only ever
attached to the clone flags. But this is only correct if all other namespaces
not recorded in the handler refer to the namespaces of the caller. However,
this need not be the case if the container has inherited namespaces from
another container. To handle this case we need to check whether caller and
container are in the same namespace. If they are, we know that things are all
good. If they aren't then we need to attach to these namespaces as well.

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
src/lxc/attach.c

index b11846ee92aa4359aaa9e9e34b7cf07c8c294750..fd36429b4a4d0490d8f1d25e08153573a4417cfe 100644 (file)
@@ -241,24 +241,89 @@ static void lxc_proc_put_context_info(struct lxc_proc_context_info *ctx)
        free(ctx);
 }
 
+/**
+ * in_same_namespace - Check whether two processes are in the same namespace.
+ * @pid1 - PID of the first process.
+ * @pid2 - PID of the second process.
+ * @ns   - Name of the namespace to check. Must correspond to one of the names
+ *         for the namespaces as shown in /proc/<pid/ns/
+ *
+ * If the two processes are not in the same namespace returns an fd to the
+ * namespace of the second process identified by @pid2. If the two processes are
+ * in the same namespace returns -EINVAL, -1 if an error occurred.
+ */
+static int in_same_namespace(pid_t pid1, pid_t pid2, const char *ns)
+{
+       int ns_fd1 = -1, ns_fd2 = -1, ret = -1;
+       struct stat ns_st1, ns_st2;
+
+       ns_fd1 = lxc_preserve_ns(pid1, ns);
+       if (ns_fd1 < 0)
+               goto out;
+
+       ns_fd2 = lxc_preserve_ns(pid2, ns);
+       if (ns_fd2 < 0)
+               goto out;
+
+       ret = fstat(ns_fd1, &ns_st1);
+       if (ret < 0)
+               goto out;
+
+       ret = fstat(ns_fd2, &ns_st2);
+       if (ret < 0)
+               goto out;
+
+       /* processes are in the same namespace */
+       ret = -EINVAL;
+       if ((ns_st1.st_dev == ns_st2.st_dev ) && (ns_st1.st_ino == ns_st2.st_ino))
+               goto out;
+
+       /* processes are in different namespaces */
+       ret = ns_fd2;
+       ns_fd2 = -1;
+
+out:
+
+       if (ns_fd1 >= 0)
+               close(ns_fd1);
+       if (ns_fd2 >= 0)
+               close(ns_fd2);
+
+       return ret;
+}
+
 static int lxc_attach_to_ns(pid_t pid, int which)
 {
        int fd[LXC_NS_MAX];
-       int i, j, saved_errno;
+       int i, j, ret, saved_errno;
 
-       if (access("/proc/self/ns", X_OK)) {
+       ret = access("/proc/self/ns", X_OK);
+       if (ret) {
                ERROR("Does this kernel version support namespaces?");
                return -1;
        }
 
        for (i = 0; i < LXC_NS_MAX; i++) {
+               fd[i] = -EINVAL;
+
                /* Ignore if we are not supposed to attach to that namespace. */
                if (which != -1 && !(which & ns_info[i].clone_flag)) {
-                       fd[i] = -1;
-                       continue;
+                       /* We likely inherited the namespace from someone. We
+                        * need to check whether we are already in the same
+                        * namespace. If we are then there's nothing for us to
+                        * do. If we are not then we need to attach to it.
+                        */
+                       fd[i] = in_same_namespace(getpid(), pid, ns_info[i].proc_name);
+                       /* we are in the same namespace */
+                       if (fd[i] == -EINVAL) {
+                               DEBUG("Inheriting %s namespace from %d",
+                                     ns_info[i].proc_name, pid);
+                               continue;
+                       }
                }
 
-               fd[i] = lxc_preserve_ns(pid, ns_info[i].proc_name);
+               if (fd[i] == -EINVAL)
+                       fd[i] = lxc_preserve_ns(pid, ns_info[i].proc_name);
                if (fd[i] < 0) {
                        saved_errno = errno;
 
@@ -269,8 +334,8 @@ static int lxc_attach_to_ns(pid_t pid, int which)
                                close(fd[j]);
 
                        errno = saved_errno;
-                       SYSERROR("Failed to open namespace: \"%s\".",
-                                ns_info[i].proc_name);
+                       SYSERROR("Failed to attach to %s namespace of %d",
+                                ns_info[i].proc_name, pid);
                        return -1;
                }
        }
@@ -286,12 +351,12 @@ static int lxc_attach_to_ns(pid_t pid, int which)
                                close(fd[j]);
 
                        errno = saved_errno;
-                       SYSERROR("Failed to attach to namespace \"%s\".",
-                                ns_info[i].proc_name);
+                       SYSERROR("Failed to attach to %s namespace of %d",
+                                ns_info[i].proc_name, pid);
                        return -1;
                }
 
-               DEBUG("Attached to namespace \"%s\".", ns_info[i].proc_name);
+               DEBUG("Attached to %s namespace of %d", ns_info[i].proc_name, pid);
 
                close(fd[i]);
        }