Port syscall filtering unit tests to C++.

That way they can be run on Android Platform Continuous Testing,
which requires gtest for parsing of test output. Also fix some issues
in the tests:
-Use ASSERT instead of EXCEPT for pointers that are later
dereferenced.
-Add a few missing ASSERTs.

syscall_filter_unittest.cpp has no changes from
syscall_filter_unittest.c. Keep parallel targets for now, and once
Chrome OS is updated, remove C targets.

Bug: 30973585
Change-Id: I46a1474afa8850015da927ce8c7d9f8b8ce95b65
diff --git a/Android.mk b/Android.mk
index 70d89f3..d3fadcc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -226,6 +226,25 @@
 include $(BUILD_NATIVE_TEST)
 
 
+# Syscall filtering native unit tests using gtest. Run with:
+# adb shell /data/nativetest/syscall_filter_unittest_gtest/syscall_filter_unittest_gtest
+# =========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := syscall_filter_unittest_gtest
+
+LOCAL_CFLAGS := $(minijailCommonCFlags)
+LOCAL_CLANG := true
+LOCAL_SRC_FILES := \
+	bpf.c \
+	syscall_filter.c \
+	util.c \
+	syscall_filter_unittest.cpp \
+
+LOCAL_STATIC_LIBRARIES := libminijail_generated
+LOCAL_SHARED_LIBRARIES := $(minijailCommonLibraries)
+include $(BUILD_NATIVE_TEST)
+
+
 # Syscall filtering native unit tests for the host. Run with:
 # out/host/linux-x86/nativetest(64)/syscall_filter_unittest/syscall_filter_unittest
 # =========================================================
diff --git a/syscall_filter_unittest.c b/syscall_filter_unittest.c
index c318d3e..603479e 100644
--- a/syscall_filter_unittest.c
+++ b/syscall_filter_unittest.c
@@ -8,7 +8,7 @@
 
 #include <asm/unistd.h>
 #include <errno.h>
-#include <fcntl.h>	/* For O_WRONLY */
+#include <fcntl.h>	/* For O_WRONLY. */
 
 #include "test_harness.h"
 
@@ -17,91 +17,7 @@
 
 #include "util.h"
 
-/* BPF testing macros. */
-#define EXPECT_EQ_BLOCK(_block, _code, _k, _jt, _jf)	\
-do {	\
-	EXPECT_EQ((_block)->code, _code);		\
-	EXPECT_EQ((_block)->k, (unsigned int)(_k));	\
-	EXPECT_EQ((_block)->jt, _jt);			\
-	EXPECT_EQ((_block)->jf, _jf);			\
-} while (0)
-
-#define EXPECT_EQ_STMT(_block, _code, _k) \
-	EXPECT_EQ_BLOCK(_block, _code, _k, 0, 0)
-
-#define EXPECT_COMP(_block) \
-do {	\
-	EXPECT_EQ((_block)->len, BPF_ARG_COMP_LEN + 1);			\
-	EXPECT_EQ((_block)->instrs->code, BPF_LD+BPF_W+BPF_ABS);	\
-} while (0)
-
-#define EXPECT_LBL(_block) \
-	do {	\
-	EXPECT_EQ((_block)->code, BPF_JMP+BPF_JA);	\
-	EXPECT_EQ((_block)->jt, LABEL_JT);		\
-	EXPECT_EQ((_block)->jf, LABEL_JF);		\
-} while (0)
-
-#define EXPECT_JUMP_LBL(_block) \
-do {	\
-	EXPECT_EQ((_block)->code, BPF_JMP+BPF_JA);	\
-	EXPECT_EQ((_block)->jt, JUMP_JT);		\
-	EXPECT_EQ((_block)->jf, JUMP_JF);		\
-} while (0)
-
-#define EXPECT_GROUP_END(_block) \
-do {	\
-	EXPECT_EQ((_block)->len, 2U);			\
-	EXPECT_JUMP_LBL(&(_block)->instrs[0]);		\
-	EXPECT_LBL(&(_block)->instrs[1]);		\
-} while (0)
-
-#define EXPECT_KILL(_block) \
-do {	\
-	EXPECT_EQ((_block)->len, 1U);				\
-	EXPECT_EQ_STMT((_block)->instrs,			\
-			BPF_RET+BPF_K, SECCOMP_RET_KILL);	\
-} while (0)
-
-#define EXPECT_TRAP(_block) \
-do {	\
-	EXPECT_EQ((_block)->len, 1U);				\
-	EXPECT_EQ_STMT((_block)->instrs,			\
-			BPF_RET+BPF_K, SECCOMP_RET_TRAP);	\
-} while (0)
-
-#define EXPECT_ALLOW(_block) \
-do {	\
-	EXPECT_EQ((_block)->len, 2U);				\
-	EXPECT_LBL(&(_block)->instrs[0]);			\
-	EXPECT_EQ_STMT(&(_block)->instrs[1],			\
-			BPF_RET+BPF_K, SECCOMP_RET_ALLOW);	\
-} while (0)
-
-#define EXPECT_ARCH_VALIDATION(_filter) \
-do {	\
-	EXPECT_EQ_STMT(&(_filter)[0], BPF_LD+BPF_W+BPF_ABS, arch_nr);	\
-	EXPECT_EQ_BLOCK(&(_filter)[1],					\
-			BPF_JMP+BPF_JEQ+BPF_K, ARCH_NR, SKIP, NEXT);	\
-	EXPECT_EQ_STMT(&(_filter)[2], BPF_RET+BPF_K, SECCOMP_RET_KILL);	\
-} while (0)
-
-#define EXPECT_ALLOW_SYSCALL(_filter, _nr) \
-do {	\
-	EXPECT_EQ_BLOCK(&(_filter)[0],					\
-			BPF_JMP+BPF_JEQ+BPF_K, (_nr), NEXT, SKIP);	\
-	EXPECT_EQ_STMT(&(_filter)[1],					\
-			BPF_RET+BPF_K, SECCOMP_RET_ALLOW);		\
-} while (0)
-
-#define EXPECT_ALLOW_SYSCALL_ARGS(_filter, _nr, _id, _jt, _jf) \
-do {	\
-	EXPECT_EQ_BLOCK(&(_filter)[0],					\
-			BPF_JMP+BPF_JEQ+BPF_K, (_nr), NEXT, SKIP);	\
-	EXPECT_EQ_BLOCK(&(_filter)[1],					\
-			BPF_JMP+BPF_JA, (_id), (_jt), (_jf));		\
-} while (0)
-
+#include "syscall_filter_unittest_macros.h"
 
 FIXTURE(bpf) {};
 
