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
--- 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;
}