diff options
author | Alice Frosi <afrosi@redhat.com> | 2023-02-14 09:12:19 +0100 |
---|---|---|
committer | Alice Frosi <afrosi@redhat.com> | 2023-02-15 14:03:42 +0100 |
commit | 8fc77732cbae451ea7b7fde88bef5f322167cd43 (patch) | |
tree | 22c5eab6b84f21e9535e0915f6e9653f6c0a6e0b /tests | |
parent | 7cb6760e3ebdc7a95f167bbeee793e8050215d2c (diff) | |
download | seitan-8fc77732cbae451ea7b7fde88bef5f322167cd43.tar seitan-8fc77732cbae451ea7b7fde88bef5f322167cd43.tar.gz seitan-8fc77732cbae451ea7b7fde88bef5f322167cd43.tar.bz2 seitan-8fc77732cbae451ea7b7fde88bef5f322167cd43.tar.lz seitan-8fc77732cbae451ea7b7fde88bef5f322167cd43.tar.xz seitan-8fc77732cbae451ea7b7fde88bef5f322167cd43.tar.zst seitan-8fc77732cbae451ea7b7fde88bef5f322167cd43.zip |
Add test actions
Unit test for the action return, block and continue. The unit test
installs a seccomp filter into the target for filter the getpid syscalls.
Based on the action, the test checks the result of the syscall in the
target to validate the correctness of the actions.
Signed-off-by: Alice Frosi <afrosi@redhat.com>
Diffstat (limited to 'tests')
-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; +} |