hgmo: update moz.build evaluation sandbox to work on CentOS 7 (bug 1263973); r?kang draft
authorGregory Szorc <gps@mozilla.com>
Fri, 21 Oct 2016 09:04:12 -0700
changeset 9741 af1b2f5182ea66e5be52ffd391a399dc60f7537b
parent 9737 45c5475395f70a1e7c99242bf86b5adece48d727
push id1306
push userbmo:gps@mozilla.com
push dateFri, 21 Oct 2016 19:29:27 +0000
reviewerskang
bugs1263973
hgmo: update moz.build evaluation sandbox to work on CentOS 7 (bug 1263973); r?kang When we upgraded hg.mozilla.org from CentOS 6 to CentOS 7, the moz.build evaluation sandbox stopped working. This commit is an attempt to port it to CentOS 7. One of the changes in CentOS 7 is many mounts are now marked as "shared" by default. Even though the child process has a new mounts namespace via CLONE_NS, unmounts in the child process were being propagated to the parent! This was resolved by recursively remounting / with MS_SLAVE to disassociate all mounts from the parent. CentOS 7 also introduced the /run/user/1000 mount, which is a tmpfs volume. We updated the unmounting code to handle this. MozReview-Commit-ID: CtFqoiq5qx6
testing/docker/builder-hgweb-chroot/mozbuild-eval.c
--- a/testing/docker/builder-hgweb-chroot/mozbuild-eval.c
+++ b/testing/docker/builder-hgweb-chroot/mozbuild-eval.c
@@ -84,16 +84,22 @@ static int call_mozbuildinfo(void* repo_
         return 1;
     }
     if (cgroup_attach_task(cg)) {
         fprintf(stderr, "unable to attach process to cgroup\n");
         return 1;
     }
     cgroup_free(&cg);
 
+    /* Mark everything as a slave so we don't propagate mount changes to real. */
+    if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) {
+        fprintf(stderr, "unable to mark mounts as slaves\n");
+        return 1;
+    }
+
     /* We have a separate mount namespace from the parent but we
      * inherited a copy. Clear out mounts because they only increase the
      * surface area for badness. */
     fmount = setmntent("/proc/mounts", "r");
     if (!fmount) {
         fprintf(stderr, "unable to open /proc/mounts\n");
         return 1;
     }
@@ -102,16 +108,29 @@ static int call_mozbuildinfo(void* repo_
         /* These can't be deleted during our first pass because there
          * are child mounts. */
         if (strcmp(mnt->mnt_dir, "/dev") == 0) {
             continue;
         }
         if (strcmp(mnt->mnt_dir, "/proc") == 0) {
             continue;
         }
+        /* We can't unmount /sys in first pass because cgroups are present. */
+        if (strcmp(mnt->mnt_dir, "/sys") == 0) {
+            continue;
+        }
+        /* Need to unmount cgroups namespaces first. */
+        if (strcmp(mnt->mnt_dir, "/sys/fs/cgroup") == 0) {
+            continue;
+        }
+        /* /run/user/1000 is mounted. Unmount /run explicitly later. */
+        if (strcmp(mnt->mnt_dir, "/run") == 0) {
+            continue;
+        }
+
         /* We need the root filesystem and the repo bind mount available
          * in order for the chroot to work. */
         if (strcmp(mnt->mnt_dir, "/") == 0) {
             continue;
         }
         if (strcmp(mnt->mnt_dir, "/repo_local/mozilla/chroot_mozbuild/repo/hg/mozilla") == 0) {
             continue;
         }
@@ -121,30 +140,46 @@ static int call_mozbuildinfo(void* repo_
             endmntent(fmount);
             return 1;
         }
     }
 
     /* Always returns 1. */
     endmntent(fmount);
 
-    /* Now unmount /dev and /proc since children should be gone. */
+    /* Now unmount skips since children should be gone. */
     if (umount2("/dev", 0)) {
         fprintf(stderr, "unable to unmount /dev\n");
         return 1;
     }
 
     /* It is especially important that proc is unmounted because
      * historically there have been a lot of root privilege escalation
      * bugs in procfs. */
     if (umount2("/proc", 0)) {
         fprintf(stderr, "unable to unmount /proc\n");
         return 1;
     }
 
+    /* /sys/fs/cgroup doesn't unmount cleanly, yielding EBUSY for unknown
+       reasons. MNT_DETACH works, however. */
+    if (umount2("/sys/fs/cgroup", MNT_DETACH)) {
+        fprintf(stderr, "unable to unmount /sys/fs/cgroup\n");
+        return 1;
+    }
+    if (umount2("/sys", 0)) {
+        fprintf(stderr, "unable to unmount /sys\n");
+        return 1;
+    }
+
+    if (umount2("/run", 0)) {
+        fprintf(stderr, "unable to unmount /run\n");
+        return 1;
+    }
+
     /* chdir() and chroot() go hand in hand. Otherwise child process can
      * get handle for things outside the chroot. */
     err = chdir(CHROOT);
     if (err) {
         fprintf(stderr, "unable to chdir: %d\n", err);
         return 1;
     }