diff options
author | Jorge Lucangeli Obes <jorgelo@google.com> | 2017-03-21 18:32:59 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2017-03-21 18:32:59 +0000 |
commit | 19cbdd29f9cec75d39ec304bc16c31f72d20971e (patch) | |
tree | a0301307d81a286b5c4577e9ab9b3912c6266168 | |
parent | f9d1827377770395b8cd4e01253dbe959124164d (diff) | |
parent | 45932a51abc18f3daddba7776fcfe7d3517da68c (diff) | |
download | minijail-19cbdd29f9cec75d39ec304bc16c31f72d20971e.tar.gz |
syscall_filter: Refactor 'compile_file' out of 'compile_filter'.
am: 45932a51ab
Change-Id: I64ebb2949590f8132bd9652105af5fe6a9933c8a
-rw-r--r-- | Android.mk | 2 | ||||
-rw-r--r-- | bpf.c | 4 | ||||
-rw-r--r-- | parse_seccomp_policy.cc | 2 | ||||
-rw-r--r-- | syscall_filter.c | 127 | ||||
-rw-r--r-- | syscall_filter.h | 14 | ||||
-rw-r--r-- | syscall_filter_unittest.cc | 220 | ||||
-rw-r--r-- | syscall_filter_unittest_macros.h | 8 |
7 files changed, 274 insertions, 103 deletions
@@ -195,7 +195,7 @@ 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_gtest +# out/host/linux-x86/nativetest(64)/syscall_filter_unittest_gtest/syscall_filter_unittest_gtest # ========================================================= include $(CLEAR_VARS) LOCAL_MODULE := syscall_filter_unittest_gtest @@ -267,8 +267,9 @@ int bpf_label_id(struct bpf_labels *labels, const char *label) } end = begin + labels->count; for (id = 0; begin < end; ++begin, ++id) { - if (!strcmp(label, begin->label)) + if (!strcmp(label, begin->label)) { return id; + } } /* The label wasn't found. Insert it only if there's space. */ @@ -284,7 +285,6 @@ int bpf_label_id(struct bpf_labels *labels, const char *label) return id; } -/* Free label strings. */ void free_label_strings(struct bpf_labels *labels) { if (labels->count == 0) diff --git a/parse_seccomp_policy.cc b/parse_seccomp_policy.cc index c50b79e..a9daed0 100644 --- a/parse_seccomp_policy.cc +++ b/parse_seccomp_policy.cc @@ -32,7 +32,7 @@ int main(int argc, char **argv) { } struct sock_fprog fp; - int res = compile_filter(f, &fp, 0); + int res = compile_filter(f, &fp, 0, 0); if (res != 0) { die("compile_filter failed"); } diff --git a/syscall_filter.c b/syscall_filter.c index ad84921..3318052 100644 --- a/syscall_filter.c +++ b/syscall_filter.c @@ -12,14 +12,13 @@ #include "util.h" /* clang-format off */ -#define MAX_LINE_LENGTH 1024 #define MAX_POLICY_LINE_LENGTH 1024 #define ONE_INSTR 1 #define TWO_INSTRS 2 /* clang-format on */ -int seccomp_can_softfail() +int seccomp_can_softfail(void) { #if defined(USE_SECCOMP_SOFTFAIL) return 1; @@ -51,7 +50,7 @@ struct sock_filter *new_instr_buf(size_t count) return buf; } -struct filter_block *new_filter_block() +struct filter_block *new_filter_block(void) { struct filter_block *block = calloc(1, sizeof(struct filter_block)); if (!block) @@ -278,10 +277,10 @@ int compile_errno(struct filter_block *head, char *ret_errno, int use_ret_trap) return 0; } -struct filter_block *compile_section(int nr, const char *policy_line, - unsigned int entry_lbl_id, - struct bpf_labels *labels, - int use_ret_trap) +struct filter_block *compile_policy_line(int nr, const char *policy_line, + unsigned int entry_lbl_id, + struct bpf_labels *labels, + int use_ret_trap) { /* * |policy_line| should be an expression of the form: @@ -429,35 +428,10 @@ struct filter_block *compile_section(int nr, const char *policy_line, return head; } -int compile_filter(FILE *policy_file, struct sock_fprog *prog, int use_ret_trap, - int allow_logging) +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) { - char line[MAX_LINE_LENGTH]; - int line_count = 0; - - struct bpf_labels labels; - labels.count = 0; - - if (!policy_file) - return -1; - - struct filter_block *head = new_filter_block(); - struct filter_block *arg_blocks = NULL; - - /* Start filter by validating arch. */ - struct sock_filter *valid_arch = new_instr_buf(ARCH_VALIDATION_LEN); - size_t len = bpf_validate_arch(valid_arch); - append_filter_block(head, valid_arch, len); - - /* Load syscall number. */ - struct sock_filter *load_nr = new_instr_buf(ONE_INSTR); - len = bpf_load_syscall_nr(load_nr); - append_filter_block(head, load_nr, len); - - /* If logging failures, allow the necessary syscalls first. */ - if (allow_logging) - allow_logging_syscalls(head); - /* * Loop through all the lines in the policy file. * Build a jump table for the syscall number. @@ -466,8 +440,9 @@ int compile_filter(FILE *policy_file, struct sock_fprog *prog, int use_ret_trap, * Chain the filter sections together and dump them into * the final buffer at the end. */ - while (fgets(line, sizeof(line), policy_file)) { - ++line_count; + char *line = NULL; + size_t len = 0; + while (getline(&line, &len, policy_file) != -1) { char *policy_line = line; char *syscall_name = strsep(&policy_line, ":"); int nr = -1; @@ -475,18 +450,21 @@ int compile_filter(FILE *policy_file, struct sock_fprog *prog, int use_ret_trap, syscall_name = strip(syscall_name); /* Allow comments and empty lines. */ - if (*syscall_name == '#' || *syscall_name == '\0') + if (*syscall_name == '#' || *syscall_name == '\0') { + /* Reuse |line| in the next getline() call. */ continue; + } - if (strlen(policy_line) == 0) { - warn("compile_filter: empty policy line"); - free_block_list(head); + policy_line = strip(policy_line); + if (*policy_line == '\0') { + warn("compile_file: empty policy line"); + free(line); return -1; } nr = lookup_syscall(syscall_name); if (nr < 0) { - warn("compile_filter: nonexistent syscall '%s'", + warn("compile_file: nonexistent syscall '%s'", syscall_name); if (allow_logging) { /* @@ -500,14 +478,13 @@ int compile_filter(FILE *policy_file, struct sock_fprog *prog, int use_ret_trap, * syscall is made, so there's no added attack * surface. */ + /* Reuse |line| in the next getline() call. */ continue; } - free_block_list(head); + free(line); return -1; } - policy_line = strip(policy_line); - /* * For each syscall, add either a simple ALLOW, * or an arg filter block. @@ -520,31 +497,71 @@ int compile_filter(FILE *policy_file, struct sock_fprog *prog, int use_ret_trap, * Create and jump to the label that will hold * the arg filter block. */ - unsigned int id = bpf_label_id(&labels, syscall_name); + unsigned int id = bpf_label_id(labels, syscall_name); struct sock_filter *nr_comp = new_instr_buf(ALLOW_SYSCALL_LEN); bpf_allow_syscall_args(nr_comp, nr, id); append_filter_block(head, nr_comp, ALLOW_SYSCALL_LEN); /* Build the arg filter block. */ - struct filter_block *block = compile_section( - nr, policy_line, id, &labels, use_ret_trap); + struct filter_block *block = compile_policy_line( + nr, policy_line, id, labels, use_ret_trap); if (!block) { - if (arg_blocks) { - free_block_list(arg_blocks); + if (*arg_blocks) { + free_block_list(*arg_blocks); } - free_label_strings(&labels); - free_block_list(head); + free(line); return -1; } - if (arg_blocks) { - extend_filter_block_list(arg_blocks, block); + if (*arg_blocks) { + extend_filter_block_list(*arg_blocks, block); } else { - arg_blocks = block; + *arg_blocks = block; } } + /* Reuse |line| in the next getline() call. */ + } + free(line); + return 0; +} + +int compile_filter(FILE *initial_file, struct sock_fprog *prog, + int use_ret_trap, int allow_logging) +{ + struct bpf_labels labels; + labels.count = 0; + + if (!initial_file) { + warn("compile_filter: |initial_file| is NULL"); + return -1; + } + + struct filter_block *head = new_filter_block(); + struct filter_block *arg_blocks = NULL; + + /* Start filter by validating arch. */ + struct sock_filter *valid_arch = new_instr_buf(ARCH_VALIDATION_LEN); + size_t len = bpf_validate_arch(valid_arch); + append_filter_block(head, valid_arch, len); + + /* Load syscall number. */ + struct sock_filter *load_nr = new_instr_buf(ONE_INSTR); + len = bpf_load_syscall_nr(load_nr); + append_filter_block(head, load_nr, len); + + /* If logging failures, allow the necessary syscalls first. */ + if (allow_logging) + allow_logging_syscalls(head); + + if (compile_file(initial_file, head, &arg_blocks, &labels, use_ret_trap, + allow_logging) != 0) { + warn("compile_filter: compile_file() failed"); + free_block_list(head); + free_block_list(arg_blocks); + free_label_strings(&labels); + return -1; } /* diff --git a/syscall_filter.h b/syscall_filter.h index 4625d80..3bfbfee 100644 --- a/syscall_filter.h +++ b/syscall_filter.h @@ -26,18 +26,22 @@ struct filter_block { struct bpf_labels; -struct filter_block *compile_section(int nr, const char *policy_line, - unsigned int label_id, - struct bpf_labels *labels, - int do_ret_trap); +struct filter_block *compile_policy_line(int nr, const char *policy_line, + unsigned int label_id, + struct bpf_labels *labels, + 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 compile_filter(FILE *policy_file, struct sock_fprog *prog, int do_ret_trap, int add_logging_syscalls); +struct filter_block *new_filter_block(void); int flatten_block_list(struct filter_block *head, struct sock_filter *filter, size_t index, size_t cap); void free_block_list(struct filter_block *head); -int seccomp_can_softfail(); +int seccomp_can_softfail(void); #ifdef __cplusplus }; /* extern "C" */ diff --git a/syscall_filter_unittest.cc b/syscall_filter_unittest.cc index 98a1835..4a80976 100644 --- a/syscall_filter_unittest.cc +++ b/syscall_filter_unittest.cc @@ -347,7 +347,17 @@ TEST_F(ArgFilterTest, empty_atom) { unsigned int id = 0; struct filter_block* block = - compile_section(nr, fragment, id, &labels_, NO_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, NO_LOGGING); + ASSERT_EQ(block, nullptr); +} + +TEST_F(ArgFilterTest, whitespace_atom) { + const char* fragment = "\t "; + int nr = 1; + unsigned int id = 0; + + struct filter_block* block = + compile_policy_line(nr, fragment, id, &labels_, NO_LOGGING); ASSERT_EQ(block, nullptr); } @@ -357,7 +367,7 @@ TEST_F(ArgFilterTest, no_comparison) { unsigned int id = 0; struct filter_block* block = - compile_section(nr, fragment, id, &labels_, NO_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, NO_LOGGING); ASSERT_EQ(block, nullptr); } @@ -367,7 +377,7 @@ TEST_F(ArgFilterTest, no_constant) { unsigned int id = 0; struct filter_block* block = - compile_section(nr, fragment, id, &labels_, NO_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, NO_LOGGING); ASSERT_EQ(block, nullptr); } @@ -376,7 +386,7 @@ TEST_F(ArgFilterTest, arg0_equals) { int nr = 1; unsigned int id = 0; struct filter_block *block = - compile_section(nr, fragment, id, &labels_, NO_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, NO_LOGGING); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; @@ -385,11 +395,11 @@ TEST_F(ArgFilterTest, arg0_equals) { /* First block is a label. */ struct filter_block *curr_block = block; ASSERT_NE(curr_block, nullptr); - EXPECT_EQ(block->len, 1U); + EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison. */ - curr_block = block->next; + curr_block = curr_block->next; EXPECT_COMP(curr_block); /* Third block is a jump and a label (end of AND group). */ @@ -417,7 +427,7 @@ TEST_F(ArgFilterTest, arg0_mask) { int nr = 1; unsigned int id = 0; struct filter_block *block = - compile_section(nr, fragment, id, &labels_, NO_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, NO_LOGGING); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; @@ -426,11 +436,11 @@ TEST_F(ArgFilterTest, arg0_mask) { /* First block is a label. */ struct filter_block *curr_block = block; ASSERT_NE(curr_block, nullptr); - EXPECT_EQ(block->len, 1U); + EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison. */ - curr_block = block->next; + curr_block = curr_block->next; EXPECT_COMP(curr_block); /* Third block is a jump and a label (end of AND group). */ @@ -458,7 +468,7 @@ TEST_F(ArgFilterTest, arg0_flag_set_inclusion) { int nr = 1; unsigned int id = 0; struct filter_block *block = - compile_section(nr, fragment, id, &labels_, NO_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, NO_LOGGING); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; @@ -467,11 +477,11 @@ TEST_F(ArgFilterTest, arg0_flag_set_inclusion) { /* First block is a label. */ struct filter_block *curr_block = block; ASSERT_NE(curr_block, nullptr); - EXPECT_EQ(block->len, 1U); + EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison. */ - curr_block = block->next; + curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_COMP(curr_block); @@ -501,7 +511,7 @@ TEST_F(ArgFilterTest, arg0_eq_mask) { unsigned int id = 0; struct filter_block *block = - compile_section(nr, fragment, id, &labels_, NO_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, NO_LOGGING); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; @@ -510,11 +520,11 @@ TEST_F(ArgFilterTest, arg0_eq_mask) { /* First block is a label. */ struct filter_block *curr_block = block; ASSERT_NE(curr_block, nullptr); - EXPECT_EQ(block->len, 1U); + EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison. */ - curr_block = block->next; + curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_COMP(curr_block); EXPECT_EQ(curr_block->instrs[BPF_ARG_COMP_LEN - 1].k, @@ -546,7 +556,7 @@ TEST_F(ArgFilterTest, and_or) { unsigned int id = 0; struct filter_block *block = - compile_section(nr, fragment, id, &labels_, NO_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, NO_LOGGING); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + 3 * (BPF_ARG_COMP_LEN + 1) + 2 + 2 + 1 + 2; EXPECT_EQ(block->total_len, exp_total_len); @@ -554,7 +564,7 @@ TEST_F(ArgFilterTest, and_or) { /* First block is a label. */ struct filter_block *curr_block = block; ASSERT_NE(curr_block, nullptr); - EXPECT_EQ(block->len, 1U); + EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison ("arg0 == 0"). */ @@ -603,7 +613,7 @@ TEST_F(ArgFilterTest, ret_errno) { unsigned int id = 0; struct filter_block *block = - compile_section(nr, fragment, id, &labels_, NO_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, NO_LOGGING); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + 2 * (BPF_ARG_COMP_LEN + 1) + 2 + 2 + 1 + 2; EXPECT_EQ(block->total_len, exp_total_len); @@ -611,7 +621,7 @@ TEST_F(ArgFilterTest, ret_errno) { /* First block is a label. */ struct filter_block *curr_block = block; ASSERT_NE(curr_block, nullptr); - EXPECT_EQ(block->len, 1U); + EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison ("arg0 == 0"). */ @@ -658,7 +668,7 @@ TEST_F(ArgFilterTest, unconditional_errno) { unsigned int id = 0; struct filter_block *block = - compile_section(nr, fragment, id, &labels_, NO_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, NO_LOGGING); ASSERT_NE(block, nullptr); size_t exp_total_len = 2; EXPECT_EQ(block->total_len, exp_total_len); @@ -666,7 +676,7 @@ TEST_F(ArgFilterTest, unconditional_errno) { /* First block is a label. */ struct filter_block *curr_block = block; ASSERT_NE(curr_block, nullptr); - EXPECT_EQ(block->len, 1U); + EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is SECCOMP_RET_ERRNO. */ @@ -688,7 +698,7 @@ TEST_F(ArgFilterTest, invalid_arg_token) { unsigned int id = 0; struct filter_block *block = - compile_section(nr, fragment, id, &labels_, NO_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, NO_LOGGING); ASSERT_EQ(block, nullptr); } @@ -698,7 +708,7 @@ TEST_F(ArgFilterTest, invalid_arg_number) { unsigned int id = 0; struct filter_block *block = - compile_section(nr, fragment, id, &labels_, NO_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, NO_LOGGING); ASSERT_EQ(block, nullptr); } @@ -708,7 +718,7 @@ TEST_F(ArgFilterTest, extra_chars_in_arg_token) { unsigned int id = 0; struct filter_block* block = - compile_section(nr, fragment, id, &labels_, NO_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, NO_LOGGING); ASSERT_EQ(block, nullptr); } @@ -718,7 +728,7 @@ TEST_F(ArgFilterTest, invalid_operator) { unsigned int id = 0; struct filter_block* block = - compile_section(nr, fragment, id, &labels_, NO_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, NO_LOGGING); ASSERT_EQ(block, nullptr); } @@ -728,7 +738,7 @@ TEST_F(ArgFilterTest, invalid_constant) { unsigned int id = 0; struct filter_block* block = - compile_section(nr, fragment, id, &labels_, NO_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, NO_LOGGING); ASSERT_EQ(block, nullptr); } @@ -738,7 +748,7 @@ TEST_F(ArgFilterTest, extra_tokens) { unsigned int id = 0; struct filter_block* block = - compile_section(nr, fragment, id, &labels_, NO_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, NO_LOGGING); ASSERT_EQ(block, nullptr); } @@ -748,7 +758,7 @@ TEST_F(ArgFilterTest, invalid_errno) { unsigned int id = 0; struct filter_block *block = - compile_section(nr, fragment, id, &labels_, NO_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, NO_LOGGING); ASSERT_EQ(block, nullptr); } @@ -757,7 +767,7 @@ TEST_F(ArgFilterTest, log_no_ret_error) { int nr = 1; unsigned int id = 0; struct filter_block *block = - compile_section(nr, fragment, id, &labels_, USE_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, USE_LOGGING); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; @@ -766,11 +776,11 @@ TEST_F(ArgFilterTest, log_no_ret_error) { /* First block is a label. */ struct filter_block *curr_block = block; ASSERT_NE(curr_block, nullptr); - EXPECT_EQ(block->len, 1U); + EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison. */ - curr_block = block->next; + curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_COMP(curr_block); @@ -800,7 +810,7 @@ TEST_F(ArgFilterTest, log_bad_ret_error) { unsigned int id = 0; struct filter_block *block = - compile_section(nr, fragment, id, &labels_, NO_LOGGING); + compile_policy_line(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); @@ -808,7 +818,7 @@ TEST_F(ArgFilterTest, log_bad_ret_error) { /* First block is a label. */ struct filter_block *curr_block = block; ASSERT_NE(curr_block, nullptr); - EXPECT_EQ(block->len, 1U); + EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison ("arg0 == 0"). */ @@ -844,7 +854,7 @@ TEST_F(ArgFilterTest, no_log_bad_ret_error) { unsigned int id = 0; struct filter_block *block = - compile_section(nr, fragment, id, &labels_, USE_LOGGING); + compile_policy_line(nr, fragment, id, &labels_, USE_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); @@ -852,7 +862,7 @@ TEST_F(ArgFilterTest, no_log_bad_ret_error) { /* First block is a label. */ struct filter_block *curr_block = block; ASSERT_NE(curr_block, nullptr); - EXPECT_EQ(block->len, 1U); + EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison ("arg0 == 0"). */ @@ -920,6 +930,126 @@ FILE *write_policy_to_pipe(const char *policy, size_t len) { return fdopen(pipefd[0], "r"); } +class FileTest : public ::testing::Test { + protected: + virtual void SetUp() { + labels_.count = 0; + head_ = new_filter_block(); + arg_blocks_ = NULL; + } + virtual void TearDown() { + free_label_strings(&labels_); + free_block_list(head_); + free_block_list(arg_blocks_); + } + struct bpf_labels labels_; + struct filter_block *head_; + struct filter_block *arg_blocks_; +}; + +TEST_F(FileTest, 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_NE(policy_file, nullptr); + int res = compile_file( + policy_file, head_, &arg_blocks_, &labels_, USE_RET_KILL, NO_LOGGING); + fclose(policy_file); + + /* + * Checks return value and that the blocks only allow expected syscalls. + */ + ASSERT_EQ(res, 0); + struct filter_block *curr_block = head_; + ASSERT_NE(curr_block, nullptr); + EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_read); + curr_block = curr_block->next; + ASSERT_NE(curr_block, nullptr); + EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_write); + curr_block = curr_block->next; + ASSERT_NE(curr_block, nullptr); + EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_rt_sigreturn); + curr_block = curr_block->next; + ASSERT_NE(curr_block, nullptr); + EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_exit); + + EXPECT_EQ(curr_block->next, nullptr); +} + +TEST_F(FileTest, seccomp_read) { + const char *policy = + "read: arg0 == 0\n" + "write: 1\n" + "rt_sigreturn: 1\n" + "exit: 1\n"; + + const int LABEL_ID = 0; + + 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); + fclose(policy_file); + + /* + * Checks return value, that the blocks only allow expected syscalls, and that + * labels between |head_| and |arg_blocks_| match. + */ + ASSERT_EQ(res, 0); + struct filter_block *curr_block = head_; + ASSERT_NE(curr_block, nullptr); + EXPECT_ALLOW_SYSCALL_ARGS(curr_block->instrs, + __NR_read, + LABEL_ID, + JUMP_JT, + JUMP_JF); + curr_block = curr_block->next; + ASSERT_NE(curr_block, nullptr); + EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_write); + curr_block = curr_block->next; + ASSERT_NE(curr_block, nullptr); + EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_rt_sigreturn); + curr_block = curr_block->next; + ASSERT_NE(curr_block, nullptr); + EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_exit); + + ASSERT_NE(arg_blocks_, nullptr); + size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; + EXPECT_EQ(arg_blocks_->total_len, exp_total_len); + + /* First block is a label. */ + curr_block = arg_blocks_; + ASSERT_NE(curr_block, nullptr); + EXPECT_EQ(curr_block->len, 1U); + EXPECT_ACTUAL_LBL(curr_block->instrs, LABEL_ID); + + /* Second block is a comparison. */ + curr_block = curr_block->next; + 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); +} + TEST(FilterTest, seccomp_mode1) { struct sock_fprog actual; const char *policy = @@ -1047,7 +1177,19 @@ TEST(FilterTest, missing_atom) { FILE* policy_file = write_policy_to_pipe(policy, strlen(policy)); ASSERT_NE(policy_file, nullptr); - int res = compile_filter(policy_file, &actual, 0, NO_LOGGING); + int res = compile_filter(policy_file, &actual, USE_RET_KILL, NO_LOGGING); + fclose(policy_file); + ASSERT_NE(res, 0); +} + +TEST(FilterTest, whitespace_atom) { + struct sock_fprog actual; + const char* policy = "open:\t \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_NE(res, 0); } @@ -1059,7 +1201,7 @@ TEST(FilterTest, invalid_name) { FILE *policy_file = write_policy_to_pipe(policy, strlen(policy)); ASSERT_NE(policy_file, nullptr); - int res = compile_filter(policy_file, &actual, 0, NO_LOGGING); + int res = compile_filter(policy_file, &actual, USE_RET_KILL, NO_LOGGING); fclose(policy_file); ASSERT_NE(res, 0); } @@ -1071,14 +1213,14 @@ TEST(FilterTest, invalid_arg) { FILE *policy_file = write_policy_to_pipe(policy, strlen(policy)); ASSERT_NE(policy_file, nullptr); - int res = compile_filter(policy_file, &actual, 0, NO_LOGGING); + int res = compile_filter(policy_file, &actual, USE_RET_KILL, NO_LOGGING); fclose(policy_file); ASSERT_NE(res, 0); } TEST(FilterTest, nonexistent) { struct sock_fprog actual; - int res = compile_filter(NULL, &actual, 0, NO_LOGGING); + int res = compile_filter(NULL, &actual, USE_RET_KILL, NO_LOGGING); ASSERT_NE(res, 0); } diff --git a/syscall_filter_unittest_macros.h b/syscall_filter_unittest_macros.h index 81ec67c..02bac89 100644 --- a/syscall_filter_unittest_macros.h +++ b/syscall_filter_unittest_macros.h @@ -44,6 +44,14 @@ do { \ EXPECT_TRUE((_block)->jf == LABEL_JF); \ } while (0) +#define EXPECT_ACTUAL_LBL(_block, _id) \ +do { \ + EXPECT_TRUE((_block)->code == (BPF_JMP+BPF_JA)); \ + EXPECT_TRUE((_block)->k == (_id)); \ + 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); \ |