merge in oc-release history after reset to master
diff --git a/syscall_filter.c b/syscall_filter.c
index 3318052..956fe21 100644
--- a/syscall_filter.c
+++ b/syscall_filter.c
@@ -164,6 +164,11 @@
 	return get_label_id(labels, lbl_str);
 }
 
+int is_implicit_relative_path(const char *filename)
+{
+	return filename[0] != '/' && (filename[0] != '.' || filename[1] != '/');
+}
+
 int compile_atom(struct filter_block *head, char *atom,
 		 struct bpf_labels *labels, int nr, int grp_idx)
 {
@@ -428,9 +433,57 @@
 	return head;
 }
 
+int parse_include_statement(char *policy_line, unsigned int include_level,
+			    const char **ret_filename)
+{
+	if (strncmp("@include", policy_line, strlen("@include")) != 0) {
+		warn("invalid statement '%s'", policy_line);
+		return -1;
+	}
+
+	if (policy_line[strlen("@include")] != ' ') {
+		warn("invalid include statement '%s'", policy_line);
+		return -1;
+	}
+
+	/*
+	 * Disallow nested includes: only the initial policy file can have
+	 * @include statements.
+	 * Nested includes are not currently necessary and make the policy
+	 * harder to understand.
+	 */
+	if (include_level > 0) {
+		warn("@include statement nested too deep");
+		return -1;
+	}
+
+	char *statement = policy_line;
+	/* Discard "@include" token. */
+	(void)strsep(&statement, " ");
+
+	/*
+	 * compile_filter() below receives a FILE*, so it's not trivial to open
+	 * included files relative to the initial policy filename.
+	 * To avoid mistakes, force the included file path to be absolute
+	 * (start with '/'), or to explicitly load the file relative to CWD by
+	 * using './'.
+	 */
+	const char *filename = statement;
+	if (is_implicit_relative_path(filename)) {
+		warn("compile_file: implicit relative path '%s' not supported, "
+		     "use './%s'",
+		     filename, filename);
+		return -1;
+	}
+
+	*ret_filename = filename;
+	return 0;
+}
+
 int compile_file(FILE *policy_file, struct filter_block *head,
 		 struct filter_block **arg_blocks, struct bpf_labels *labels,
-		 int use_ret_trap, int allow_logging)
+		 int use_ret_trap, int allow_logging,
+		 unsigned int include_level)
 {
 	/*
 	 * Loop through all the lines in the policy file.
@@ -442,27 +495,62 @@
 	 */
 	char *line = NULL;
 	size_t len = 0;
+	int ret = 0;
+
 	while (getline(&line, &len, policy_file) != -1) {
 		char *policy_line = line;
-		char *syscall_name = strsep(&policy_line, ":");
-		int nr = -1;
-
-		syscall_name = strip(syscall_name);
+		policy_line = strip(policy_line);
 
 		/* Allow comments and empty lines. */
-		if (*syscall_name == '#' || *syscall_name == '\0') {
+		if (*policy_line == '#' || *policy_line == '\0') {
 			/* Reuse |line| in the next getline() call. */
 			continue;
 		}
 
+		/* Allow @include statements. */
+		if (*policy_line == '@') {
+			const char *filename = NULL;
+			if (parse_include_statement(policy_line, include_level,
+						    &filename) != 0) {
+				warn("compile_file: failed to parse include "
+				     "statement");
+				ret = -1;
+				goto free_line;
+			}
+
+			FILE *included_file = fopen(filename, "re");
+			if (included_file == NULL) {
+				pwarn("compile_file: fopen('%s') failed",
+				      filename);
+				ret = -1;
+				goto free_line;
+			}
+			if (compile_file(included_file, head, arg_blocks,
+					 labels, use_ret_trap, allow_logging,
+					 ++include_level) == -1) {
+				warn("compile_file: '@include %s' failed",
+				     filename);
+				fclose(included_file);
+				ret = -1;
+				goto free_line;
+			}
+			fclose(included_file);
+			continue;
+		}
+
+		/*
+		 * If it's not a comment, or an empty line, or an @include
+		 * statement, treat |policy_line| as a regular policy line.
+		 */
+		char *syscall_name = strsep(&policy_line, ":");
 		policy_line = strip(policy_line);
 		if (*policy_line == '\0') {
 			warn("compile_file: empty policy line");
-			free(line);
-			return -1;
+			ret = -1;
+			goto free_line;
 		}
 
-		nr = lookup_syscall(syscall_name);
+		int nr = lookup_syscall(syscall_name);
 		if (nr < 0) {
 			warn("compile_file: nonexistent syscall '%s'",
 			     syscall_name);
@@ -481,8 +569,8 @@
 				/* Reuse |line| in the next getline() call. */
 				continue;
 			}
-			free(line);
-			return -1;
+			ret = -1;
+			goto free_line;
 		}
 
 		/*
@@ -511,8 +599,8 @@
 				if (*arg_blocks) {
 					free_block_list(*arg_blocks);
 				}
-				free(line);
-				return -1;
+				ret = -1;
+				goto free_line;
 			}
 
 			if (*arg_blocks) {
@@ -523,8 +611,10 @@
 		}
 		/* Reuse |line| in the next getline() call. */
 	}
+
+free_line:
 	free(line);
-	return 0;
+	return ret;
 }
 
 int compile_filter(FILE *initial_file, struct sock_fprog *prog,
@@ -556,7 +646,7 @@
 		allow_logging_syscalls(head);
 
 	if (compile_file(initial_file, head, &arg_blocks, &labels, use_ret_trap,
-			 allow_logging) != 0) {
+			 allow_logging, 0 /* include_level */) != 0) {
 		warn("compile_filter: compile_file() failed");
 		free_block_list(head);
 		free_block_list(arg_blocks);
diff --git a/syscall_filter.h b/syscall_filter.h
index 3bfbfee..d15e8a9 100644
--- a/syscall_filter.h
+++ b/syscall_filter.h
@@ -32,7 +32,8 @@
 					 int do_ret_trap);
 int compile_file(FILE *policy_file, struct filter_block *head,
 		 struct filter_block **arg_blocks, struct bpf_labels *labels,
-		 int use_ret_trap, int allow_logging);
+		 int use_ret_trap, int allow_logging,
+		 unsigned int include_level);
 int compile_filter(FILE *policy_file, struct sock_fprog *prog, int do_ret_trap,
 		   int add_logging_syscalls);
 
