minijail0: fix multiple data options with the -k mount am: 4f3e09f23a am: e998c8fdc2
am: b94b51bf98

Change-Id: I2e0b5fee84c1bfde0c9c4da613830aa3c1ef8532
diff --git a/minijail0.1 b/minijail0.1
index 9c962b4..e713fed 100644
--- a/minijail0.1
+++ b/minijail0.1
@@ -65,10 +65,22 @@
 Run \fIprogram\fR as init (pid 1) inside a new pid namespace (implies \fB-p\fR).
 .TP
 \fB-k <src>,<dest>,<type>[,<flags>[,<data>]]\fR
-Mount \fIsrc\fR, a \fItype\fR filesystem, into the chroot directory at \fIdest\fR,
-with optional \fIflags\fR and optional \fIdata\fR (see \fBmount\fR(2)).
+Mount \fIsrc\fR, a \fItype\fR filesystem, at \fIdest\fR.  If a chroot or pivot
+root is active, \fIdest\fR will automatically be placed below that path.
+
+The \fIflags\fR field is optional and is a hex constant.  These represent the
+\fIMS_XXX\fR settings (see \fBmount\fR(2) for details).  Their values can be
+looked up in the sys/mount.h header file.  \fI0xe\fR is a common value here
+(a writable mount with nodev/nosuid/noexec bits set), and it is strongly
+recommended that all mounts have these three bits set whenever possible.
+
+The \fIdata\fR field is optional and is a comma delimited string (see
+\fBmount\fR(2) for details).  It is passed directly to the kernel, so all
+fields here are filesystem specific.
+
 If the mount is not a pseudo filesystem (e.g. proc or sysfs), \fIsrc\fR path
 must be an absolute path (e.g. \fI/dev/sda1\fR and not \fIsda1\fR).
+
 If the destination does not exist, it will be created as a directory.
 .TP
 \fB-K\fR
diff --git a/minijail0_cli.c b/minijail0_cli.c
index ea4b6cc..8d3240e 100644
--- a/minijail0_cli.c
+++ b/minijail0_cli.c
@@ -158,10 +158,25 @@
 	char *flags = tokenize(&arg, ",");
 	char *data = tokenize(&arg, ",");
 	if (!src || src[0] == '\0' || !dest || dest[0] == '\0' ||
-	    !type || type[0] == '\0' || arg != NULL) {
+	    !type || type[0] == '\0') {
 		fprintf(stderr, "Bad mount: %s %s %s\n", src, dest, type);
 		exit(1);
 	}
+
+	/*
+	 * Fun edge case: the data option itself is comma delimited.  If there
+	 * were no more options, then arg would be set to NULL.  But if we had
+	 * more pending, it'll be pointing to the next token.  Back up and undo
+	 * the null byte so it'll be merged back.
+	 * An example:
+	 *   none,/tmp,tmpfs,0xe,mode=0755,uid=10,gid=10
+	 * The tokenize calls above will turn this memory into:
+	 *   none\0/tmp\0tmpfs\00xe\0mode=0755\0uid=10,gid=10
+	 * With data pointing at mode=0755 and arg pointing at uid=10,gid=10.
+	 */
+	if (arg != NULL)
+		arg[-1] = ',';
+
 	if (minijail_mount_with_data(j, src, dest, type,
 				     flags ? strtoul(flags, NULL, 16) : 0,
 				     data)) {
diff --git a/minijail0_cli_unittest.cc b/minijail0_cli_unittest.cc
index e6cf544..a774d55 100644
--- a/minijail0_cli_unittest.cc
+++ b/minijail0_cli_unittest.cc
@@ -391,6 +391,10 @@
   // Flags are optional.
   argv[2] = "none,/,none,,mode=755";
   ASSERT_TRUE(parse_args_(argv));
+
+  // Multiple data options to the kernel.
+  argv[2] = "none,/,none,0xe,mode=755,uid=0,gid=10";
+  ASSERT_TRUE(parse_args_(argv));
 }
 
 // Invalid calls to the mount option.