syscall_filter: Implement flag set inclusion.

When filtering syscalls that take flags as an argument, we usually want
to allow a small set of "safe" flags. This is hard to express with the
current language.

Implement this by adding a "flag set inclusion" mode using the 'in'
keyword. This works by allowing the syscall as long as the passed
flags, when viewed as a set, are included in the set of flags described
by the policy.

Also, clang-format all of bpf.c.

Bug: 31997910
Test: syscall_filter_unittest
Change-Id: I121af56b176bd3260904d367fd92d47a16bb3dcb
diff --git a/bpf.c b/bpf.c
index ce754a7..b618866 100644
--- a/bpf.c
+++ b/bpf.c
@@ -15,9 +15,9 @@
 size_t bpf_validate_arch(struct sock_filter *filter)
 {
 	struct sock_filter *curr_block = filter;
-	set_bpf_stmt(curr_block++, BPF_LD+BPF_W+BPF_ABS, arch_nr);
-	set_bpf_jump(curr_block++,
-			BPF_JMP+BPF_JEQ+BPF_K, ARCH_NR, SKIP, NEXT);
+	set_bpf_stmt(curr_block++, BPF_LD + BPF_W + BPF_ABS, arch_nr);
+	set_bpf_jump(curr_block++, BPF_JMP + BPF_JEQ + BPF_K, ARCH_NR, SKIP,
+		     NEXT);
 	set_bpf_ret_kill(curr_block++);
 	return curr_block - filter;
 }
@@ -26,16 +26,16 @@
 size_t bpf_allow_syscall(struct sock_filter *filter, int nr)
 {
 	struct sock_filter *curr_block = filter;
-	set_bpf_jump(curr_block++, BPF_JMP+BPF_JEQ+BPF_K, nr, NEXT, SKIP);
-	set_bpf_stmt(curr_block++, BPF_RET+BPF_K, SECCOMP_RET_ALLOW);
+	set_bpf_jump(curr_block++, BPF_JMP + BPF_JEQ + BPF_K, nr, NEXT, SKIP);
+	set_bpf_stmt(curr_block++, BPF_RET + BPF_K, SECCOMP_RET_ALLOW);
 	return curr_block - filter;
 }
 
-size_t bpf_allow_syscall_args(struct sock_filter *filter,
-		int nr, unsigned int id)
+size_t bpf_allow_syscall_args(struct sock_filter *filter, int nr,
+			      unsigned int id)
 {
 	struct sock_filter *curr_block = filter;
-	set_bpf_jump(curr_block++, BPF_JMP+BPF_JEQ+BPF_K, nr, NEXT, SKIP);
+	set_bpf_jump(curr_block++, BPF_JMP + BPF_JEQ + BPF_K, nr, NEXT, SKIP);
 	set_bpf_jump_lbl(curr_block++, id);
 	return curr_block - filter;
 }
@@ -44,16 +44,16 @@
 #if defined(BITS32)
 size_t bpf_load_arg(struct sock_filter *filter, int argidx)
 {
-	set_bpf_stmt(filter, BPF_LD+BPF_W+BPF_ABS, LO_ARG(argidx));
+	set_bpf_stmt(filter, BPF_LD + BPF_W + BPF_ABS, LO_ARG(argidx));
 	return 1U;
 }
 #elif defined(BITS64)
 size_t bpf_load_arg(struct sock_filter *filter, int argidx)
 {
 	struct sock_filter *curr_block = filter;
-	set_bpf_stmt(curr_block++, BPF_LD+BPF_W+BPF_ABS, LO_ARG(argidx));
+	set_bpf_stmt(curr_block++, BPF_LD + BPF_W + BPF_ABS, LO_ARG(argidx));
 	set_bpf_stmt(curr_block++, BPF_ST, 0); /* lo -> M[0] */
-	set_bpf_stmt(curr_block++, BPF_LD+BPF_W+BPF_ABS, HI_ARG(argidx));
+	set_bpf_stmt(curr_block++, BPF_LD + BPF_W + BPF_ABS, HI_ARG(argidx));
 	set_bpf_stmt(curr_block++, BPF_ST, 1); /* hi -> M[1] */
 	return curr_block - filter;
 }
