diff options
Diffstat (limited to 'tests/unit')
-rw-r--r-- | tests/unit/Makefile | 8 | ||||
-rw-r--r-- | tests/unit/test_actions.c | 222 |
2 files changed, 229 insertions, 1 deletions
diff --git a/tests/unit/Makefile b/tests/unit/Makefile index 5e7116b..a61f34c 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -5,6 +5,12 @@ test-filter: test-filter.c $(CC) $(CFLAGS) -o test-filter ../../filter.c ../../disasm.c test-filter.c ./test-filter -test-actions: test_action_call.c ../../actions.c ../../actions.h +test-action-call: test_action_call.c ../../actions.c ../../actions.h $(CC) $(CFLAGS) -o test-action-call ../../actions.c test_action_call.c ./test-action-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 diff --git a/tests/unit/test_actions.c b/tests/unit/test_actions.c new file mode 100644 index 0000000..5020358 --- /dev/null +++ b/tests/unit/test_actions.c @@ -0,0 +1,222 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sched.h> +#include <unistd.h> +#include <signal.h> +#include <limits.h> +#include <sys/prctl.h> +#include <sys/syscall.h> +#include <sys/ioctl.h> +#include <linux/audit.h> +#include <linux/filter.h> +#include <linux/seccomp.h> +#include <sys/mman.h> + +#include <check.h> + +#include "../../gluten.h" +#include "../../actions.h" +#include "../../common.h" + +struct args_target { + long ret; + int err; +}; + +struct seccomp_notif req; +int notifyfd; +struct args_target *at; +int pipefd[2]; +int nr = __NR_getpid; + +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 target() +{ + int ret = 0; + int fd; + + close(pipefd[0]); + fd = install_notification_filter(); + if (fd < 0) { + return -1; + } + at->ret = getpid(); + at->err = errno; + + write(pipefd[1], &ret, 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) +{ + int buf; + + read(pipefd[0], &buf, 1); + 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 setup() +{ + int ret; + + ck_assert_int_ne(pipe(pipefd), -1); + at = mmap(NULL, sizeof(struct args_target), PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + pid_t pid = do_clone(target, NULL); + ck_assert_int_ge(pid, 0); + + 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); +} + +START_TEST(test_act_continue) +{ + struct action actions[] = { + { .type = A_CONT }, + }; + int ret = do_actions(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(actions, sizeof(actions) / sizeof(actions[0]), -1, + notifyfd, req.id); + ck_assert_msg(ret == 0, strerror(errno)); + check_target_result(-1, 0); +} +END_TEST + +START_TEST(test_act_return) +{ + struct action actions[] = { + { + .type = A_RETURN, + .ret = { .value = 1 }, + }, + }; + int ret = do_actions(actions, sizeof(actions) / sizeof(actions[0]), -1, + notifyfd, req.id); + ck_assert_msg(ret == 0, strerror(errno)); + check_target_result(1, 0); +} +END_TEST + +Suite *action_call_suite(void) +{ + Suite *s; + TCase *tactions; + + s = suite_create("Perform actions"); + tactions = tcase_create("actions"); + + tcase_add_checked_fixture(tactions, setup, NULL); + tcase_set_timeout(tactions, 30); + tcase_add_test(tactions, test_act_return); + tcase_add_test(tactions, test_act_block); + tcase_add_test(tactions, test_act_continue); + + 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; +} |