@@ -252,7 +168,9 @@
 };
 
 FIXTURE_SETUP(arg_filter) {}
-FIXTURE_TEARDOWN(arg_filter) {}
+FIXTURE_TEARDOWN(arg_filter) {
+	free_label_strings(&self->labels);
+}
 
 TEST_F(arg_filter, arg0_equals) {
 	const char *fragment = "arg0 == 0";
@@ -273,27 +191,27 @@
 
 	/* 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;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_GROUP_END(curr_block);
 
-	/* Fourth block is SECCOMP_RET_KILL */
+	/* Fourth block is SECCOMP_RET_KILL. */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_KILL(curr_block);
 
-	/* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW */
+	/* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_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, arg0_mask) {
@@ -315,27 +233,27 @@
 
 	/* 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;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_GROUP_END(curr_block);
 
-	/* Fourth block is SECCOMP_RET_KILL */
+	/* Fourth block is SECCOMP_RET_KILL. */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_KILL(curr_block);
 
-	/* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW */
+	/* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_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, arg0_eq_mask) {
@@ -357,29 +275,29 @@
 
 	/* Second block is a comparison. */
 	curr_block = block->next;
+	ASSERT_NE(curr_block, NULL);
 	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);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_GROUP_END(curr_block);
 
-	/* Fourth block is SECCOMP_RET_KILL */
+	/* Fourth block is SECCOMP_RET_KILL. */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_KILL(curr_block);
 
-	/* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW */
+	/* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_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) {
@@ -401,37 +319,37 @@
 
 	/* Second block is a comparison ("arg0 == 0"). */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_COMP(curr_block);
 
 	/* Third block is a comparison ("arg1 == 0"). */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_COMP(curr_block);
 
 	/* Fourth block is a jump and a label (end of AND group). */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_GROUP_END(curr_block);
 
 	/* Fifth block is a comparison ("arg0 == 1"). */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_COMP(curr_block);
 
 	/* Sixth block is a jump and a label (end of AND group). */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_GROUP_END(curr_block);
 
-	/* Seventh block is SECCOMP_RET_KILL */
+	/* Seventh block is SECCOMP_RET_KILL. */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_KILL(curr_block);
 
-	/* Eigth block is "SUCCESS" label and SECCOMP_RET_ALLOW */
+	/* Eigth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_ALLOW(curr_block);
 
 	EXPECT_EQ(curr_block->next, NULL);
@@ -459,35 +377,35 @@
 
 	/* Second block is a comparison ("arg0 == 0"). */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	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;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_GROUP_END(curr_block);
 
 	/* Fourth block is a comparison ("arg0 == 1"). */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_COMP(curr_block);
 
 	/* Fifth block is a jump and a label (end of AND group). */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_GROUP_END(curr_block);
 
-	/* Sixth block is SECCOMP_RET_ERRNO */
+	/* Sixth block is SECCOMP_RET_ERRNO. */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_EQ(curr_block->len, 1U);
 	EXPECT_EQ_STMT(curr_block->instrs,
 			BPF_RET+BPF_K,
 			SECCOMP_RET_ERRNO | (1 & SECCOMP_RET_DATA));
 
