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)) {