Separate child process sync from user namespace

Syncing the child and parent was only done so that the uid/gid maps
could be setup.  Make this more general so that the next commit can add setting
of cgroups which also wants to happen after the child forks but before the
jailed process is run similar top uid/gid map setting.

BUG=b/26549867
TEST=security_Minijail0

Change-Id: I81d512f351cfe459cd7af4c55263504d22b929fa
Signed-off-by: Dylan Reid <dgreid@chromium.org>
diff --git a/libminijail.c b/libminijail.c
index 3caafa4..e1d5cb5 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -871,12 +871,11 @@
 	return ret;
 }
 
-static void write_ugid_mappings(const struct minijail *j, int *pipe_fds)
+static void write_ugid_mappings(const struct minijail *j)
 {
 	int fd, ret, len;
 	size_t sz;
 	char fname[32];
-	close(pipe_fds[0]);
 
 	sz = sizeof(fname);
 	if (j->uidmap) {
@@ -903,21 +902,32 @@
 			die("failed to set gid_map");
 		close(fd);
 	}
+}
 
+static void parent_setup_complete(int *pipe_fds)
+{
+	close(pipe_fds[0]);
 	close(pipe_fds[1]);
 }
 
-static void enter_user_namespace(const struct minijail *j, int *pipe_fds)
+/*
+ * wait_for_parent_setup: Called by the child process to wait for any
+ * further parent-side setup to complete before continuing.
+ */
+static void wait_for_parent_setup(int *pipe_fds)
 {
 	char buf;
 
 	close(pipe_fds[1]);
 
-	/* Wait for parent to set up uid/gid mappings. */
+	/* Wait for parent to complete setup and close the pipe. */
 	if (read(pipe_fds[0], &buf, 1) != 0)
 		die("failed to sync with parent");
 	close(pipe_fds[0]);
+}
 
+static void enter_user_namespace(const struct minijail *j)
+{
 	if (j->uidmap && setresuid(0, 0, 0))
 		pdie("setresuid");
 	if (j->gidmap && setresgid(0, 0, 0))
@@ -1546,7 +1556,8 @@
 	int stdin_fds[2];
 	int stdout_fds[2];
 	int stderr_fds[2];
-	int userns_pipe_fds[2];
+	int child_sync_pipe_fds[2];
+	int sync_child = 0;
 	int ret;
 	/* We need to remember this across the minijail_preexec() call. */
 	int pid_namespace = j->flags.pids;
@@ -1624,7 +1635,8 @@
 	 * create the pipe(2) to sync between parent and child.
 	 */
 	if (j->flags.userns) {
-		if (pipe(userns_pipe_fds))
+		sync_child = 1;
+		if (pipe(child_sync_pipe_fds))
 			return -EFAULT;
 	}
 
@@ -1703,7 +1715,10 @@
 			write_pid_file(j);
 
 		if (j->flags.userns)
-			write_ugid_mappings(j, userns_pipe_fds);
+			write_ugid_mappings(j);
+
+		if (sync_child)
+			parent_setup_complete(child_sync_pipe_fds);
 
 		if (use_preload) {
 			/* Send marshalled minijail. */
@@ -1755,8 +1770,11 @@
 			pdie("sigprocmask failed");
 	}
 
+	if (sync_child)
+		wait_for_parent_setup(child_sync_pipe_fds);
+
 	if (j->flags.userns)
-		enter_user_namespace(j, userns_pipe_fds);
+		enter_user_namespace(j);
 
 	/*
 	 * If we want to write to the jailed process' standard input,