diff --git a/syscall_filter_unittest.cc b/syscall_filter_unittest.cc
index 4a80976..85c8a55 100644
--- a/syscall_filter_unittest.cc
+++ b/syscall_filter_unittest.cc
@@ -948,7 +948,6 @@
 };
 
 TEST_F(FileTest, seccomp_mode1) {
-  // struct sock_fprog actual;
   const char *policy =
       "read: 1\n"
       "write: 1\n"
@@ -958,7 +957,7 @@
   FILE *policy_file = write_policy_to_pipe(policy, strlen(policy));
   ASSERT_NE(policy_file, nullptr);
   int res = compile_file(
-      policy_file, head_, &arg_blocks_, &labels_, USE_RET_KILL, NO_LOGGING);
+      policy_file, head_, &arg_blocks_, &labels_, USE_RET_KILL, NO_LOGGING, 0);
   fclose(policy_file);
 
   /*
@@ -993,7 +992,7 @@
     FILE *policy_file = write_policy_to_pipe(policy, strlen(policy));
   ASSERT_NE(policy_file, nullptr);
   int res = compile_file(
-      policy_file, head_, &arg_blocks_, &labels_, USE_RET_KILL, NO_LOGGING);
+      policy_file, head_, &arg_blocks_, &labels_, USE_RET_KILL, NO_LOGGING, 0);
   fclose(policy_file);
 
   /*
@@ -1174,7 +1173,7 @@
   struct sock_fprog actual;
   const char* policy = "open:\n";
 
-  FILE* policy_file = write_policy_to_pipe(policy, strlen(policy));
+  FILE *policy_file = write_policy_to_pipe(policy, strlen(policy));
   ASSERT_NE(policy_file, nullptr);
 
   int res = compile_filter(policy_file, &actual, USE_RET_KILL, NO_LOGGING);
@@ -1186,7 +1185,7 @@
   struct sock_fprog actual;
   const char* policy = "open:\t    \n";
 
-  FILE* policy_file = write_policy_to_pipe(policy, strlen(policy));
+  FILE *policy_file = write_policy_to_pipe(policy, strlen(policy));
   ASSERT_NE(policy_file, nullptr);
 
   int res = compile_filter(policy_file, &actual, USE_RET_KILL, NO_LOGGING);
@@ -1315,3 +1314,239 @@
 
   free(actual.filter);
 }
+
+TEST(FilterTest, include_invalid_token) {
+  struct sock_fprog actual;
+  const char *invalid_token = "@unclude ./test/seccomp.policy\n";
+
+  FILE *policy_file =
+      write_policy_to_pipe(invalid_token, strlen(invalid_token));
+  ASSERT_NE(policy_file, nullptr);
+  int res = compile_filter(policy_file, &actual, USE_RET_KILL, NO_LOGGING);
+  fclose(policy_file);
+  EXPECT_NE(res, 0);
+}
+
+TEST(FilterTest, include_no_space) {
+  struct sock_fprog actual;
+  const char *no_space = "@includetest/seccomp.policy\n";
+
+  FILE *policy_file = write_policy_to_pipe(no_space, strlen(no_space));
+  ASSERT_NE(policy_file, nullptr);
+  int res = compile_filter(policy_file, &actual, USE_RET_KILL, NO_LOGGING);
+  fclose(policy_file);
+  EXPECT_NE(res, 0);
+}
+
+TEST(FilterTest, include_double_token) {
+  struct sock_fprog actual;
+  const char *double_token = "@includeinclude ./test/seccomp.policy\n";
+
+  FILE *policy_file = write_policy_to_pipe(double_token, strlen(double_token));
+  ASSERT_NE(policy_file, nullptr);
+  int res = compile_filter(policy_file, &actual, USE_RET_KILL, NO_LOGGING);
+  fclose(policy_file);
+  EXPECT_NE(res, 0);
+}
+
+TEST(FilterTest, include_no_file) {
+  struct sock_fprog actual;
+  const char *no_file = "@include\n";
+
+  FILE *policy_file = write_policy_to_pipe(no_file, strlen(no_file));
+  ASSERT_NE(policy_file, nullptr);
+  int res = compile_filter(policy_file, &actual, USE_RET_KILL, NO_LOGGING);
+  fclose(policy_file);
+  EXPECT_NE(res, 0);
+}
+
+TEST(FilterTest, include_space_no_file) {
+  struct sock_fprog actual;
+  const char *space_no_file = "@include \n";
+
+  FILE *policy_file =
+      write_policy_to_pipe(space_no_file, strlen(space_no_file));
+  ASSERT_NE(policy_file, nullptr);
+  int res = compile_filter(policy_file, &actual, USE_RET_KILL, NO_LOGGING);
+  fclose(policy_file);
+  EXPECT_NE(res, 0);
+}
+
+TEST(FilterTest, include_implicit_relative_path) {
+  struct sock_fprog actual;
+  const char *implicit_relative_path = "@include test/seccomp.policy\n";
+
+  FILE *policy_file = write_policy_to_pipe(implicit_relative_path,
+                                           strlen(implicit_relative_path));
+  ASSERT_NE(policy_file, nullptr);
+  int res = compile_filter(policy_file, &actual, USE_RET_KILL, NO_LOGGING);
+  fclose(policy_file);
+  EXPECT_NE(res, 0);
+}
+
+TEST(FilterTest, include_extra_text) {
+  struct sock_fprog actual;
+  const char *extra_text = "@include /some/file: sneaky comment\n";
+
+  FILE *policy_file =
+      write_policy_to_pipe(extra_text, strlen(extra_text));
+  ASSERT_NE(policy_file, nullptr);
+  int res = compile_filter(policy_file, &actual, USE_RET_KILL, NO_LOGGING);
+  fclose(policy_file);
+  EXPECT_NE(res, 0);
+}
+
+TEST(FilterTest, include_split_filename) {
+  struct sock_fprog actual;
+  const char *split_filename = "@include /some/file:colon.policy\n";
+
+  FILE *policy_file =
+      write_policy_to_pipe(split_filename, strlen(split_filename));
+  ASSERT_NE(policy_file, nullptr);
+  int res = compile_filter(policy_file, &actual, USE_RET_KILL, NO_LOGGING);
+  fclose(policy_file);
+  EXPECT_NE(res, 0);
+}
+
+TEST(FilterTest, include_nonexistent_file) {
+  struct sock_fprog actual;
+  const char *include_policy = "@include ./nonexistent.policy\n";
+
+  FILE *policy_file =
+      write_policy_to_pipe(include_policy, strlen(include_policy));
+  ASSERT_NE(policy_file, nullptr);
+
+  int res = compile_filter(policy_file, &actual, USE_RET_KILL, NO_LOGGING);
+  fclose(policy_file);
+
+  ASSERT_NE(res, 0);
+}
+
+// TODO(jorgelo): Android unit tests don't currently support data files.
+// Re-enable by creating a temporary policy file at runtime.
+#if !defined(__ANDROID__)
+
+TEST(FilterTest, include) {
+  struct sock_fprog compiled_plain;
+  struct sock_fprog compiled_with_include;
+
+  const char *policy_plain =
+      "read: 1\n"
+      "write: 1\n"
+      "rt_sigreturn: 1\n"
+      "exit: 1\n";
+
+  const char *policy_with_include = "@include ./test/seccomp.policy\n";
+
+  FILE *file_plain = write_policy_to_pipe(policy_plain, strlen(policy_plain));
+  ASSERT_NE(file_plain, nullptr);
+  int res_plain =
+      compile_filter(file_plain, &compiled_plain, USE_RET_KILL, NO_LOGGING);
+  fclose(file_plain);
+
+  FILE *file_with_include =
+      write_policy_to_pipe(policy_with_include, strlen(policy_with_include));
+  ASSERT_NE(file_with_include, nullptr);
+  int res_with_include = compile_filter(
+      file_with_include, &compiled_with_include, USE_RET_KILL, NO_LOGGING);
+  fclose(file_with_include);
+
+  /*
+   * Checks that filter length is the same for a plain policy and an equivalent
+   * policy with an @include statement. Also checks that the filter generated
+   * from the policy with an @include statement is exactly the same as one
+   * generated from a plain policy.
+   */
+  ASSERT_EQ(res_plain, 0);
+  ASSERT_EQ(res_with_include, 0);
+
+  EXPECT_EQ(compiled_plain.len, 13);
+  EXPECT_EQ(compiled_with_include.len, 13);
+
+  EXPECT_ARCH_VALIDATION(compiled_with_include.filter);
+  EXPECT_EQ_STMT(compiled_with_include.filter + ARCH_VALIDATION_LEN,
+                 BPF_LD + BPF_W + BPF_ABS,
+                 syscall_nr);
+  EXPECT_ALLOW_SYSCALL(compiled_with_include.filter + ARCH_VALIDATION_LEN + 1,
+                       __NR_read);
+  EXPECT_ALLOW_SYSCALL(compiled_with_include.filter + ARCH_VALIDATION_LEN + 3,
+                       __NR_write);
+  EXPECT_ALLOW_SYSCALL(compiled_with_include.filter + ARCH_VALIDATION_LEN + 5,
+                       __NR_rt_sigreturn);
+  EXPECT_ALLOW_SYSCALL(compiled_with_include.filter + ARCH_VALIDATION_LEN + 7,
+                       __NR_exit);
+  EXPECT_EQ_STMT(compiled_with_include.filter + ARCH_VALIDATION_LEN + 9,
+                 BPF_RET + BPF_K,
+                 SECCOMP_RET_KILL);
+
+  free(compiled_plain.filter);
+  free(compiled_with_include.filter);
+}
+
+TEST(FilterTest, include_same_syscalls) {
+  struct sock_fprog actual;
+  const char *policy =
+      "read: 1\n"
+      "write: 1\n"
+      "rt_sigreturn: 1\n"
+      "exit: 1\n"
+      "@include ./test/seccomp.policy\n";
+
+  FILE *policy_file =
+      write_policy_to_pipe(policy, strlen(policy));
+  ASSERT_NE(policy_file, nullptr);
+
+  int res = compile_filter(policy_file, &actual, USE_RET_KILL, NO_LOGGING);
+  fclose(policy_file);
+
+  ASSERT_EQ(res, 0);
+  EXPECT_EQ(actual.len,
+            ARCH_VALIDATION_LEN + 1 /* load syscall nr */ +
+                2 * 8 /* check syscalls twice */ + 1 /* filter return */);
+  free(actual.filter);
+}
+
+TEST(FilterTest, include_invalid_policy) {
+  struct sock_fprog actual;
+  const char *policy =
+      "read: 1\n"
+      "write: 1\n"
+      "rt_sigreturn: 1\n"
+      "exit: 1\n"
+      "@include ./test/invalid_syscall_name.policy\n";
+
+  FILE *policy_file =
+      write_policy_to_pipe(policy, strlen(policy));
+  ASSERT_NE(policy_file, nullptr);
+
+  /* Ensure the included (invalid) policy file exists. */
+  FILE *included_file = fopen("./test/invalid_syscall_name.policy", "r");
+  ASSERT_NE(included_file, nullptr);
+  fclose(included_file);
+
+  int res = compile_filter(policy_file, &actual, USE_RET_KILL, NO_LOGGING);
+  fclose(policy_file);
+
+  ASSERT_NE(res, 0);
+}
+
+TEST(FilterTest, include_nested) {
+  struct sock_fprog actual;
+  const char *policy = "@include ./test/nested.policy\n";
+
+  FILE *policy_file =
+      write_policy_to_pipe(policy, strlen(policy));
+  ASSERT_NE(policy_file, nullptr);
+
+  /* Ensure the policy file exists. */
+  FILE *included_file = fopen("./test/nested.policy", "r");
+  ASSERT_NE(included_file, nullptr);
+  fclose(included_file);
+
+  int res = compile_filter(policy_file, &actual, USE_RET_KILL, NO_LOGGING);
+  fclose(policy_file);
+
+  ASSERT_NE(res, 0);
+}
+
+#endif  // !__ANDROID__
diff --git a/test/nested.policy b/test/nested.policy
new file mode 100644
index 0000000..df4491b
--- /dev/null
+++ b/test/nested.policy
@@ -0,0 +1 @@
+@include ./test/nested.policy