/* SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2023 Red Hat GmbH * Author: Alice Frosi */ #include #include #include #include #include #include "cooker/filter.h" #include "disasm.h" #include "testutil.h" char tfilter[] = "/tmp/test-filter.bpf"; static bool filter_eq(struct sock_filter *f1, struct sock_filter *f2, unsigned int n) { unsigned i; for (i = 0; i < n; i++) { if (memcmp((void *)(f1 + i), (void *)(f2 + i), sizeof(struct sock_filter)) != 0) { printf("expected:\n"); bpf_disasm(f1[i], i); printf("got:\n"); bpf_disasm(f2[i], i); return false; } } return true; } START_TEST(test_single_instr) { struct sock_filter filter[10]; unsigned int size; long nr = 42; struct sock_filter expected[] = { /* l0 */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, arch))), /* l1 */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SEITAN_AUDIT_ARCH, 1, 0), /* l10 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /* l2 */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), /* l3 */ EQ(nr, 1, 0), /* l4 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /* l5 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), }; filter_notify(nr); filter_write(tfilter); size = read_filter(filter, tfilter); ck_assert_uint_eq(size, ARRAY_SIZE(expected)); ck_assert(filter_eq(expected, filter, ARRAY_SIZE(expected))); } END_TEST START_TEST(test_single_instr_two_args) { unsigned int size; long nr = 42; struct bpf_arg a1 = { .cmp = EQ, .value = { .v32 = 0x123 }, .type = BPF_U32 }; struct bpf_arg a2 = { .cmp = EQ, .value = { .v32 = 0x321 }, .type = BPF_U32 }; struct sock_filter result[20]; struct sock_filter expected[] = { /* l0 */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, arch))), /* l1 */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SEITAN_AUDIT_ARCH, 1, 0), /* l2 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /* l3 */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), /* l4 */ EQ(nr, 2, 0), /* l5 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /* l6 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), /* l7 */ LOAD(offsetof(struct seccomp_data, args[1])), /* l8 */ EQ(0x123, 0, 2), /* l9 */ LOAD(offsetof(struct seccomp_data, args[2])), /* l10 */ EQ(0x321, 0, 1), /* l11 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), /* l12 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), }; filter_notify(nr); filter_add_arg(1, a1); filter_add_arg(2, a2); filter_flush_args(); filter_write(tfilter); size = read_filter(result, tfilter); bpf_disasm_all(result, size); ck_assert_uint_eq(size, ARRAY_SIZE(expected)); ck_assert(filter_eq(expected, result, ARRAY_SIZE(expected))); } END_TEST //START_TEST(test_two_instr) //{ // unsigned int size; // struct bpf_call calls[] = { // { .name = "test1" }, // { .name = "test2" }, // }; // struct syscall_entry table[] = { // { .count = 1, .nr = 42, .entry = &calls[0] }, // { .count = 1, .nr = 49, .entry = &calls[1] }, // }; // struct sock_filter expected[] = { // /* l0 */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, // (offsetof(struct seccomp_data, arch))), // /* l1 */ // BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SEITAN_AUDIT_ARCH, 1, 0), // /* l2 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), // /* l3 */ // BPF_STMT(BPF_LD | BPF_W | BPF_ABS, // (offsetof(struct seccomp_data, nr))), // /* ------- level0 -------- */ // /* l4 */ JGE(49, 1, 0), // /* ------- leaves -------- */ // /* l5 */ EQ(42, 2, 1), // /* l6 */ EQ(49, 1, 0), // /* l7 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), // /* l8 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), // }; // struct sock_filter result[30]; // // size = create_bfp_program(table, result, // sizeof(table) / sizeof(table[0])); // ck_assert_uint_eq(size, sizeof(expected) / sizeof(expected[0])); // ck_assert(filter_eq(expected, result, // sizeof(expected) / sizeof(expected[0]))); //} //END_TEST // //START_TEST(test_multiple_instr_no_args) //{ // unsigned int size; // struct bpf_call calls[] = { // { .name = "test1" }, { .name = "test2" }, { .name = "test3" }, // { .name = "test4" }, { .name = "test5" }, // }; // struct syscall_entry table[] = { // { .count = 1, .nr = 42, .entry = &calls[0] }, // { .count = 1, .nr = 43, .entry = &calls[1] }, // { .count = 1, .nr = 44, .entry = &calls[2] }, // { .count = 1, .nr = 45, .entry = &calls[3] }, // { .count = 1, .nr = 46, .entry = &calls[4] }, // }; // struct sock_filter expected[] = { // /* l0 */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, // (offsetof(struct seccomp_data, arch))), // /* l1 */ // BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SEITAN_AUDIT_ARCH, 1, 0), // /* l2 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), // /* l3 */ // BPF_STMT(BPF_LD | BPF_W | BPF_ABS, // (offsetof(struct seccomp_data, nr))), // /* ------- level0 -------- */ // /* l4 */ JGE(46, 1, 0), // /* ------- level1 -------- */ // /* l5 */ JGE(45, 2, 1), // /* l6 */ JGE(46, 3, 2), // /* ------- level2 -------- */ // /* l7 */ JGE(43, 4, 3), // /* l8 */ JGE(45, 5, 4), // /* l9 */ JGE(46, 6, 5), // /* l10 */ JUMPA(5), // /* -------- leaves ------- */ // /* l11 */ EQ(42, 5, 4), // /* l12 */ EQ(43, 4, 3), // /* l13 */ EQ(44, 3, 2), // /* l14 */ EQ(45, 2, 1), // /* l15 */ EQ(46, 1, 0), // /* l16 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), // /* l17 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), // }; // struct sock_filter result[sizeof(expected) / sizeof(expected[0]) + 10]; // // size = create_bfp_program(table, result, // sizeof(table) / sizeof(table[0])); // ck_assert_uint_eq(size, sizeof(expected) / sizeof(expected[0])); // ck_assert(filter_eq(expected, result, // sizeof(expected) / sizeof(expected[0]))); //} //END_TEST // //START_TEST(test_multiple_instr_with_args) //{ // unsigned int size; // struct bpf_call calls[] = { // { .name = "test1", // .args = { [1] = { .cmp = EQ, // .value = { .v32 = 123 }, // .type = BPF_U32 }, // [2] = { .cmp = EQ, // .value = { .v32 = 321 }, // .type = BPF_U32 } } }, // { .name = "test2" }, // { .name = "test3" }, // { .name = "test4", // .args = { [1] = { .cmp = EQ, // .value = { .v32 = 123 }, // .type = BPF_U32 }, // [2] = { .cmp = EQ, // .value = { .v32 = 321 }, // .type = BPF_U32 } } }, // { .name = "test5" }, // }; // struct syscall_entry table[] = { // { .count = 1, .nr = 42, .entry = &calls[0] }, // { .count = 1, .nr = 43, .entry = &calls[1] }, // { .count = 1, .nr = 44, .entry = &calls[2] }, // { .count = 1, .nr = 45, .entry = &calls[3] }, // { .count = 1, .nr = 46, .entry = &calls[4] }, // }; // struct sock_filter expected[] = { // /* l0 */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, // (offsetof(struct seccomp_data, arch))), // /* l1 */ // BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SEITAN_AUDIT_ARCH, 1, 0), // /* l2 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), // /* l3 */ // BPF_STMT(BPF_LD | BPF_W | BPF_ABS, // (offsetof(struct seccomp_data, nr))), // /* ------- level0 -------- */ // /* l4 */ JGE(46, 1, 0), // /* ------- level1 -------- */ // /* l5 */ JGE(45, 2, 1), // /* l6 */ JGE(46, 3, 2), // /* ------- level2 -------- */ // /* l7 */ JGE(43, 4, 3), // /* l8 */ JGE(45, 5, 4), // /* l9 */ JGE(46, 6, 5), // /* l10 */ JUMPA(5), // /* -------- leaves ------- */ // /* l11 */ EQ(42, 6, 4), // /* l12 */ EQ(43, 4, 3), // /* l13 */ EQ(44, 3, 2), // /* l14 */ EQ(45, 9, 1), // /* l15 */ EQ(46, 1, 0), // /* l16 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), // /* l17 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), // /* ------- args ---------- */ // /* l18 */ LOAD(offsetof(struct seccomp_data, args[1])), // /* l19 */ EQ(123, 0, 2), // /* l20 */ LOAD(offsetof(struct seccomp_data, args[2])), // /* l21 */ EQ(321, 0, 1), // /* l22 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), // /* l23 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), // /* ----- end call42 ------ */ // /* l24 */ LOAD(offsetof(struct seccomp_data, args[1])), // /* l25 */ EQ(123, 0, 2), // /* l26 */ LOAD(offsetof(struct seccomp_data, args[2])), // /* l27 */ EQ(321, 0, 1), // /* l28 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), // /* l29 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), // /* ----- end call45 ------ */ // }; // struct sock_filter result[sizeof(expected) / sizeof(expected[0]) + 10]; // // size = create_bfp_program(table, result, // sizeof(table) / sizeof(table[0])); // // bpf_disasm_all(result, size); // ck_assert_uint_eq(size, sizeof(expected) / sizeof(expected[0])); // ck_assert(filter_eq(expected, result, // sizeof(expected) / sizeof(expected[0]))); //} //END_TEST // //START_TEST(test_multiple_instance_same_instr) //{ // unsigned int size; // struct bpf_call calls[] = { // { .name = "test1", // .args = { [1] = { .cmp = EQ, // .value = { .v32 = 123 }, // .type = BPF_U32 } } }, // { .name = "test1", // .args = { [2] = { .cmp = EQ, // .value = { .v32 = 321 }, // .type = BPF_U32 } } }, // { .name = "test2" }, // { .name = "test3" }, // { .name = "test4", // .args = { [1] = { .cmp = EQ, // .value = { .v32 = 123 }, // .type = BPF_U32 } } }, // { .name = "test4", // .args = { [2] = { .cmp = EQ, // .value = { .v32 = 321 }, // .type = BPF_U32 } } }, // { .name = "test5" }, // }; // struct syscall_entry table[] = { // { .count = 2, .nr = 42, .entry = &calls[0] }, // { .count = 1, .nr = 43, .entry = &calls[2] }, // { .count = 1, .nr = 44, .entry = &calls[3] }, // { .count = 2, .nr = 45, .entry = &calls[4] }, // { .count = 1, .nr = 46, .entry = &calls[6] }, // }; // struct sock_filter expected[] = { // /* l0 */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, // (offsetof(struct seccomp_data, arch))), // /* l1 */ // BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SEITAN_AUDIT_ARCH, 1, 0), // /* l2 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), // /* l3 */ // BPF_STMT(BPF_LD | BPF_W | BPF_ABS, // (offsetof(struct seccomp_data, nr))), // /* ------- level0 -------- */ // /* l4 */ JGE(46, 1, 0), // /* ------- level1 -------- */ // /* l5 */ JGE(45, 2, 1), // /* l6 */ JGE(46, 3, 2), // /* ------- level2 -------- */ // /* l7 */ JGE(43, 4, 3), // /* l8 */ JGE(45, 5, 4), // /* l9 */ JGE(46, 6, 5), // /* l10 */ JUMPA(5), // /* -------- leaves ------- */ // /* l11 */ EQ(42, 6, 4), // /* l12 */ EQ(43, 4, 3), // /* l13 */ EQ(44, 3, 2), // /* l14 */ EQ(45, 10, 1), // /* l15 */ EQ(46, 1, 0), // /* l16 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), // /* l17 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), // /* ------- args ---------- */ // /* l18 */ LOAD(offsetof(struct seccomp_data, args[1])), // /* l19 */ EQ(123, 0, 1), // /* l20 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), // /* l21 */ LOAD(offsetof(struct seccomp_data, args[2])), // /* l22 */ EQ(321, 0, 1), // /* l23 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), // /* l24 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), // /* ----- end call42 ------ */ // /* l25 */ LOAD(offsetof(struct seccomp_data, args[1])), // /* l26 */ EQ(123, 0, 1), // /* l27 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), // /* l28 */ LOAD(offsetof(struct seccomp_data, args[2])), // /* l29 */ EQ(321, 0, 1), // /* l30 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), // /* l31 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), // /* ----- end call44 ------ */ // }; // struct sock_filter result[sizeof(expected) / sizeof(expected[0]) + 10]; // // size = create_bfp_program(table, result, // sizeof(table) / sizeof(table[0])); // ck_assert_uint_eq(size, sizeof(expected) / sizeof(expected[0])); // ck_assert(filter_eq(expected, result, // sizeof(expected) / sizeof(expected[0]))); //} //END_TEST Suite *bpf_suite(void) { Suite *s; TCase *tsingle_instr, *tmultiple_instr; s = suite_create("Create BPF filter"); tsingle_instr = tcase_create("single instruction"); tmultiple_instr = tcase_create("multiple instructions"); tcase_add_test(tsingle_instr, test_single_instr); tcase_add_test(tsingle_instr, test_single_instr_two_args); //tcase_add_test(tmultiple_instr, test_two_instr); //tcase_add_test(tmultiple_instr, test_multiple_instr_no_args); //tcase_add_test(tmultiple_instr, test_multiple_instr_with_args); //tcase_add_test(tmultiple_instr, test_multiple_instance_same_instr); suite_add_tcase(s, tsingle_instr); //suite_add_tcase(s, tmultiple_instr); return s; } int main(void) { int no_failed = 0; Suite *s; SRunner *runner; s = bpf_suite(); runner = srunner_create(s); srunner_run_all(runner, CK_VERBOSE); no_failed = srunner_ntests_failed(runner); srunner_free(runner); return (no_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; }