minijail: Add support for building constants through masks

This change adds support for building numeric constants through
bitmasks. Things like "O_WRONLY|O_CREAT" are now valid constants.

Since this change works at the atom level, there can be no spaces around
the "|" character.

BUG=chromium:516701
TEST=syscall_filter_unittest passes.

Change-Id: Ia74957f4c5661c9dff7df072684cc6d725c83c10
Reviewed-on: https://chromium-review.googlesource.com/290468
Tested-by: Luis Hector Chavez <lhchavez@google.com>
Reviewed-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
Commit-Queue: Luis Hector Chavez <lhchavez@google.com>
diff --git a/syscall_filter_unittest.c b/syscall_filter_unittest.c
index df014c3..61d614e 100644
--- a/syscall_filter_unittest.c
+++ b/syscall_filter_unittest.c
@@ -331,6 +331,50 @@
 	free_label_strings(&self->labels);
 }
 
+TEST_F(arg_filter, arg0_eq_mask) {
+	const char *fragment = "arg1 == O_WRONLY|O_CREAT";
+	int nr = 1;
+	unsigned int id = 0;
+	struct filter_block *block =
+		compile_section(nr, fragment, id, &self->labels);
+
+	ASSERT_NE(block, NULL);
+	size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2;
+	EXPECT_EQ(block->total_len, exp_total_len);
+
+	/* First block is a label. */
+	struct filter_block *curr_block = block;
+	ASSERT_NE(curr_block, NULL);
+	EXPECT_EQ(block->len, 1U);
+	EXPECT_LBL(curr_block->instrs);
+
+	/* Second block is a comparison. */
+	curr_block = block->next;
+	EXPECT_COMP(curr_block);
+	EXPECT_EQ(curr_block->instrs[BPF_ARG_COMP_LEN  - 1].k,
+		(unsigned int)(O_WRONLY | O_CREAT));
+
+	/* Third block is a jump and a label (end of AND group). */
+	curr_block = curr_block->next;
+	EXPECT_NE(curr_block, NULL);
+	EXPECT_GROUP_END(curr_block);
+
+	/* Fourth block is SECCOMP_RET_KILL */
+	curr_block = curr_block->next;
+	EXPECT_NE(curr_block, NULL);
+	EXPECT_KILL(curr_block);
+
+	/* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW */
+	curr_block = curr_block->next;
+	EXPECT_NE(curr_block, NULL);
+	EXPECT_ALLOW(curr_block);
+
+	EXPECT_EQ(curr_block->next, NULL);
+
+	free_block_list(block);
+	free_label_strings(&self->labels);
+}
+
 TEST_F(arg_filter, and_or) {
 	const char *fragment = "arg0 == 0 && arg1 == 0 || arg0 == 1";
 	int nr = 1;
diff --git a/util.c b/util.c
index 6b16a23..90ac9f0 100644
--- a/util.c
+++ b/util.c
@@ -40,6 +40,8 @@
 
 const size_t log_syscalls_len = sizeof(log_syscalls)/sizeof(log_syscalls[0]);
 
+long int parse_single_constant(char *constant_str, char **endptr);
+
 int lookup_syscall(const char *name)
 {
 	const struct syscall_entry *entry = syscall_table;
@@ -60,6 +62,36 @@
 
 long int parse_constant(char *constant_str, char **endptr)
 {
+	long int value = 0;
+	char *group, *lastpos = constant_str;
+	char *original_constant_str = constant_str;
+
+	/*
+	 * Try to parse constants separated by pipes.  Note that since
+	 * |constant_str| is an atom, there can be no spaces between the
+	 * constant and the pipe.  Constants can be either a named constant
+	 * defined in libconstants.gen.c or a number parsed with strtol.
+	 *
+	 * If there is an error parsing any of the constants, the whole process
+	 * fails.
+	 */
+	while ((group = tokenize(&constant_str, "|")) != NULL) {
+		char *end = group;
+		value |= parse_single_constant(group, &end);
+		if (end == group) {
+			lastpos = original_constant_str;
+			value = 0;
+			break;
+		}
+		lastpos = end;
+	}
+	if (endptr)
+		*endptr = lastpos;
+	return value;
+}
+
+long int parse_single_constant(char *constant_str, char **endptr)
+{
 	const struct constant_entry *entry = constant_table;
 	for (; entry->name; ++entry) {
 		if (!strcmp(entry->name, constant_str)) {