@@ -61,10 +61,10 @@
 
 /* Size-aware equality comparison. */
 size_t bpf_comp_jeq32(struct sock_filter *filter, unsigned long c,
-		unsigned char jt, unsigned char jf)
+		      unsigned char jt, unsigned char jf)
 {
 	unsigned int lo = (unsigned int)(c & 0xFFFFFFFF);
-	set_bpf_jump(filter, BPF_JMP+BPF_JEQ+BPF_K, lo, jt, jf);
+	set_bpf_jump(filter, BPF_JMP + BPF_JEQ + BPF_K, lo, jt, jf);
 	return 1U;
 }
 
@@ -73,8 +73,8 @@
  * We jump true when *both* comparisons are true.
  */
 #if defined(BITS64)
-size_t bpf_comp_jeq64(struct sock_filter *filter, uint64_t c,
-		unsigned char jt, unsigned char jf)
+size_t bpf_comp_jeq64(struct sock_filter *filter, uint64_t c, unsigned char jt,
+		      unsigned char jf)
 {
 	unsigned int lo = (unsigned int)(c & 0xFFFFFFFF);
 	unsigned int hi = (unsigned int)(c >> 32);
@@ -83,7 +83,7 @@
 
 	/* bpf_load_arg leaves |hi| in A */
 	curr_block += bpf_comp_jeq32(curr_block, hi, NEXT, SKIPN(2) + jf);
-	set_bpf_stmt(curr_block++, BPF_LD+BPF_MEM, 0); /* swap in |lo| */
+	set_bpf_stmt(curr_block++, BPF_LD + BPF_MEM, 0); /* swap in |lo| */
 	curr_block += bpf_comp_jeq32(curr_block, lo, jt, jf);
 
 	return curr_block - filter;
@@ -92,10 +92,10 @@
 
 /* Size-aware bitwise AND. */
 size_t bpf_comp_jset32(struct sock_filter *filter, unsigned long mask,
-		unsigned char jt, unsigned char jf)
+		       unsigned char jt, unsigned char jf)
 {
 	unsigned int mask_lo = (unsigned int)(mask & 0xFFFFFFFF);
-	set_bpf_jump(filter, BPF_JMP+BPF_JSET+BPF_K, mask_lo, jt, jf);
+	set_bpf_jump(filter, BPF_JMP + BPF_JSET + BPF_K, mask_lo, jt, jf);
 	return 1U;
 }
 
@@ -105,7 +105,7 @@
  */
 #if defined(BITS64)
 size_t bpf_comp_jset64(struct sock_filter *filter, uint64_t mask,
-		unsigned char jt, unsigned char jf)
+		       unsigned char jt, unsigned char jf)
 {
 	unsigned int mask_lo = (unsigned int)(mask & 0xFFFFFFFF);
 	unsigned int mask_hi = (unsigned int)(mask >> 32);
@@ -114,20 +114,32 @@
 
 	/* bpf_load_arg leaves |hi| in A */
 	curr_block += bpf_comp_jset32(curr_block, mask_hi, SKIPN(2) + jt, NEXT);
-	set_bpf_stmt(curr_block++, BPF_LD+BPF_MEM, 0); /* swap in |lo| */
+	set_bpf_stmt(curr_block++, BPF_LD + BPF_MEM, 0); /* swap in |lo| */
 	curr_block += bpf_comp_jset32(curr_block, mask_lo, jt, jf);
 
 	return curr_block - filter;
 }
 #endif
 
