]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
lxc: don't call pivot_root if / is on a ramfs
authorAndrey Vagin <avagin@gmail.com>
Sat, 4 Oct 2014 21:49:16 +0000 (01:49 +0400)
committerSerge Hallyn <serge.hallyn@ubuntu.com>
Wed, 8 Oct 2014 16:24:52 +0000 (11:24 -0500)
pivot_root can't be called if / is on a ramfs. Currently chroot is
called before pivot_root. In this case the standard well-known
'chroot escape' technique allows to escape a container.

I think the best way to handle this situation is to make following actions:
* clean all mounts, which should not be visible in CT
* move CT's rootfs into /
* make chroot into /

I don't have a host, where / is on a ramfs, so I can't test this patch.

Signed-off-by: Andrey Vagin <avagin@openvz.org>
Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
src/lxc/conf.c

index e8979c967daa497e65a5077bc6d37aaa6a6f55c5..366250333cca67480038d9c99128679ddf14fe1e 100644 (file)
@@ -1449,68 +1449,6 @@ int lxc_delete_autodev(struct lxc_handler *handler)
        return 0;
 }
 
-/*
- * I'll forgive you for asking whether all of this is needed :)  The
- * answer is yes.
- * pivot_root will fail if the new root, the put_old dir, or the parent
- * of current->fs->root are MS_SHARED.  (parent of current->fs_root may
- * or may not be current->fs_root - if we assumed it always was, we could
- * just mount --make-rslave /).  So,
- *    1. mount a tiny tmpfs to be parent of current->fs->root.
- *    2. make that MS_SLAVE
- *    3. make a 'root' directory under that
- *    4. mount --rbind / under the $tinyroot/root.
- *    5. make that rslave
- *    6. chdir and chroot into $tinyroot/root
- *    7. $tinyroot will be unmounted by our parent in start.c
- */
-static int chroot_into_slave(struct lxc_conf *conf)
-{
-       char path[MAXPATHLEN];
-       const char *destpath = conf->rootfs.mount;
-       int ret;
-
-       if (mount(destpath, destpath, NULL, MS_BIND, 0)) {
-               SYSERROR("failed to mount %s bind", destpath);
-               return -1;
-       }
-       if (mount("", destpath, NULL, MS_SLAVE, 0)) {
-               SYSERROR("failed to make %s slave", destpath);
-               return -1;
-       }
-       if (mount("none", destpath, "tmpfs", 0, "size=10000,mode=755")) {
-               SYSERROR("Failed to mount tmpfs / at %s", destpath);
-               return -1;
-       }
-       ret = snprintf(path, MAXPATHLEN, "%s/root", destpath);
-       if (ret < 0 || ret >= MAXPATHLEN) {
-               ERROR("out of memory making root path");
-               return -1;
-       }
-       if (mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) {
-               SYSERROR("Failed to create /dev/pts in container");
-               return -1;
-       }
-       if (mount("/", path, NULL, MS_BIND|MS_REC, 0)) {
-               SYSERROR("Failed to rbind mount / to %s", path);
-               return -1;
-       }
-       if (mount("", destpath, NULL, MS_SLAVE|MS_REC, 0)) {
-               SYSERROR("Failed to make tmp-/ at %s rslave", path);
-               return -1;
-       }
-       if (chroot(path)) {
-               SYSERROR("Failed to chroot into tmp-/");
-               return -1;
-       }
-       if (chdir("/")) {
-               SYSERROR("Failed to chdir into tmp-/");
-               return -1;
-       }
-       INFO("Chrooted into tmp-/ at %s", path);
-       return 0;
-}
-
 static int setup_rootfs(struct lxc_conf *conf)
 {
        const struct lxc_rootfs *rootfs = &conf->rootfs;
@@ -1548,12 +1486,106 @@ static int setup_rootfs(struct lxc_conf *conf)
        return 0;
 }
 
+int prepare_ramfs_root(char *root)
+{
+       char buf[LINELEN], *p;
+       char nroot[PATH_MAX];
+       FILE *f;
+       int i;
+       char *p2;
+
+       if (realpath(root, nroot) == NULL)
+               return -1;
+
+       if (chdir("/") == -1)
+               return -1;
+
+       /*
+        * We could use here MS_MOVE, but in userns this mount is
+        * locked and can't be moved.
+        */
+       if (mount(root, "/", NULL, MS_REC | MS_BIND, NULL)) {
+               SYSERROR("Failed to move %s into /", root);
+               return -1;
+       }
+
+       if (mount(".", NULL, NULL, MS_REC | MS_PRIVATE, NULL)) {
+               SYSERROR("Failed to make . rprivate");
+               return -1;
+       }
+
+       /*
+        * The following code cleans up inhereted mounts which are not
+        * required for CT.
+        *
+        * The mountinfo file shows not all mounts, if a few points have been
+        * unmounted between read operations from the mountinfo. So we need to
+        * read mountinfo a few times.
+        *
+        * This loop can be skipped if a container uses unserns, because all
+        * inherited mounts are locked and we should live with all this trash.
+        */
+       while (1) {
+               int progress = 0;
+
+               f = fopen("./proc/self/mountinfo", "r");
+               if (!f) {
+                       SYSERROR("Unable to open /proc/self/mountinfo");
+                       return -1;
+               }
+               while (fgets(buf, LINELEN, f)) {
+                       for (p = buf, i=0; p && i < 4; i++)
+                               p = strchr(p+1, ' ');
+                       if (!p)
+                               continue;
+                       p2 = strchr(p+1, ' ');
+                       if (!p2)
+                               continue;
+
+                       *p2 = '\0';
+                       *p = '.';
+
+                       if (strcmp(p + 1, "/") == 0)
+                               continue;
+                       if (strcmp(p + 1, "/proc") == 0)
+                               continue;
+
+                       if (umount2(p, MNT_DETACH) == 0)
+                               progress++;
+               }
+               fclose(f);
+               if (!progress)
+                       break;
+       }
+
+       if (umount2("./proc", MNT_DETACH)) {
+               SYSERROR("Unable to umount /proc");
+               return -1;
+       }
+
+       /* It is weird, but chdir("..") moves us in a new root */
+       if (chdir("..") == -1) {
+               SYSERROR("Unable to change working directory");
+               return -1;
+       }
+
+       if (chroot(".") == -1) {
+               SYSERROR("Unable to chroot");
+               return -1;
+       }
+
+       return 0;
+}
+
 static int setup_pivot_root(const struct lxc_rootfs *rootfs)
 {
        if (!rootfs->path)
                return 0;
 
-       if (setup_rootfs_pivot_root(rootfs->mount, rootfs->pivot)) {
+       if (detect_ramfs_rootfs()) {
+               if (prepare_ramfs_root(rootfs->mount))
+                       return -1;
+       } else if (setup_rootfs_pivot_root(rootfs->mount, rootfs->pivot)) {
                ERROR("failed to setup pivot root");
                return -1;
        }
@@ -3952,13 +3984,6 @@ int do_rootfs_setup(struct lxc_conf *conf, const char *name, const char *lxcpath
                }
        }
 
-       if (detect_ramfs_rootfs()) {
-               if (chroot_into_slave(conf)) {
-                       ERROR("Failed to chroot into slave /");
-                       return -1;
-               }
-       }
-
        remount_all_slave();
 
        if (run_lxc_hooks(name, "pre-mount", conf, lxcpath, NULL)) {