-	/* Seventh block is "SUCCESS" label and SECCOMP_RET_ALLOW */
+	/* Seventh block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_ALLOW(curr_block);
 
 	EXPECT_EQ(curr_block->next, NULL);
@@ -513,9 +431,9 @@
 	EXPECT_EQ(block->len, 1U);
 	EXPECT_LBL(curr_block->instrs);
 
-	/* Second block is SECCOMP_RET_ERRNO */
+	/* Second block is SECCOMP_RET_ERRNO. */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_EQ(curr_block->len, 1U);
 	EXPECT_EQ_STMT(curr_block->instrs,
 			BPF_RET+BPF_K,
@@ -560,21 +478,22 @@
 
 	/* 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;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_GROUP_END(curr_block);
 
 	/* Fourth block is SECCOMP_RET_TRAP, with no errno. */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_TRAP(curr_block);
 
 	/* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_ALLOW(curr_block);
 
 	EXPECT_EQ(curr_block->next, NULL);
@@ -602,24 +521,24 @@
 
 	/* Second block is a comparison ("arg0 == 0"). */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	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;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_GROUP_END(curr_block);
 
 	/*
 	 * Sixth block is NOT SECCOMP_RET_ERRNO, it should be SECCOMP_RET_KILL.
 	 */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_KILL(curr_block);
 
 	/* Seventh block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_ALLOW(curr_block);
 
 	EXPECT_EQ(curr_block->next, NULL);
@@ -647,24 +566,24 @@
 
 	/* Second block is a comparison ("arg0 == 0"). */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	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;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_GROUP_END(curr_block);
 
 	/*
 	 * Sixth block is NOT SECCOMP_RET_ERRNO, it should be SECCOMP_RET_TRAP.
 	 */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_TRAP(curr_block);
 
 	/* Seventh block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
 	curr_block = curr_block->next;
-	EXPECT_NE(curr_block, NULL);
+	ASSERT_NE(curr_block, NULL);
 	EXPECT_ALLOW(curr_block);
 
 	EXPECT_EQ(curr_block->next, NULL);
diff --git a/syscall_filter_unittest.cpp b/syscall_filter_unittest.cpp
new file mode 100644
index 0000000..09e98f6
--- /dev/null
+++ b/syscall_filter_unittest.cpp
@@ -0,0 +1,769 @@
+// syscall_filter_unittest.cpp
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Test syscall filtering using gtest.
+
+#include <asm/unistd.h>
+#include <errno.h>
+#include <fcntl.h> /* For O_WRONLY. */
+
+#include <gtest/gtest.h>
+
+#include "bpf.h"
+#include "syscall_filter.h"
+#include "syscall_filter_unittest_macros.h"
+#include "util.h"
+
+/* Test that setting one BPF instruction works. */
+TEST(bpf, set_bpf_instr) {
+  struct sock_filter instr;
+  unsigned char code = BPF_LD + BPF_W + BPF_ABS;
+  unsigned int k = 4;
+  unsigned char jt = 1, jf = 2;
+
+  size_t len = set_bpf_instr(&instr, code, k, jt, jf);
+
+  EXPECT_EQ(len, 1U);
+  EXPECT_EQ_BLOCK(&instr, code, k, jt, jf);
+}
+
+TEST(bpf, bpf_load_arg) {
+  struct sock_filter load_arg[BPF_LOAD_ARG_LEN];
+  int argidx = 1;
+  size_t len = bpf_load_arg(load_arg, argidx);
+
+  EXPECT_EQ(len, BPF_LOAD_ARG_LEN);
+
+#if defined(BITS32)
+  EXPECT_EQ_STMT(&load_arg[0], BPF_LD + BPF_W + BPF_ABS, LO_ARG(argidx));
+#elif defined(BITS64)
+  EXPECT_EQ_STMT(&load_arg[0], BPF_LD + BPF_W + BPF_ABS, LO_ARG(argidx));
+  EXPECT_EQ_STMT(&load_arg[1], BPF_ST, 0);
+  EXPECT_EQ_STMT(&load_arg[2], BPF_LD + BPF_W + BPF_ABS, HI_ARG(argidx));
+  EXPECT_EQ_STMT(&load_arg[3], BPF_ST, 1);
+#endif
+}
+
+TEST(bpf, bpf_comp_jeq) {
+  struct sock_filter comp_jeq[BPF_COMP_LEN];
+  unsigned long c = 1;
+  unsigned char jt = 1;
+  unsigned char jf = 2;
+
+  size_t len = bpf_comp_jeq(comp_jeq, c, jt, jf);
+
+  EXPECT_EQ(len, BPF_COMP_LEN);
+
+#if defined(BITS32)
+  EXPECT_EQ_BLOCK(&comp_jeq[0], BPF_JMP + BPF_JEQ + BPF_K, c, jt, jf);
+#elif defined(BITS64)
+  EXPECT_EQ_BLOCK(&comp_jeq[0], BPF_JMP + BPF_JEQ + BPF_K, 0, 0, jf + 2);
+  EXPECT_EQ_STMT(&comp_jeq[1], BPF_LD + BPF_MEM, 0);
+  EXPECT_EQ_BLOCK(&comp_jeq[2], BPF_JMP + BPF_JEQ + BPF_K, c, jt, jf);
+#endif
+}
+
+TEST(bpf, bpf_comp_jset) {
+  struct sock_filter comp_jset[BPF_COMP_LEN];
+  unsigned long mask = O_WRONLY;
+  unsigned char jt = 1;
+  unsigned char jf = 2;
+
+  size_t len = bpf_comp_jset(comp_jset, mask, jt, jf);
+
+  EXPECT_EQ(len, BPF_COMP_LEN);
+
+#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_STMT(&comp_jset[1], BPF_LD + BPF_MEM, 0);
+  EXPECT_EQ_BLOCK(&comp_jset[2], BPF_JMP + BPF_JSET + BPF_K, mask, jt, jf);
+#endif
+}
+
+TEST(bpf, bpf_arg_comp) {
+  struct sock_filter *arg_comp;
+  int op = EQ;
+  int argidx = 1;
+  unsigned long c = 3;
+  unsigned int label_id = 0;
+
+  size_t len = bpf_arg_comp(&arg_comp, op, argidx, c, label_id);
+
+  EXPECT_EQ(len, BPF_ARG_COMP_LEN + 1);
+
+#if defined(BITS32)
+  EXPECT_EQ_STMT(&arg_comp[0], BPF_LD + BPF_W + BPF_ABS, LO_ARG(argidx));
+  EXPECT_EQ_BLOCK(&arg_comp[1], BPF_JMP + BPF_JEQ + BPF_K, c, 1, 0);
+  EXPECT_JUMP_LBL(&arg_comp[2]);
+#elif defined(BITS64)
+  EXPECT_EQ_STMT(&arg_comp[0], BPF_LD + BPF_W + BPF_ABS, LO_ARG(argidx));
+  EXPECT_EQ_STMT(&arg_comp[1], BPF_ST, 0);
+  EXPECT_EQ_STMT(&arg_comp[2], BPF_LD + BPF_W + BPF_ABS, HI_ARG(argidx));
+  EXPECT_EQ_STMT(&arg_comp[3], BPF_ST, 1);
+
+  EXPECT_EQ_BLOCK(&arg_comp[4], BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 2);
+  EXPECT_EQ_STMT(&arg_comp[5], BPF_LD + BPF_MEM, 0);
+  EXPECT_EQ_BLOCK(&arg_comp[6], BPF_JMP + BPF_JEQ + BPF_K, c, 1, 0);
+  EXPECT_JUMP_LBL(&arg_comp[7]);
+#endif
+  free(arg_comp);
+}
+
+TEST(bpf, bpf_validate_arch) {
+  struct sock_filter validate_arch[ARCH_VALIDATION_LEN];
+
+  size_t len = bpf_validate_arch(validate_arch);
+
+  EXPECT_EQ(len, ARCH_VALIDATION_LEN);
+  EXPECT_ARCH_VALIDATION(validate_arch);
+}
+
+TEST(bpf, bpf_allow_syscall) {
+  struct sock_filter allow_syscall[ALLOW_SYSCALL_LEN];
+  int nr = 1;
+
+  size_t len = bpf_allow_syscall(allow_syscall, nr);
+
+  EXPECT_EQ(len, ALLOW_SYSCALL_LEN);
+  EXPECT_ALLOW_SYSCALL(allow_syscall, nr);
+}
+
+TEST(bpf, bpf_allow_syscall_args) {
+  struct sock_filter allow_syscall[ALLOW_SYSCALL_LEN];
+  int nr = 1;
+  unsigned int id = 1024;
+
+  size_t len = bpf_allow_syscall_args(allow_syscall, nr, id);
+
+  EXPECT_EQ(len, ALLOW_SYSCALL_LEN);
+  EXPECT_ALLOW_SYSCALL_ARGS(allow_syscall, nr, id, JUMP_JT, JUMP_JF);
+}
+
+class ArgFilterTest : public ::testing::Test {
+ protected:
+  virtual void TearDown() { free_label_strings(&labels_); }
+  struct bpf_labels labels_;
+};
+
+TEST_F(ArgFilterTest, arg0_equals) {
+  const char *fragment = "arg0 == 0";
+  int nr = 1;
+  unsigned int id = 0;
+  struct filter_block *block =
+      compile_section(nr, fragment, id, &labels_, NO_LOGGING);
+
+  ASSERT_TRUE(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_TRUE(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);
+
+  /* Third block is a jump and a label (end of AND group). */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_GROUP_END(curr_block);
+
+  /* Fourth block is SECCOMP_RET_KILL. */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_KILL(curr_block);
+
+  /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_ALLOW(curr_block);
+
+  EXPECT_TRUE(curr_block->next == NULL);
+
+  free_block_list(block);
+}
+
+TEST_F(ArgFilterTest, arg0_mask) {
+  const char *fragment = "arg1 & O_RDWR";
+  int nr = 1;
+  unsigned int id = 0;
+  struct filter_block *block =
+      compile_section(nr, fragment, id, &labels_, NO_LOGGING);
+
+  ASSERT_TRUE(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_TRUE(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);
+
+  /* Third block is a jump and a label (end of AND group). */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_GROUP_END(curr_block);
+
+  /* Fourth block is SECCOMP_RET_KILL. */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_KILL(curr_block);
+
+  /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_ALLOW(curr_block);
+
+  EXPECT_TRUE(curr_block->next == NULL);
+
+  free_block_list(block);
+}
+
+TEST_F(ArgFilterTest, 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, &labels_, NO_LOGGING);
+
+  ASSERT_TRUE(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_TRUE(curr_block != NULL);
+  EXPECT_EQ(block->len, 1U);
+  EXPECT_LBL(curr_block->instrs);
+
+  /* Second block is a comparison. */
+  curr_block = block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  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;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_GROUP_END(curr_block);
+
+  /* Fourth block is SECCOMP_RET_KILL. */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_KILL(curr_block);
+
+  /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_ALLOW(curr_block);
+
+  EXPECT_TRUE(curr_block->next == NULL);
+
+  free_block_list(block);
+}
+
+TEST_F(ArgFilterTest, and_or) {
+  const char *fragment = "arg0 == 0 && arg1 == 0 || arg0 == 1";
+  int nr = 1;
+  unsigned int id = 0;
+
+  struct filter_block *block =
+      compile_section(nr, fragment, id, &labels_, NO_LOGGING);
+  ASSERT_TRUE(block != NULL);
+  size_t exp_total_len = 1 + 3 * (BPF_ARG_COMP_LEN + 1) + 2 + 2 + 1 + 2;
+  EXPECT_EQ(block->total_len, exp_total_len);
+
+  /* First block is a label. */
+  struct filter_block *curr_block = block;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_EQ(block->len, 1U);
+  EXPECT_LBL(curr_block->instrs);
+
+  /* Second block is a comparison ("arg0 == 0"). */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_COMP(curr_block);
+
+  /* Third block is a comparison ("arg1 == 0"). */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_COMP(curr_block);
+
+  /* Fourth block is a jump and a label (end of AND group). */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_GROUP_END(curr_block);
+
+  /* Fifth block is a comparison ("arg0 == 1"). */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_COMP(curr_block);
+
+  /* Sixth block is a jump and a label (end of AND group). */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_GROUP_END(curr_block);
+
+  /* Seventh block is SECCOMP_RET_KILL. */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_KILL(curr_block);
+
+  /* Eigth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_ALLOW(curr_block);
+
+  EXPECT_TRUE(curr_block->next == NULL);
+
+  free_block_list(block);
+}
+
+TEST_F(ArgFilterTest, ret_errno) {
+  const char *fragment = "arg0 == 0 || arg0 == 1; return 1";
+  int nr = 1;
+  unsigned int id = 0;
+
+  struct filter_block *block =
+      compile_section(nr, fragment, id, &labels_, NO_LOGGING);
+  ASSERT_TRUE(block != NULL);
+  size_t exp_total_len = 1 + 2 * (BPF_ARG_COMP_LEN + 1) + 2 + 2 + 1 + 2;
+  EXPECT_EQ(block->total_len, exp_total_len);
+
+  /* First block is a label. */
+  struct filter_block *curr_block = block;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_EQ(block->len, 1U);
+  EXPECT_LBL(curr_block->instrs);
+
+  /* Second block is a comparison ("arg0 == 0"). */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(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_TRUE(curr_block != NULL);
+  EXPECT_GROUP_END(curr_block);
+
+  /* Fourth block is a comparison ("arg0 == 1"). */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_COMP(curr_block);
+
+  /* Fifth block is a jump and a label (end of AND group). */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_GROUP_END(curr_block);
+
+  /* Sixth block is SECCOMP_RET_ERRNO. */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_EQ(curr_block->len, 1U);
+  EXPECT_EQ_STMT(curr_block->instrs,
+                 BPF_RET + BPF_K,
+                 SECCOMP_RET_ERRNO | (1 & SECCOMP_RET_DATA));
+
+  /* Seventh block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_ALLOW(curr_block);
+
+  EXPECT_TRUE(curr_block->next == NULL);
+
+  free_block_list(block);
+}
+
+TEST_F(ArgFilterTest, unconditional_errno) {
+  const char *fragment = "return 1";
+  int nr = 1;
+  unsigned int id = 0;
+
+  struct filter_block *block =
+      compile_section(nr, fragment, id, &labels_, NO_LOGGING);
+  ASSERT_TRUE(block != NULL);
+  size_t exp_total_len = 2;
+  EXPECT_EQ(block->total_len, exp_total_len);
+
+  /* First block is a label. */
+  struct filter_block *curr_block = block;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_EQ(block->len, 1U);
+  EXPECT_LBL(curr_block->instrs);
+
+  /* Second block is SECCOMP_RET_ERRNO. */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_EQ(curr_block->len, 1U);
+  EXPECT_EQ_STMT(curr_block->instrs,
+                 BPF_RET + BPF_K,
+                 SECCOMP_RET_ERRNO | (1 & SECCOMP_RET_DATA));
+
+  EXPECT_TRUE(curr_block->next == NULL);
+
+  free_block_list(block);
+}
+
+TEST_F(ArgFilterTest, invalid) {
+  const char *fragment = "argnn == 0";
+  int nr = 1;
+  unsigned int id = 0;
+
+  struct filter_block *block =
+      compile_section(nr, fragment, id, &labels_, NO_LOGGING);
+  ASSERT_TRUE(block == NULL);
+
+  fragment = "arg0 == 0 && arg1 == 1; return errno";
+  block = compile_section(nr, fragment, id, &labels_, NO_LOGGING);
+  ASSERT_TRUE(block == NULL);
+}
+
+TEST_F(ArgFilterTest, log_no_ret_error) {
+  const char *fragment = "arg0 == 0";
+  int nr = 1;
+  unsigned int id = 0;
+  struct filter_block *block =
+      compile_section(nr, fragment, id, &labels_, USE_LOGGING);
+
+  ASSERT_TRUE(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_TRUE(curr_block != NULL);
+  EXPECT_EQ(block->len, 1U);
+  EXPECT_LBL(curr_block->instrs);
+
+  /* Second block is a comparison. */
+  curr_block = block->next;
+  ASSERT_TRUE(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_TRUE(curr_block != NULL);
+  EXPECT_GROUP_END(curr_block);
+
+  /* Fourth block is SECCOMP_RET_TRAP, with no errno. */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_TRAP(curr_block);
+
+  /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_ALLOW(curr_block);
+
+  EXPECT_TRUE(curr_block->next == NULL);
+
+  free_block_list(block);
+}
+
+TEST_F(ArgFilterTest, log_bad_ret_error) {
+  const char *fragment = "arg0 == 0; return";
+  int nr = 1;
+  unsigned int id = 0;
+
+  struct filter_block *block =
+      compile_section(nr, fragment, id, &labels_, NO_LOGGING);
+  ASSERT_TRUE(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_TRUE(curr_block != NULL);
+  EXPECT_EQ(block->len, 1U);
+  EXPECT_LBL(curr_block->instrs);
+
+  /* Second block is a comparison ("arg0 == 0"). */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(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_TRUE(curr_block != NULL);
+  EXPECT_GROUP_END(curr_block);
+
+  /*
+   * Sixth block is NOT SECCOMP_RET_ERRNO, it should be SECCOMP_RET_KILL.
+   */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_KILL(curr_block);
+
+  /* Seventh block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_ALLOW(curr_block);
+
+  EXPECT_TRUE(curr_block->next == NULL);
+
+  free_block_list(block);
+}
+
+TEST_F(ArgFilterTest, no_log_bad_ret_error) {
+  const char *fragment = "arg0 == 0; return";
+  int nr = 1;
+  unsigned int id = 0;
+
+  struct filter_block *block =
+      compile_section(nr, fragment, id, &labels_, USE_LOGGING);
+  ASSERT_TRUE(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_TRUE(curr_block != NULL);
+  EXPECT_EQ(block->len, 1U);
+  EXPECT_LBL(curr_block->instrs);
+
+  /* Second block is a comparison ("arg0 == 0"). */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(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_TRUE(curr_block != NULL);
+  EXPECT_GROUP_END(curr_block);
+
+  /*
+   * Sixth block is *not* SECCOMP_RET_ERRNO, it should be
+   * SECCOMP_RET_TRAP.
+   */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_TRAP(curr_block);
+
+  /* Seventh block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
+  curr_block = curr_block->next;
+  ASSERT_TRUE(curr_block != NULL);
+  EXPECT_ALLOW(curr_block);
+
+  EXPECT_TRUE(curr_block->next == NULL);
+
+  free_block_list(block);
+}
+
+FILE *write_policy_to_pipe(const char *policy, size_t len) {
+  int pipefd[2];
+  if (pipe(pipefd) == -1) {
+    pwarn("pipe(pipefd) failed");
+    return NULL;
+  }
+
+  size_t i = 0;
+  unsigned int attempts = 0;
+  ssize_t ret;
+  while (i < len) {
+    ret = write(pipefd[1], &policy[i], len - i);
+    if (ret == -1) {
+      close(pipefd[0]);
+      close(pipefd[1]);
+      return NULL;
+    }
+
+    /* If we write 0 bytes three times in a row, fail. */
+    if (ret == 0) {
+      if (++attempts >= 3) {
+        close(pipefd[0]);
+        close(pipefd[1]);
+        warn("write() returned 0 three times in a row");
+        return NULL;
+      }
+      continue;
+    }
+
+    attempts = 0;
+    i += (size_t)ret;
+  }
+
+  close(pipefd[1]);
+  return fdopen(pipefd[0], "r");
+}
+
+TEST(FilterTest, seccomp_mode1) {
+  struct sock_fprog actual;
+  const char *policy =
+      "read: 1\n"
+      "write: 1\n"
+      "rt_sigreturn: 1\n"
+      "exit: 1\n";
+
+  FILE *policy_file = write_policy_to_pipe(policy, strlen(policy));
+  ASSERT_TRUE(policy_file != NULL);
+
+  int res = compile_filter(policy_file, &actual, NO_LOGGING);
+  fclose(policy_file);
+
+  /*
+   * Checks return value, filter length, and that the filter
+   * validates arch, loads syscall number, and
+   * only allows expected syscalls.
+   */
+  ASSERT_EQ(res, 0);
+  EXPECT_EQ(actual.len, 13);
+  EXPECT_ARCH_VALIDATION(actual.filter);
+  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN,
+                 BPF_LD + BPF_W + BPF_ABS,
+                 syscall_nr);
+  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 1, __NR_read);
+  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 3, __NR_write);
+  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 5,
+                       __NR_rt_sigreturn);
+  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 7, __NR_exit);
+  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN + 9,
+                 BPF_RET + BPF_K,
+                 SECCOMP_RET_KILL);
+
+  free(actual.filter);
+}
+
+TEST(FilterTest, seccomp_read_write) {
+  struct sock_fprog actual;
+  const char *policy =
+      "read: arg0 == 0\n"
+      "write: arg0 == 1 || arg0 == 2\n"
+      "rt_sigreturn: 1\n"
+      "exit: 1\n";
+
+  FILE *policy_file = write_policy_to_pipe(policy, strlen(policy));
+  ASSERT_TRUE(policy_file != NULL);
+
+  int res = compile_filter(policy_file, &actual, NO_LOGGING);
+  fclose(policy_file);
+
+  /*
+   * Checks return value, filter length, and that the filter
+   * validates arch, loads syscall number, and
+   * only allows expected syscalls, jumping to correct arg filter
+   * offsets.
+   */
+  ASSERT_EQ(res, 0);
+  size_t exp_total_len = 27 + 3 * (BPF_ARG_COMP_LEN + 1);
+  EXPECT_EQ(actual.len, exp_total_len);
+
+  EXPECT_ARCH_VALIDATION(actual.filter);
+  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN,
+                 BPF_LD + BPF_W + BPF_ABS,
+                 syscall_nr);
+  EXPECT_ALLOW_SYSCALL_ARGS(
+      actual.filter + ARCH_VALIDATION_LEN + 1, __NR_read, 7, 0, 0);
+  EXPECT_ALLOW_SYSCALL_ARGS(actual.filter + ARCH_VALIDATION_LEN + 3,
+                            __NR_write,
+                            12 + BPF_ARG_COMP_LEN,
+                            0,
+                            0);
+  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 5,
+                       __NR_rt_sigreturn);
+  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 7, __NR_exit);
+  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN + 9,
+                 BPF_RET + BPF_K,
+                 SECCOMP_RET_KILL);
+
+  free(actual.filter);
+}
+
+TEST(FilterTest, invalid_name) {
+  struct sock_fprog actual;
+  const char *policy = "notasyscall: 1\n";
+
+  FILE *policy_file = write_policy_to_pipe(policy, strlen(policy));
+  ASSERT_TRUE(policy_file != NULL);
+
+  int res = compile_filter(policy_file, &actual, NO_LOGGING);
+  fclose(policy_file);
+  ASSERT_NE(res, 0);
+}
+
+TEST(FilterTest, invalid_arg) {
+  struct sock_fprog actual;
+  const char *policy = "open: argnn ==\n";
+
+  FILE *policy_file = write_policy_to_pipe(policy, strlen(policy));
+  ASSERT_TRUE(policy_file != NULL);
+
+  int res = compile_filter(policy_file, &actual, NO_LOGGING);
+  fclose(policy_file);
+  ASSERT_NE(res, 0);
+}
+
+TEST(FilterTest, nonexistent) {
+  struct sock_fprog actual;
+  int res = compile_filter(NULL, &actual, NO_LOGGING);
+  ASSERT_NE(res, 0);
+}
+
+TEST(FilterTest, log) {
+  struct sock_fprog actual;
+  const char *policy =
+      "read: 1\n"
+      "write: 1\n"
+      "rt_sigreturn: 1\n"
+      "exit: 1\n";
+
+  FILE *policy_file = write_policy_to_pipe(policy, strlen(policy));
+  ASSERT_TRUE(policy_file != NULL);
+
+  int res = compile_filter(policy_file, &actual, USE_LOGGING);
+  fclose(policy_file);
+
+  size_t i;
+  size_t index = 0;
+  /*
+   * Checks return value, filter length, and that the filter
+   * validates arch, loads syscall number, only allows expected syscalls,
+   * and returns TRAP on failure.
+   * NOTE(jorgelo): the filter is longer since we add the syscalls needed
+   * for logging.
+   */
+  ASSERT_EQ(res, 0);
+  EXPECT_EQ(actual.len, 13 + 2 * log_syscalls_len);
+  EXPECT_ARCH_VALIDATION(actual.filter);
+  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN,
+                 BPF_LD + BPF_W + BPF_ABS,
+                 syscall_nr);
+
+  index = ARCH_VALIDATION_LEN + 1;
+  for (i = 0; i < log_syscalls_len; i++)
+    EXPECT_ALLOW_SYSCALL(actual.filter + (index + 2 * i),
+                         lookup_syscall(log_syscalls[i]));
+
+  index += 2 * log_syscalls_len;
+
+  EXPECT_ALLOW_SYSCALL(actual.filter + index, __NR_read);
+  EXPECT_ALLOW_SYSCALL(actual.filter + index + 2, __NR_write);
+  EXPECT_ALLOW_SYSCALL(actual.filter + index + 4, __NR_rt_sigreturn);
+  EXPECT_ALLOW_SYSCALL(actual.filter + index + 6, __NR_exit);
+  EXPECT_EQ_STMT(actual.filter + index + 8, BPF_RET + BPF_K, SECCOMP_RET_TRAP);
+
+  free(actual.filter);
+}
diff --git a/syscall_filter_unittest_macros.h b/syscall_filter_unittest_macros.h
new file mode 100644
index 0000000..ec86374
--- /dev/null
+++ b/syscall_filter_unittest_macros.h
@@ -0,0 +1,99 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* BPF testing macros. */
+#define EXPECT_EQ_BLOCK(_block, _code, _k, _jt, _jf)	\
+do {	\
+	EXPECT_EQ((_block)->code, _code);		\
+	EXPECT_EQ((_block)->k, (unsigned int)(_k));	\
+	EXPECT_EQ((_block)->jt, _jt);			\
+	EXPECT_EQ((_block)->jf, _jf);			\
+} while (0)
+
+#define EXPECT_EQ_STMT(_block, _code, _k) \
+	EXPECT_EQ_BLOCK(_block, _code, _k, 0, 0)
+
+#define EXPECT_COMP(_block) \
+do {	\
+	EXPECT_EQ((_block)->len, BPF_ARG_COMP_LEN + 1);			\
+	EXPECT_EQ((_block)->instrs->code, BPF_LD+BPF_W+BPF_ABS);	\
+} while (0)
+
+#define EXPECT_LBL(_block) \
+do {	\
+	EXPECT_TRUE((_block)->code == (BPF_JMP+BPF_JA));	\
+	EXPECT_TRUE((_block)->jt == LABEL_JT);			\
+	EXPECT_TRUE((_block)->jf == LABEL_JF);			\
+} while (0)
+
+#define EXPECT_JUMP_LBL(_block) \
+do {	\
+	EXPECT_EQ((_block)->code, BPF_JMP+BPF_JA);	\
+	EXPECT_EQ((_block)->jt, JUMP_JT);		\
+	EXPECT_EQ((_block)->jf, JUMP_JF);		\
+} while (0)
+
+#define EXPECT_GROUP_END(_block) \
+do {	\
+	EXPECT_EQ((_block)->len, 2U);			\
+	EXPECT_JUMP_LBL(&(_block)->instrs[0]);		\
+	EXPECT_LBL(&(_block)->instrs[1]);		\
+} while (0)
+
+#define EXPECT_KILL(_block) \
+do {	\
+	EXPECT_EQ((_block)->len, 1U);				\
+	EXPECT_EQ_STMT((_block)->instrs,			\
+			BPF_RET+BPF_K, SECCOMP_RET_KILL);	\
+} while (0)
+
+#define EXPECT_TRAP(_block) \
+do {	\
+	EXPECT_EQ((_block)->len, 1U);				\
+	EXPECT_EQ_STMT((_block)->instrs,			\
+			BPF_RET+BPF_K, SECCOMP_RET_TRAP);	\
+} while (0)
+
+#define EXPECT_ALLOW(_block) \
+do {	\
+	EXPECT_EQ((_block)->len, 2U);				\
+	EXPECT_LBL(&(_block)->instrs[0]);			\
+	EXPECT_EQ_STMT(&(_block)->instrs[1],			\
+			BPF_RET+BPF_K, SECCOMP_RET_ALLOW);	\
+} while (0)
+
+#define EXPECT_ARCH_VALIDATION(_filter) \
+do {	\
+	EXPECT_EQ_STMT(&(_filter)[0], BPF_LD+BPF_W+BPF_ABS, arch_nr);	\
+	EXPECT_EQ_BLOCK(&(_filter)[1],					\
+			BPF_JMP+BPF_JEQ+BPF_K, ARCH_NR, SKIP, NEXT);	\
+	EXPECT_EQ_STMT(&(_filter)[2], BPF_RET+BPF_K, SECCOMP_RET_KILL);	\
+} while (0)
+
+#define EXPECT_ALLOW_SYSCALL(_filter, _nr) \
+do {	\
+	EXPECT_EQ_BLOCK(&(_filter)[0],					\
+			BPF_JMP+BPF_JEQ+BPF_K, (_nr), NEXT, SKIP);	\
+	EXPECT_EQ_STMT(&(_filter)[1],					\
+			BPF_RET+BPF_K, SECCOMP_RET_ALLOW);		\
+} while (0)
+
+#define EXPECT_ALLOW_SYSCALL_ARGS(_filter, _nr, _id, _jt, _jf) \
+do {	\
+	EXPECT_EQ_BLOCK(&(_filter)[0],					\
+			BPF_JMP+BPF_JEQ+BPF_K, (_nr), NEXT, SKIP);	\
+	EXPECT_EQ_BLOCK(&(_filter)[1],					\
+			BPF_JMP+BPF_JA, (_id), (_jt), (_jf));		\
+} while (0)