-size_t bpf_arg_comp(struct sock_filter **pfilter,
-		int op, int argidx, unsigned long c, unsigned int label_id)
+size_t bpf_comp_jin(struct sock_filter *filter, unsigned long mask,
+		    unsigned char jt, unsigned char jf)
 {
-	struct sock_filter *filter = calloc(BPF_ARG_COMP_LEN + 1,
-			sizeof(struct sock_filter));
+	unsigned long negative_mask = ~mask;
+	/*
+	 * The mask is negated, so the comparison will be true when the argument
+	 * includes a flag that wasn't listed in the original (non-negated)
+	 * mask. This would be the failure case, so we switch |jt| and |jf|.
+	 */
+	return bpf_comp_jset(filter, negative_mask, jf, jt);
+}
+
+size_t bpf_arg_comp(struct sock_filter **pfilter, int op, int argidx,
+		    unsigned long c, unsigned int label_id)
+{
+	struct sock_filter *filter =
+	    calloc(BPF_ARG_COMP_LEN + 1, sizeof(struct sock_filter));
 	struct sock_filter *curr_block = filter;
-	size_t (*comp_function)(struct sock_filter *filter, unsigned long k,
+	size_t (*comp_function)(struct sock_filter * filter, unsigned long k,
 				unsigned char jt, unsigned char jf);
 	int flip = 0;
 
@@ -148,6 +160,10 @@
 		comp_function = bpf_comp_jset;
 		flip = 0;
 		break;
+	case IN:
+		comp_function = bpf_comp_jin;
+		flip = 0;
+		break;
 	default:
 		*pfilter = NULL;
 		return 0;
@@ -279,7 +295,7 @@
 	end = begin + labels->count;
 	for (; begin < end; ++begin) {
 		if (begin->label)
-			free((void*)(begin->label));
+			free((void *)(begin->label));
 	}
 
 	labels->count = 0;
diff --git a/bpf.h b/bpf.h
index e4577fd..dd8c3f3 100644
--- a/bpf.h
+++ b/bpf.h
@@ -37,7 +37,8 @@
 	LE,
 	GT,
 	GE,
-	SET
+	SET,
+	IN
 };
 
 /*
@@ -174,9 +175,11 @@
 /* BPF helper functions. */
 size_t bpf_load_arg(struct sock_filter *filter, int argidx);
 size_t bpf_comp_jeq(struct sock_filter *filter, unsigned long c,
-		unsigned char jt, unsigned char jf);
+		    unsigned char jt, unsigned char jf);
 size_t bpf_comp_jset(struct sock_filter *filter, unsigned long mask,
-		unsigned char jt, unsigned char jf);
+		     unsigned char jt, unsigned char jf);
+size_t bpf_comp_jin(struct sock_filter *filter, unsigned long mask,
+		    unsigned char jt, unsigned char jf);
 
 /* Functions called by syscall_filter.c */
 #define ARCH_VALIDATION_LEN 3U
diff --git a/syscall_filter.c b/syscall_filter.c
index e42f017..34f6b3a 100644
--- a/syscall_filter.c
+++ b/syscall_filter.c
@@ -33,6 +33,8 @@
 		return NE;
 	} else if (!strcmp(op_str, "&")) {
 		return SET;
+	} else if (!strcmp(op_str, "in")) {
+		return IN;
 	} else {
 		return 0;
 	}
