allow bind mounts outside of chroots

If we're in a unique namespace, there's no reason to not allow bind
mounts.  These can be useful like when setting up a unique /dev, or
to make specific paths read-only for a daemon.

Bug: chromium:680859
Test: `sudo ./minijail0 -rvpd -b /dev/log,/dev/log /bin/logger asdf` works
Change-Id: I9dcb7f072dfdaee8b405e5d05e5d94ee999767e8
diff --git a/libminijail.c b/libminijail.c
index 0d83cb2..9ae1c87 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -192,6 +192,21 @@
 static void run_hooks_or_die(const struct minijail *j,
 			     minijail_hook_event_t event);
 
+static void free_mounts_list(struct minijail *j)
+{
+	while (j->mounts_head) {
+		struct mountpoint *m = j->mounts_head;
+		j->mounts_head = j->mounts_head->next;
+		free(m->data);
+		free(m->type);
+		free(m->dest);
+		free(m->src);
+		free(m);
+	}
+	// No need to clear mounts_head as we know it's NULL after the loop.
+	j->mounts_tail = NULL;
+}
+
 /*
  * Strip out flags meant for the parent.
  * We keep things that are not inherited across execve(2) (e.g. capabilities),
@@ -228,6 +243,7 @@
 	if (j->suppl_gid_list)
 		free(j->suppl_gid_list);
 	j->suppl_gid_list = NULL;
+	free_mounts_list(j);
 	memset(&j->flags, 0, sizeof(j->flags));
 	/* Now restore anything we meant to keep. */
 	j->flags.vfs = vfs;
@@ -1145,15 +1161,7 @@
 	return 0;
 
 bad_cgroups:
-	while (j->mounts_head) {
-		struct mountpoint *m = j->mounts_head;
-		j->mounts_head = j->mounts_head->next;
-		free(m->data);
-		free(m->type);
-		free(m->dest);
-		free(m->src);
-		free(m);
-	}
+	free_mounts_list(j);
 	for (i = 0; i < j->cgroup_count; ++i)
 		free(j->cgroups[i]);
 bad_mounts:
@@ -1356,7 +1364,7 @@
 		if (asprintf(&dest, "%s%s", dev_path, m->dest + 4) < 0)
 			return -ENOMEM;
 	} else {
-		if (asprintf(&dest, "%s%s", j->chrootdir, m->dest) < 0)
+		if (asprintf(&dest, "%s%s", j->chrootdir ?: "", m->dest) < 0)
 			return -ENOMEM;
 	}
 
@@ -1399,20 +1407,35 @@
 	return ret;
 }
 
-static int enter_chroot(const struct minijail *j, char *dev_path)
+static void process_mounts_or_die(const struct minijail *j)
 {
-	int ret;
+	/*
+	 * We have to mount /dev first in case there are bind mounts from
+	 * the original /dev into the new unique tmpfs one.
+	 */
+	char *dev_path = NULL;
+	if (j->flags.mount_dev && mount_dev(&dev_path))
+		pdie("mount_dev failed");
 
-	if (j->mounts_head && (ret = mount_one(j, j->mounts_head, dev_path)))
-		return ret;
+	if (j->mounts_head && mount_one(j, j->mounts_head, dev_path)) {
+		if (dev_path) {
+			int saved_errno = errno;
+			mount_dev_cleanup(dev_path);
+			errno = saved_errno;
+		}
+		pdie("mount_one failed");
+	}
 
 	/*
-	 * Once all bind mounts have been processed, but before we chroot,
-	 * move the temp dev to its final /dev home.
+	 * Once all bind mounts have been processed, move the temp dev to
+	 * its final /dev home.
 	 */
 	if (j->flags.mount_dev && mount_dev_finalize(j, dev_path))
-		return ret;
+		pdie("mount_dev_finalize failed");
+}
 
+static int enter_chroot(const struct minijail *j)
+{
 	run_hooks_or_die(j, MINIJAIL_HOOK_EVENT_PRE_CHROOT);
 
 	if (chroot(j->chrootdir))
@@ -1424,19 +1447,9 @@
 	return 0;
 }
 
-static int enter_pivot_root(const struct minijail *j, char *dev_path)
+static int enter_pivot_root(const struct minijail *j)
 {
-	int ret, oldroot, newroot;
-
-	if (j->mounts_head && (ret = mount_one(j, j->mounts_head, dev_path)))
-		return ret;
-
-	/*
-	 * Once all bind mounts have been processed, but before we pivot,
-	 * move the temp dev to its final /dev home.
-	 */
-	if (j->flags.mount_dev && mount_dev_finalize(j, dev_path))
-		return ret;
+	int oldroot, newroot;
 
 	run_hooks_or_die(j, MINIJAIL_HOOK_EVENT_PRE_CHROOT);
 
@@ -1970,33 +1983,14 @@
 			pdie("keyctl(KEYCTL_JOIN_SESSION_KEYRING) failed");
 	}
 
-	/*
-	 * This has to come before the chroot/pivot_root in case there are
-	 * bind mounts from /dev into the chroot dev.
-	 */
-	char *dev_path = NULL;
-	if (j->flags.mount_dev && mount_dev(&dev_path))
-		pdie("mount_dev");
+	/* We have to process all the mounts before we chroot/pivot_root. */
+	process_mounts_or_die(j);
 
-	if (j->flags.chroot && enter_chroot(j, dev_path)) {
-		if (dev_path)
-			mount_dev_cleanup(dev_path);
+	if (j->flags.chroot && enter_chroot(j))
 		pdie("chroot");
-	}
 
-	if (j->flags.pivot_root && enter_pivot_root(j, dev_path)) {
-		if (dev_path)
-			mount_dev_cleanup(dev_path);
+	if (j->flags.pivot_root && enter_pivot_root(j))
 		pdie("pivot_root");
-	}
-
-	/*
-	 * If using a chroot or pivot root, we already finalized /dev at
-	 * the right point.  If not, we need to call it ourselves.
-	 */
-	if (j->flags.mount_dev && !j->flags.chroot && !j->flags.pivot_root &&
-	    mount_dev_finalize(j, dev_path))
-		pdie("mount_dev_finalize");
 
 	if (j->flags.mount_tmp && mount_tmp(j))
 		pdie("mount_tmp");
@@ -2862,16 +2856,7 @@
 		free(j->filter_prog->filter);
 		free(j->filter_prog);
 	}
-	while (j->mounts_head) {
-		struct mountpoint *m = j->mounts_head;
-		j->mounts_head = j->mounts_head->next;
-		free(m->data);
-		free(m->type);
-		free(m->dest);
-		free(m->src);
-		free(m);
-	}
-	j->mounts_tail = NULL;
+	free_mounts_list(j);
 	while (j->hooks_head) {
 		struct hook *c = j->hooks_head;
 		j->hooks_head = c->next;