From 977ffec509a95efeca2b7c508912d5d1df7cd677 Mon Sep 17 00:00:00 2001 From: Alice Frosi Date: Wed, 22 Feb 2023 15:52:54 +0100 Subject: Rename actions to operations Replace all the action related names to operations to make them more generic. --- tests/unit/Makefile | 18 +- tests/unit/test_action_call.c | 218 --------------------- tests/unit/test_actions.c | 438 ------------------------------------------ tests/unit/test_op_call.c | 218 +++++++++++++++++++++ tests/unit/test_operations.c | 438 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 665 insertions(+), 665 deletions(-) delete mode 100644 tests/unit/test_action_call.c delete mode 100644 tests/unit/test_actions.c create mode 100644 tests/unit/test_op_call.c create mode 100644 tests/unit/test_operations.c (limited to 'tests') diff --git a/tests/unit/Makefile b/tests/unit/Makefile index 87f64c4..3ae4fd7 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -1,16 +1,16 @@ CFLAGS += -lcheck -test: test-filter test-actions test-action-call +test: test-filter test-operations test-op-call test-filter: test-filter.c $(CC) $(CFLAGS) -o test-filter ../../filter.c ../../disasm.c test-filter.c ./test-filter -test-action-call: test_action_call.c ../../actions.c ../../actions.h ../../gluten.h - $(CC) $(CFLAGS) -o test-action-call ../../actions.c test_action_call.c - ./test-action-call +test-op-call: test_op_call.c ../../operations.c ../../operations.h ../../gluten.h + $(CC) $(CFLAGS) -o test-op-call ../../operations.c test_op_call.c + ./test-op-call -test-actions: test_actions.c ../../actions.c ../../actions.h ../../common.h - $(CC) $(CFLAGS) -o test-actions \ - ../../common.c ../../actions.c \ - test_actions.c - ./test-actions +test-operations: test_operations.c ../../operations.c ../../operations.h ../../common.h + $(CC) $(CFLAGS) -o test-operations \ + ../../common.c ../../operations.c \ + test_operations.c + ./test-operations diff --git a/tests/unit/test_action_call.c b/tests/unit/test_action_call.c deleted file mode 100644 index 8621174..0000000 --- a/tests/unit/test_action_call.c +++ /dev/null @@ -1,218 +0,0 @@ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "../../gluten.h" -#include "../../actions.h" - -struct args_write_file { - char *file; - char *t; - ssize_t size; -}; - -static void write_file(char *file, char *t, ssize_t size) -{ - int fd; - - fd = open(file, O_CREAT | O_RDWR, S_IWUSR | S_IRUSR); - ck_assert_int_ge(fd, -1); - write(fd, t, size); - close(fd); -} - -static int write_file_get_fd(char *file, char *t, ssize_t size) -{ - int fd; - - write_file(file, t, size); - fd = open(file, O_RDONLY, S_IWUSR); - unlink(file); - return fd; -} - -static int write_file_clone(void *a) -{ - struct args_write_file *args = (struct args_write_file *)a; - write_file(args->file, args->t, args->size); - pause(); - return 0; -} - -static pid_t create_func_ns(int (*fn)(void *), void *arg, struct ns_spec ns[]) -{ - char stack[STACK_SIZE]; - pid_t child; - int flags = SIGCHLD; - unsigned int i; - - for (i = 0; i < sizeof(sizeof(enum ns_type)); i++) { - if (ns[i].type == NS_NONE) - continue; - switch (i) { - case NS_CGROUP: - flags |= CLONE_NEWCGROUP; - break; - case NS_IPC: - flags |= CLONE_NEWIPC; - break; - case NS_NET: - flags |= CLONE_NEWNET; - break; - case NS_MOUNT: - flags |= CLONE_NEWNS; - break; - case NS_PID: - flags |= CLONE_NEWPID; - break; - case NS_USER: - flags |= CLONE_NEWUSER; - break; - case NS_UTS: - flags |= CLONE_NEWUTS; - break; - case NS_TIME: - fprintf(stderr, - "option NS_TIME not suppoted by clone\n"); - break; - default: - fprintf(stderr, "unrecognized option %d\n", i); - } - } - child = clone(fn, stack + sizeof(stack), flags, arg); - if (child == -1) { - perror("clone"); - exit(EXIT_FAILURE); - } - return child; -} - -START_TEST(test_with_open_read_ns) -{ - char test_file[] = "/tmp/test.txt"; - char t[PATH_MAX] = "Hello Test"; - struct args_write_file args = { test_file, t, sizeof(t) }; - struct act_call call; - int flags = O_RDWR; - struct arg_clone c; - char buf[PATH_MAX]; - unsigned i; - long count; - pid_t pid; - int ret; - - c.args = &call; - count = sizeof(buf); - for (i = 0; i < sizeof(enum ns_type); i++) - call.context.ns[i].type = NS_NONE; - call.context.ns[NS_MOUNT].type = NS_SPEC_PID; - pid = create_func_ns(write_file_clone, (void *)&args, call.context.ns); - call.context.ns[NS_MOUNT].pid = pid; - call.nr = SYS_open; - call.args[0] = (void *)&test_file; - call.args[1] = (void *)(long)flags; - ret = do_call(&c); - ck_assert_int_eq(ret, 0); - ck_assert_msg(c.ret >= 0, "expect ret %ld should be nonegative", c.ret); - - call.nr = SYS_read; - call.args[0] = (void *)(long)c.ret; - call.args[1] = (void *)&buf; - call.args[2] = (void *)count; - ret = do_call(&c); - kill(pid, SIGCONT); - - ck_assert_int_eq(ret, 0); - ck_assert_msg(c.ret == count, "expect ret %ld to be %ld", c.ret, count); - ck_assert_str_eq(t, buf); -} -END_TEST - -START_TEST(test_with_read) -{ - char test_file[] = "/tmp/test.txt"; - char t[PATH_MAX] = "Hello Test"; - struct act_call call; - struct arg_clone c; - char buf[PATH_MAX]; - unsigned i; - long count; - int fd, ret; - - c.args = &call; - fd = write_file_get_fd(test_file, t, sizeof(t)); - count = sizeof(buf); - for (i = 0; i < sizeof(enum ns_type); i++) - call.context.ns[i].type = NS_NONE; - call.nr = SYS_read; - call.args[0] = (void *)(long)fd; - call.args[1] = (void *)&buf; - call.args[2] = (void *)count; - ret = do_call(&c); - - ck_assert_int_eq(ret, 0); - ck_assert_msg(c.ret == count, "expect ret %ld to be %ld", c.ret, count); - ck_assert_str_eq(t, buf); -} -END_TEST - -START_TEST(test_with_getppid) -{ - struct act_call call; - struct arg_clone c; - unsigned i; - long pid = (long)getpid(); - int ret; - - for (i = 0; i < sizeof(enum ns_type); i++) - call.context.ns[i].type = NS_NONE; - call.nr = SYS_getppid; - c.args = &call; - ret = do_call(&c); - ck_assert_int_eq(ret, 0); - ck_assert_msg(c.ret == pid, "expect ret %ld to be equal to %ld", c.ret, - pid); -} -END_TEST - -Suite *action_call_suite(void) -{ - Suite *s; - TCase *tactions; - - s = suite_create("Perform actions call"); - tactions = tcase_create("action calls"); - - tcase_add_test(tactions, test_with_getppid); - tcase_add_test(tactions, test_with_read); - tcase_add_test(tactions, test_with_open_read_ns); - - suite_add_tcase(s, tactions); - - return s; -} - -int main(void) -{ - int no_failed = 0; - Suite *s; - SRunner *runner; - - s = action_call_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; -} diff --git a/tests/unit/test_actions.c b/tests/unit/test_actions.c deleted file mode 100644 index 8893722..0000000 --- a/tests/unit/test_actions.c +++ /dev/null @@ -1,438 +0,0 @@ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "../../gluten.h" -#include "../../actions.h" -#include "../../common.h" - -struct args_target { - long ret; - int err; - bool check_fd; - int fd; -}; - -struct seccomp_notif req; -int notifyfd; -struct args_target *at; -int pipefd[2]; -int nr = __NR_getpid; -pid_t pid; - -uint16_t tmp_data[TMP_DATA_SIZE]; - -static int install_notification_filter() -{ - int fd; - /* filter a single syscall for the tests */ - struct sock_filter filter[] = { - BPF_STMT(BPF_LD | BPF_W | BPF_ABS, - (offsetof(struct seccomp_data, arch))), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SEITAN_AUDIT_ARCH, 0, 3), - BPF_STMT(BPF_LD | BPF_W | BPF_ABS, - (offsetof(struct seccomp_data, nr))), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1), - BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), - BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW), - }; - struct sock_fprog prog; - - prog.filter = filter; - prog.len = (unsigned short)(sizeof(filter) / sizeof(filter[0])); - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) { - perror("prctl"); - return -1; - } - if ((fd = syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, - SECCOMP_FILTER_FLAG_NEW_LISTENER, &prog)) < 0) { - perror("seccomp"); - return -1; - } - return fd; -} - -static int create_test_fd() -{ - return open("/tmp", O_RDWR | O_TMPFILE); -} - -static int target() -{ - int buf = 0; - if (install_notification_filter() < 0) { - return -1; - } - - at->ret = getpid(); - at->err = errno; - if (at->check_fd) - read(pipefd[0], &buf, 1); - - close(pipefd[0]); - - write(pipefd[1], &buf, 1); - close(pipefd[1]); - exit(0); -} - -static pid_t do_clone(int (*fn)(void *), void *arg) -{ - char stack[STACK_SIZE]; - pid_t child; - int flags = SIGCHLD; - - child = clone(fn, stack + sizeof(stack), flags, arg); - if (child == -1) { - perror("clone"); - return -1; - } - return child; -} - -int get_fd_notifier(pid_t pid) -{ - char path[PATH_MAX + 1]; - int pidfd, notifier; - int fd = -1; - - snprintf(path, sizeof(path), "/proc/%d/fd", pid); - while (fd < 0) { - sleep(2); - fd = find_fd_seccomp_notifier(path); - } - ck_assert_int_ge(fd, 0); - pidfd = syscall(SYS_pidfd_open, pid, 0); - ck_assert_msg(pidfd >= 0, strerror(errno)); - sleep(1); - notifier = syscall(SYS_pidfd_getfd, pidfd, fd, 0); - ck_assert_msg(notifier >= 0, strerror(errno)); - return notifier; -} - -static void check_target_result(long ret, int err, bool ignore_ret) -{ - int buf; - - read(pipefd[0], &buf, 1); - if (!ignore_ret) - ck_assert_msg(at->ret == ret, - "expect return value %ld to be equal to %ld", - at->ret, ret); - ck_assert_int_eq(at->err, err); - ck_assert_int_eq(close(pipefd[0]), 0); -} - -void target_exit() -{ - int status; - - waitpid(-1, &status, WUNTRACED | WNOHANG); - if (WEXITSTATUS(status) != 0) { - fprintf(stderr, "target process exited with an error\n"); - exit(-1); - } -} - -static bool has_fd(int pid, int fd) -{ - char path[PATH_MAX + 1]; - - snprintf(path, sizeof(path), "/proc/%d/fd/%d", pid, fd); - return access(path, F_OK) == 0; -} - -static void check_target_fd(int pid, int fd) -{ - int buf = 0; - - ck_assert(has_fd(pid, fd)); - write(pipefd[1], &buf, 1); - close(pipefd[1]); -} - -void setup(bool check_fd) -{ - int ret; - - signal(SIGCHLD, target_exit); - ck_assert_int_ne(pipe(pipefd), -1); - at = mmap(NULL, sizeof(struct args_target), PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS, -1, 0); - at->check_fd = check_fd; - pid = do_clone(target, NULL); - ck_assert_int_ge(pid, 0); - - /* Use write pipe to sync the target for checking the existance of the fd */ - if (!check_fd) - ck_assert_int_ne(close(pipefd[1]), -1); - - notifyfd = get_fd_notifier(pid); - - memset(&req, 0, sizeof(req)); - ret = ioctl(notifyfd, SECCOMP_IOCTL_NOTIF_RECV, &req); - ck_assert_msg(ret == 0, strerror(errno)); - ck_assert_msg((req.data).nr == nr, "filter syscall nr: %d", - (req.data).nr); -} - -void teardown() -{ - if (at != NULL) - munmap(at, sizeof(struct args_target)); -} - -void setup_without_fd() -{ - setup(false); -} -void setup_fd() -{ - setup(true); -} - -START_TEST(test_act_continue) -{ - struct action actions[] = { - { .type = A_CONT }, - }; - int ret = do_actions(NULL, actions, - sizeof(actions) / sizeof(actions[0]), -1, notifyfd, - req.id); - ck_assert_msg(ret == 0, strerror(errno)); - ck_assert_int_eq(at->err, 0); -} -END_TEST - -START_TEST(test_act_block) -{ - struct action actions[] = { - { - .type = A_BLOCK, - .block = { .error = -1 }, - }, - }; - int ret = do_actions(NULL, actions, - sizeof(actions) / sizeof(actions[0]), -1, notifyfd, - req.id); - ck_assert_msg(ret == 0, strerror(errno)); - check_target_result(-1, 0, false); -} -END_TEST - -START_TEST(test_act_return) -{ - struct action actions[] = { - { - .type = A_RETURN, - .ret = { .type = IMMEDIATE, .value = 1 }, - }, - }; - int ret = do_actions(NULL, actions, - sizeof(actions) / sizeof(actions[0]), -1, notifyfd, - req.id); - ck_assert_msg(ret == 0, strerror(errno)); - check_target_result(1, 0, false); -} -END_TEST - -START_TEST(test_act_return_ref) -{ - int64_t v = 2; - uint16_t offset = 4; - struct action actions[] = { - { - .type = A_RETURN, - .ret = { .type = REFERENCE, .value_off = offset }, - }, - }; - memcpy((uint16_t *)&tmp_data + offset, &v, sizeof(v)); - - int ret = do_actions(&tmp_data, actions, - sizeof(actions) / sizeof(actions[0]), -1, notifyfd, - req.id); - ck_assert_msg(ret == 0, strerror(errno)); - check_target_result(v, 0, false); -} -END_TEST - -START_TEST(test_act_call) -{ - struct action actions[] = { - { - .type = A_CALL, - .call = { .nr = __NR_getppid, .has_ret = false }, - }, - { .type = A_CONT }, - }; - int ret = do_actions(NULL, actions, - sizeof(actions) / sizeof(actions[0]), -1, notifyfd, - req.id); - ck_assert_msg(ret == 0, strerror(errno)); - check_target_result(1, 0, true); -} -END_TEST - -START_TEST(test_act_call_ret) -{ - struct action actions[] = { - { - .type = A_CALL, - .call = { .nr = __NR_getppid, - .has_ret = true, - .ret_off = 2 }, - }, - { .type = A_CONT }, - }; - int ret = do_actions(&tmp_data, actions, - sizeof(actions) / sizeof(actions[0]), -1, notifyfd, - req.id); - long r; - ck_assert_msg(ret == 0, strerror(errno)); - check_target_result(1, 0, true); - memcpy(&r, &tmp_data[2], sizeof(r)); - ck_assert_int_eq(r, getpid()); -} -END_TEST - -static void test_inject(struct action actions[], int n, bool reference) -{ - uint16_t new_off = 2, old_off = 4; - int fd_inj; - int test_fd = 3; - int ret; - - fd_inj = create_test_fd(); - ck_assert_int_ge(fd_inj, 0); - if (reference) { - memcpy((uint16_t *)&tmp_data + new_off, &fd_inj, - sizeof(fd_inj)); - memcpy((uint16_t *)&tmp_data + old_off, &test_fd, - sizeof(test_fd)); - - actions[0].inj.newfd.fd_off = new_off; - actions[0].inj.newfd.type = REFERENCE; - actions[0].inj.oldfd.fd_off = old_off; - actions[0].inj.oldfd.type = REFERENCE; - } else { - actions[0].inj.newfd.fd = fd_inj; - actions[0].inj.newfd.type = IMMEDIATE; - actions[0].inj.oldfd.fd = test_fd; - actions[0].inj.oldfd.type = IMMEDIATE; - } - - ret = do_actions(&tmp_data, actions, n, -1, notifyfd, req.id); - ck_assert_msg(ret == 0, strerror(errno)); - check_target_fd(pid, test_fd); -} - -START_TEST(test_act_inject_a) -{ - struct action actions[] = { { .type = A_INJECT_A } }; - test_inject(actions, sizeof(actions) / sizeof(actions[0]), false); -} -END_TEST - -START_TEST(test_act_inject_a_ref) -{ - struct action actions[] = { { .type = A_INJECT_A } }; - test_inject(actions, sizeof(actions) / sizeof(actions[0]), true); -} -END_TEST - -START_TEST(test_act_inject) -{ - struct action actions[] = { { .type = A_INJECT } }; - test_inject(actions, sizeof(actions) / sizeof(actions[0]), false); -} -END_TEST - -START_TEST(test_act_inject_ref) -{ - struct action actions[] = { { .type = A_INJECT } }; - test_inject(actions, sizeof(actions) / sizeof(actions[0]), true); -} -END_TEST - -Suite *action_call_suite(void) -{ - Suite *s; - int timeout = 30; - TCase *cont, *block, *ret, *call; - TCase *inject, *inject_a; - - s = suite_create("Perform actions"); - - cont = tcase_create("a_continue"); - tcase_add_checked_fixture(cont, setup_without_fd, teardown); - tcase_set_timeout(cont, timeout); - tcase_add_test(cont, test_act_continue); - suite_add_tcase(s, cont); - - ret = tcase_create("a_return"); - tcase_add_checked_fixture(ret, setup_without_fd, teardown); - tcase_set_timeout(ret, timeout); - tcase_add_test(ret, test_act_return); - tcase_add_test(ret, test_act_return_ref); - suite_add_tcase(s, ret); - - block = tcase_create("a_block"); - tcase_add_checked_fixture(block, setup_without_fd, teardown); - tcase_set_timeout(block, timeout); - tcase_add_test(block, test_act_block); - suite_add_tcase(s, block); - - call = tcase_create("a_call"); - tcase_add_checked_fixture(call, setup_without_fd, teardown); - tcase_set_timeout(call, timeout); - tcase_add_test(call, test_act_call); - tcase_add_test(call, test_act_call_ret); - suite_add_tcase(s, call); - - inject = tcase_create("a_inject"); - tcase_add_checked_fixture(inject, setup_fd, teardown); - tcase_set_timeout(inject, timeout); - tcase_add_test(inject, test_act_inject); - tcase_add_test(inject, test_act_inject_ref); - suite_add_tcase(s, inject); - - inject_a = tcase_create("a_inject_a"); - tcase_add_checked_fixture(inject_a, setup_fd, teardown); - tcase_set_timeout(inject_a, timeout); - tcase_add_test(inject_a, test_act_inject_a); - tcase_add_test(inject_a, test_act_inject_a_ref); - suite_add_tcase(s, inject_a); - - return s; -} - -int main(void) -{ - int no_failed = 0; - Suite *s; - SRunner *runner; - - s = action_call_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; -} diff --git a/tests/unit/test_op_call.c b/tests/unit/test_op_call.c new file mode 100644 index 0000000..6ffef99 --- /dev/null +++ b/tests/unit/test_op_call.c @@ -0,0 +1,218 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../../gluten.h" +#include "../../operations.h" + +struct args_write_file { + char *file; + char *t; + ssize_t size; +}; + +static void write_file(char *file, char *t, ssize_t size) +{ + int fd; + + fd = open(file, O_CREAT | O_RDWR, S_IWUSR | S_IRUSR); + ck_assert_int_ge(fd, -1); + write(fd, t, size); + close(fd); +} + +static int write_file_get_fd(char *file, char *t, ssize_t size) +{ + int fd; + + write_file(file, t, size); + fd = open(file, O_RDONLY, S_IWUSR); + unlink(file); + return fd; +} + +static int write_file_clone(void *a) +{ + struct args_write_file *args = (struct args_write_file *)a; + write_file(args->file, args->t, args->size); + pause(); + return 0; +} + +static pid_t create_func_ns(int (*fn)(void *), void *arg, struct ns_spec ns[]) +{ + char stack[STACK_SIZE]; + pid_t child; + int flags = SIGCHLD; + unsigned int i; + + for (i = 0; i < sizeof(sizeof(enum ns_type)); i++) { + if (ns[i].type == NS_NONE) + continue; + switch (i) { + case NS_CGROUP: + flags |= CLONE_NEWCGROUP; + break; + case NS_IPC: + flags |= CLONE_NEWIPC; + break; + case NS_NET: + flags |= CLONE_NEWNET; + break; + case NS_MOUNT: + flags |= CLONE_NEWNS; + break; + case NS_PID: + flags |= CLONE_NEWPID; + break; + case NS_USER: + flags |= CLONE_NEWUSER; + break; + case NS_UTS: + flags |= CLONE_NEWUTS; + break; + case NS_TIME: + fprintf(stderr, + "option NS_TIME not suppoted by clone\n"); + break; + default: + fprintf(stderr, "unrecognized option %d\n", i); + } + } + child = clone(fn, stack + sizeof(stack), flags, arg); + if (child == -1) { + perror("clone"); + exit(EXIT_FAILURE); + } + return child; +} + +START_TEST(test_with_open_read_ns) +{ + char test_file[] = "/tmp/test.txt"; + char t[PATH_MAX] = "Hello Test"; + struct args_write_file args = { test_file, t, sizeof(t) }; + struct op_call call; + int flags = O_RDWR; + struct arg_clone c; + char buf[PATH_MAX]; + unsigned i; + long count; + pid_t pid; + int ret; + + c.args = &call; + count = sizeof(buf); + for (i = 0; i < sizeof(enum ns_type); i++) + call.context.ns[i].type = NS_NONE; + call.context.ns[NS_MOUNT].type = NS_SPEC_PID; + pid = create_func_ns(write_file_clone, (void *)&args, call.context.ns); + call.context.ns[NS_MOUNT].pid = pid; + call.nr = SYS_open; + call.args[0] = (void *)&test_file; + call.args[1] = (void *)(long)flags; + ret = do_call(&c); + ck_assert_int_eq(ret, 0); + ck_assert_msg(c.ret >= 0, "expect ret %ld should be nonegative", c.ret); + + call.nr = SYS_read; + call.args[0] = (void *)(long)c.ret; + call.args[1] = (void *)&buf; + call.args[2] = (void *)count; + ret = do_call(&c); + kill(pid, SIGCONT); + + ck_assert_int_eq(ret, 0); + ck_assert_msg(c.ret == count, "expect ret %ld to be %ld", c.ret, count); + ck_assert_str_eq(t, buf); +} +END_TEST + +START_TEST(test_with_read) +{ + char test_file[] = "/tmp/test.txt"; + char t[PATH_MAX] = "Hello Test"; + struct op_call call; + struct arg_clone c; + char buf[PATH_MAX]; + unsigned i; + long count; + int fd, ret; + + c.args = &call; + fd = write_file_get_fd(test_file, t, sizeof(t)); + count = sizeof(buf); + for (i = 0; i < sizeof(enum ns_type); i++) + call.context.ns[i].type = NS_NONE; + call.nr = SYS_read; + call.args[0] = (void *)(long)fd; + call.args[1] = (void *)&buf; + call.args[2] = (void *)count; + ret = do_call(&c); + + ck_assert_int_eq(ret, 0); + ck_assert_msg(c.ret == count, "expect ret %ld to be %ld", c.ret, count); + ck_assert_str_eq(t, buf); +} +END_TEST + +START_TEST(test_with_getppid) +{ + struct op_call call; + struct arg_clone c; + unsigned i; + long pid = (long)getpid(); + int ret; + + for (i = 0; i < sizeof(enum ns_type); i++) + call.context.ns[i].type = NS_NONE; + call.nr = SYS_getppid; + c.args = &call; + ret = do_call(&c); + ck_assert_int_eq(ret, 0); + ck_assert_msg(c.ret == pid, "expect ret %ld to be equal to %ld", c.ret, + pid); +} +END_TEST + +Suite *op_call_suite(void) +{ + Suite *s; + TCase *tops; + + s = suite_create("Perform ops call"); + tops = tcase_create("op calls"); + + tcase_add_test(tops, test_with_getppid); + tcase_add_test(tops, test_with_read); + tcase_add_test(tops, test_with_open_read_ns); + + suite_add_tcase(s, tops); + + return s; +} + +int main(void) +{ + int no_failed = 0; + Suite *s; + SRunner *runner; + + s = op_call_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; +} diff --git a/tests/unit/test_operations.c b/tests/unit/test_operations.c new file mode 100644 index 0000000..6c5952f --- /dev/null +++ b/tests/unit/test_operations.c @@ -0,0 +1,438 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../../gluten.h" +#include "../../operations.h" +#include "../../common.h" + +struct args_target { + long ret; + int err; + bool check_fd; + int fd; +}; + +struct seccomp_notif req; +int notifyfd; +struct args_target *at; +int pipefd[2]; +int nr = __NR_getpid; +pid_t pid; + +uint16_t tmp_data[TMP_DATA_SIZE]; + +static int install_notification_filter() +{ + int fd; + /* filter a single syscall for the tests */ + struct sock_filter filter[] = { + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, + (offsetof(struct seccomp_data, arch))), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SEITAN_AUDIT_ARCH, 0, 3), + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, + (offsetof(struct seccomp_data, nr))), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), + BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW), + }; + struct sock_fprog prog; + + prog.filter = filter; + prog.len = (unsigned short)(sizeof(filter) / sizeof(filter[0])); + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) { + perror("prctl"); + return -1; + } + if ((fd = syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, + SECCOMP_FILTER_FLAG_NEW_LISTENER, &prog)) < 0) { + perror("seccomp"); + return -1; + } + return fd; +} + +static int create_test_fd() +{ + return open("/tmp", O_RDWR | O_TMPFILE); +} + +static int target() +{ + int buf = 0; + if (install_notification_filter() < 0) { + return -1; + } + + at->ret = getpid(); + at->err = errno; + if (at->check_fd) + read(pipefd[0], &buf, 1); + + close(pipefd[0]); + + write(pipefd[1], &buf, 1); + close(pipefd[1]); + exit(0); +} + +static pid_t do_clone(int (*fn)(void *), void *arg) +{ + char stack[STACK_SIZE]; + pid_t child; + int flags = SIGCHLD; + + child = clone(fn, stack + sizeof(stack), flags, arg); + if (child == -1) { + perror("clone"); + return -1; + } + return child; +} + +int get_fd_notifier(pid_t pid) +{ + char path[PATH_MAX + 1]; + int pidfd, notifier; + int fd = -1; + + snprintf(path, sizeof(path), "/proc/%d/fd", pid); + while (fd < 0) { + sleep(2); + fd = find_fd_seccomp_notifier(path); + } + ck_assert_int_ge(fd, 0); + pidfd = syscall(SYS_pidfd_open, pid, 0); + ck_assert_msg(pidfd >= 0, strerror(errno)); + sleep(1); + notifier = syscall(SYS_pidfd_getfd, pidfd, fd, 0); + ck_assert_msg(notifier >= 0, strerror(errno)); + return notifier; +} + +static void check_target_result(long ret, int err, bool ignore_ret) +{ + int buf; + + read(pipefd[0], &buf, 1); + if (!ignore_ret) + ck_assert_msg(at->ret == ret, + "expect return value %ld to be equal to %ld", + at->ret, ret); + ck_assert_int_eq(at->err, err); + ck_assert_int_eq(close(pipefd[0]), 0); +} + +void target_exit() +{ + int status; + + waitpid(-1, &status, WUNTRACED | WNOHANG); + if (WEXITSTATUS(status) != 0) { + fprintf(stderr, "target process exited with an error\n"); + exit(-1); + } +} + +static bool has_fd(int pid, int fd) +{ + char path[PATH_MAX + 1]; + + snprintf(path, sizeof(path), "/proc/%d/fd/%d", pid, fd); + return access(path, F_OK) == 0; +} + +static void check_target_fd(int pid, int fd) +{ + int buf = 0; + + ck_assert(has_fd(pid, fd)); + write(pipefd[1], &buf, 1); + close(pipefd[1]); +} + +void setup(bool check_fd) +{ + int ret; + + signal(SIGCHLD, target_exit); + ck_assert_int_ne(pipe(pipefd), -1); + at = mmap(NULL, sizeof(struct args_target), PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + at->check_fd = check_fd; + pid = do_clone(target, NULL); + ck_assert_int_ge(pid, 0); + + /* Use write pipe to sync the target for checking the existance of the fd */ + if (!check_fd) + ck_assert_int_ne(close(pipefd[1]), -1); + + notifyfd = get_fd_notifier(pid); + + memset(&req, 0, sizeof(req)); + ret = ioctl(notifyfd, SECCOMP_IOCTL_NOTIF_RECV, &req); + ck_assert_msg(ret == 0, strerror(errno)); + ck_assert_msg((req.data).nr == nr, "filter syscall nr: %d", + (req.data).nr); +} + +void teardown() +{ + if (at != NULL) + munmap(at, sizeof(struct args_target)); +} + +void setup_without_fd() +{ + setup(false); +} +void setup_fd() +{ + setup(true); +} + +START_TEST(test_act_continue) +{ + struct op operations[] = { + { .type = OP_CONT }, + }; + int ret = do_operations(NULL, operations, + sizeof(operations) / sizeof(operations[0]), -1, notifyfd, + req.id); + ck_assert_msg(ret == 0, strerror(errno)); + ck_assert_int_eq(at->err, 0); +} +END_TEST + +START_TEST(test_act_block) +{ + struct op operations[] = { + { + .type = OP_BLOCK, + .block = { .error = -1 }, + }, + }; + int ret = do_operations(NULL, operations, + sizeof(operations) / sizeof(operations[0]), -1, notifyfd, + req.id); + ck_assert_msg(ret == 0, strerror(errno)); + check_target_result(-1, 0, false); +} +END_TEST + +START_TEST(test_act_return) +{ + struct op operations[] = { + { + .type = OP_RETURN, + .ret = { .type = IMMEDIATE, .value = 1 }, + }, + }; + int ret = do_operations(NULL, operations, + sizeof(operations) / sizeof(operations[0]), -1, notifyfd, + req.id); + ck_assert_msg(ret == 0, strerror(errno)); + check_target_result(1, 0, false); +} +END_TEST + +START_TEST(test_act_return_ref) +{ + int64_t v = 2; + uint16_t offset = 4; + struct op operations[] = { + { + .type = OP_RETURN, + .ret = { .type = REFERENCE, .value_off = offset }, + }, + }; + memcpy((uint16_t *)&tmp_data + offset, &v, sizeof(v)); + + int ret = do_operations(&tmp_data, operations, + sizeof(operations) / sizeof(operations[0]), -1, notifyfd, + req.id); + ck_assert_msg(ret == 0, strerror(errno)); + check_target_result(v, 0, false); +} +END_TEST + +START_TEST(test_act_call) +{ + struct op operations[] = { + { + .type = OP_CALL, + .call = { .nr = __NR_getppid, .has_ret = false }, + }, + { .type = OP_CONT }, + }; + int ret = do_operations(NULL, operations, + sizeof(operations) / sizeof(operations[0]), -1, notifyfd, + req.id); + ck_assert_msg(ret == 0, strerror(errno)); + check_target_result(1, 0, true); +} +END_TEST + +START_TEST(test_act_call_ret) +{ + struct op operations[] = { + { + .type = OP_CALL, + .call = { .nr = __NR_getppid, + .has_ret = true, + .ret_off = 2 }, + }, + { .type = OP_CONT }, + }; + int ret = do_operations(&tmp_data, operations, + sizeof(operations) / sizeof(operations[0]), -1, notifyfd, + req.id); + long r; + ck_assert_msg(ret == 0, strerror(errno)); + check_target_result(1, 0, true); + memcpy(&r, &tmp_data[2], sizeof(r)); + ck_assert_int_eq(r, getpid()); +} +END_TEST + +static void test_inject(struct op operations[], int n, bool reference) +{ + uint16_t new_off = 2, old_off = 4; + int fd_inj; + int test_fd = 3; + int ret; + + fd_inj = create_test_fd(); + ck_assert_int_ge(fd_inj, 0); + if (reference) { + memcpy((uint16_t *)&tmp_data + new_off, &fd_inj, + sizeof(fd_inj)); + memcpy((uint16_t *)&tmp_data + old_off, &test_fd, + sizeof(test_fd)); + + operations[0].inj.newfd.fd_off = new_off; + operations[0].inj.newfd.type = REFERENCE; + operations[0].inj.oldfd.fd_off = old_off; + operations[0].inj.oldfd.type = REFERENCE; + } else { + operations[0].inj.newfd.fd = fd_inj; + operations[0].inj.newfd.type = IMMEDIATE; + operations[0].inj.oldfd.fd = test_fd; + operations[0].inj.oldfd.type = IMMEDIATE; + } + + ret = do_operations(&tmp_data, operations, n, -1, notifyfd, req.id); + ck_assert_msg(ret == 0, strerror(errno)); + check_target_fd(pid, test_fd); +} + +START_TEST(test_act_inject_a) +{ + struct op operations[] = { { .type = OP_INJECT_A } }; + test_inject(operations, sizeof(operations) / sizeof(operations[0]), false); +} +END_TEST + +START_TEST(test_act_inject_a_ref) +{ + struct op operations[] = { { .type = OP_INJECT_A } }; + test_inject(operations, sizeof(operations) / sizeof(operations[0]), true); +} +END_TEST + +START_TEST(test_act_inject) +{ + struct op operations[] = { { .type = OP_INJECT } }; + test_inject(operations, sizeof(operations) / sizeof(operations[0]), false); +} +END_TEST + +START_TEST(test_act_inject_ref) +{ + struct op operations[] = { { .type = OP_INJECT } }; + test_inject(operations, sizeof(operations) / sizeof(operations[0]), true); +} +END_TEST + +Suite *op_call_suite(void) +{ + Suite *s; + int timeout = 30; + TCase *cont, *block, *ret, *call; + TCase *inject, *inject_a; + + s = suite_create("Perform operations"); + + cont = tcase_create("a_continue"); + tcase_add_checked_fixture(cont, setup_without_fd, teardown); + tcase_set_timeout(cont, timeout); + tcase_add_test(cont, test_act_continue); + suite_add_tcase(s, cont); + + ret = tcase_create("a_return"); + tcase_add_checked_fixture(ret, setup_without_fd, teardown); + tcase_set_timeout(ret, timeout); + tcase_add_test(ret, test_act_return); + tcase_add_test(ret, test_act_return_ref); + suite_add_tcase(s, ret); + + block = tcase_create("a_block"); + tcase_add_checked_fixture(block, setup_without_fd, teardown); + tcase_set_timeout(block, timeout); + tcase_add_test(block, test_act_block); + suite_add_tcase(s, block); + + call = tcase_create("a_call"); + tcase_add_checked_fixture(call, setup_without_fd, teardown); + tcase_set_timeout(call, timeout); + tcase_add_test(call, test_act_call); + tcase_add_test(call, test_act_call_ret); + suite_add_tcase(s, call); + + inject = tcase_create("a_inject"); + tcase_add_checked_fixture(inject, setup_fd, teardown); + tcase_set_timeout(inject, timeout); + tcase_add_test(inject, test_act_inject); + tcase_add_test(inject, test_act_inject_ref); + suite_add_tcase(s, inject); + + inject_a = tcase_create("a_inject_a"); + tcase_add_checked_fixture(inject_a, setup_fd, teardown); + tcase_set_timeout(inject_a, timeout); + tcase_add_test(inject_a, test_act_inject_a); + tcase_add_test(inject_a, test_act_inject_a_ref); + suite_add_tcase(s, inject_a); + + return s; +} + +int main(void) +{ + int no_failed = 0; + Suite *s; + SRunner *runner; + + s = op_call_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; +} -- cgit v1.2.3