diff --git a/syscall_filter_unittest.c b/syscall_filter_unittest.c
index 541f98b..7862a5a 100644
--- a/syscall_filter_unittest.c
+++ b/syscall_filter_unittest.c
@@ -78,7 +78,8 @@
 
 TEST_F(bpf, bpf_comp_jset) {
 	struct sock_filter comp_jset[BPF_COMP_LEN];
-	unsigned long mask = O_WRONLY;
+	unsigned long mask =
+	    (1UL << (sizeof(unsigned long) * 8 - 1)) | O_WRONLY;
 	unsigned char jt = 1;
 	unsigned char jf = 2;
 
@@ -91,10 +92,33 @@
 			BPF_JMP+BPF_JSET+BPF_K, mask, jt, jf);
 #elif defined(BITS64)
 	EXPECT_EQ_BLOCK(&comp_jset[0],
-			BPF_JMP+BPF_JSET+BPF_K, 0, jt + 2, 0);
+			BPF_JMP+BPF_JSET+BPF_K, 0x80000000, jt + 2, 0);
 	EXPECT_EQ_STMT(&comp_jset[1], BPF_LD+BPF_MEM, 0);
 	EXPECT_EQ_BLOCK(&comp_jset[2],
-			BPF_JMP+BPF_JSET+BPF_K, mask, jt, jf);
+			BPF_JMP+BPF_JSET+BPF_K, O_WRONLY, jt, jf);
+#endif
+}
+
+TEST_F(bpf, bpf_comp_jin) {
+	struct sock_filter comp_jin[BPF_COMP_LEN];
+	unsigned long mask =
+	    (1UL << (sizeof(unsigned long) * 8 - 1)) | O_WRONLY;
+	unsigned char jt = 10;
+	unsigned char jf = 20;
+
+	size_t len = bpf_comp_jin(comp_jin, mask, jt, jf);
+
+	EXPECT_EQ(len, BPF_COMP_LEN);
+
+#if defined(BITS32)
+	EXPECT_EQ_BLOCK(&comp_jin[0],
+			BPF_JMP+BPF_JSET+BPF_K, ~mask, jf, jt);
+#elif defined(BITS64)
+	EXPECT_EQ_BLOCK(&comp_jin[0],
+			BPF_JMP+BPF_JSET+BPF_K, 0x7FFFFFFF, jf + 2, 0);
+	EXPECT_EQ_STMT(&comp_jin[1], BPF_LD+BPF_MEM, 0);
+	EXPECT_EQ_BLOCK(&comp_jin[2],
+			BPF_JMP+BPF_JSET+BPF_K, ~O_WRONLY, jf, jt);
 #endif
 }
 
@@ -354,6 +378,48 @@
 	free_block_list(block);
 }
 
+TEST_F(arg_filter, arg0_flag_set_inclusion) {
+	const char *fragment = "arg0 in O_RDONLY|O_CREAT";
+	int nr = 1;
+	unsigned int id = 0;
+	struct filter_block *block =
+		compile_section(nr, fragment, id, &self->labels, NO_LOGGING);
+
+	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;
+	ASSERT_NE(curr_block, NULL);
+	EXPECT_COMP(curr_block);
+
+	/* Third block is a jump and a label (end of AND group). */
+	curr_block = curr_block->next;
+	ASSERT_NE(curr_block, NULL);
+	EXPECT_GROUP_END(curr_block);
+
+	/* Fourth block is SECCOMP_RET_KILL. */
+	curr_block = curr_block->next;
+	ASSERT_NE(curr_block, NULL);
+	EXPECT_KILL(curr_block);
+
+	/* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
+	curr_block = curr_block->next;
+	ASSERT_NE(curr_block, NULL);
+	EXPECT_ALLOW(curr_block);
+
+	EXPECT_EQ(curr_block->next, NULL);
+
+	free_block_list(block);
+}
+
 TEST_F(arg_filter, arg0_eq_mask) {
 	const char *fragment = "arg1 == O_WRONLY|O_CREAT";
 	int nr = 1;
diff --git a/syscall_filter_unittest.cpp b/syscall_filter_unittest.cpp
index ce2e019..403c10d 100644
--- a/syscall_filter_unittest.cpp
+++ b/syscall_filter_unittest.cpp
@@ -77,7 +77,7 @@
 
 TEST(bpf, bpf_comp_jset) {
   struct sock_filter comp_jset[BPF_COMP_LEN];
-  unsigned long mask = O_WRONLY;
+  unsigned long mask = (1UL << (sizeof(unsigned long) * 8 - 1)) | O_WRONLY;
   unsigned char jt = 1;
   unsigned char jf = 2;
 
@@ -88,9 +88,30 @@
 #if defined(BITS32)
   EXPECT_EQ_BLOCK(&comp_jset[0], BPF_JMP + BPF_JSET + BPF_K, mask, jt, jf);
 #elif defined(BITS64)
-  EXPECT_EQ_BLOCK(&comp_jset[0], BPF_JMP + BPF_JSET + BPF_K, 0, jt + 2, 0);
+  EXPECT_EQ_BLOCK(
+      &comp_jset[0], BPF_JMP + BPF_JSET + BPF_K, 0x80000000, jt + 2, 0);
   EXPECT_EQ_STMT(&comp_jset[1], BPF_LD + BPF_MEM, 0);
-  EXPECT_EQ_BLOCK(&comp_jset[2], BPF_JMP + BPF_JSET + BPF_K, mask, jt, jf);
+  EXPECT_EQ_BLOCK(&comp_jset[2], BPF_JMP + BPF_JSET + BPF_K, O_WRONLY, jt, jf);
+#endif
+}
+
+TEST(bpf, bpf_comp_jin) {
+  struct sock_filter comp_jin[BPF_COMP_LEN];
+  unsigned long mask = (1UL << (sizeof(unsigned long) * 8 - 1)) | O_WRONLY;
+  unsigned char jt = 10;
+  unsigned char jf = 20;
+
+  size_t len = bpf_comp_jin(comp_jin, mask, jt, jf);
+
+  EXPECT_EQ(len, BPF_COMP_LEN);
+
+#if defined(BITS32)
+  EXPECT_EQ_BLOCK(&comp_jin[0], BPF_JMP + BPF_JSET + BPF_K, ~mask, jf, jt);
+#elif defined(BITS64)
+  EXPECT_EQ_BLOCK(
+      &comp_jin[0], BPF_JMP + BPF_JSET + BPF_K, 0x7FFFFFFF, jf + 2, 0);
+  EXPECT_EQ_STMT(&comp_jin[1], BPF_LD + BPF_MEM, 0);
+  EXPECT_EQ_BLOCK(&comp_jin[2], BPF_JMP + BPF_JSET + BPF_K, ~O_WRONLY, jf, jt);
 #endif
 }
 
@@ -336,6 +357,48 @@
   free_block_list(block);
 }
 
+TEST_F(ArgFilterTest, arg0_flag_set_inclusion) {
+  const char *fragment = "arg0 in O_RDONLY|O_CREAT";
+  int nr = 1;
+  unsigned int id = 0;
+  struct filter_block *block =
+      compile_section(nr, fragment, id, &labels_, NO_LOGGING);
+
+  ASSERT_NE(block, nullptr);
+  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, nullptr);
+  EXPECT_EQ(block->len, 1U);
+  EXPECT_LBL(curr_block->instrs);
+
+  /* Second block is a comparison. */
+  curr_block = block->next;
+  ASSERT_NE(curr_block, nullptr);
+  EXPECT_COMP(curr_block);
+
+  /* Third block is a jump and a label (end of AND group). */
+  curr_block = curr_block->next;
+  ASSERT_NE(curr_block, nullptr);
+  EXPECT_GROUP_END(curr_block);
+
+  /* Fourth block is SECCOMP_RET_KILL. */
+  curr_block = curr_block->next;
+  ASSERT_NE(curr_block, nullptr);
+  EXPECT_KILL(curr_block);
+
+  /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
+  curr_block = curr_block->next;
+  ASSERT_NE(curr_block, nullptr);
+  EXPECT_ALLOW(curr_block);
+
+  EXPECT_EQ(curr_block->next, nullptr);
+
+  free_block_list(block);
+}
+
 TEST_F(ArgFilterTest, arg0_eq_mask) {
   const char *fragment = "arg1 == O_WRONLY|O_CREAT";
   int nr = 1;
diff --git a/util.c b/util.c
index b242c71..f0dc23d 100644
--- a/util.c
+++ b/util.c
@@ -108,7 +108,7 @@
 	 * 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.
+	 * defined in libconstants.gen.c or a number parsed with strtol(3).
 	 *
 	 * If there is an error parsing any of the constants, the whole process
 	 * fails.