diff options
Diffstat (limited to 'tests/unit')
-rw-r--r-- | tests/unit/Makefile | 8 | ||||
-rw-r--r-- | tests/unit/test_operations.c | 177 | ||||
-rw-r--r-- | tests/unit/testutil.h | 45 | ||||
-rw-r--r-- | tests/unit/util.c | 184 |
4 files changed, 238 insertions, 176 deletions
diff --git a/tests/unit/Makefile b/tests/unit/Makefile index aad8549..1c3a413 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -13,9 +13,9 @@ HEADERS_FILTER := $(COOKER_DIR)/filter.h $(DBG_DIR)/disasm.h $(COMMON_DIR)/commo HEADERS_OP_CALL := $(COMMON_DIR)/gluten.h $(OP_DIR)/operations.h SRCS_OP_CALL := $(OP_DIR)/operations.c -HEADERS_OP_CALL := $(COMMON_DIR)/gluten.h $(OP_DIR)/operations.h \ - $(COMMON_DIR)/common.h -SRCS_OP := $(COMMON_DIR)/common.c $(OP_DIR)/operations.c +HEADERS_OP := $(COMMON_DIR)/gluten.h $(OP_DIR)/operations.h \ + $(COMMON_DIR)/common.h testutil.h +SRCS_OP := $(COMMON_DIR)/common.c $(OP_DIR)/operations.c util.c TARGET := $(shell $(CC) -dumpmachine) TARGET_ARCH := $(shell echo $(TARGET) | cut -f1 -d- | tr [A-Z] [a-z]) @@ -43,7 +43,7 @@ test-op-call: test_op_call.c $(SRCS_OP_CALL) $(HEADERS_OP_CALL) test_op_call.c ./test-op-call -test-operations: test_operations.c $(SRCS_OP) $(HEADERS_OP_CALL) +test-operations: test_operations.c $(SRCS_OP) $(HEADERS_OP) $(CC) $(CFLAGS) -o test-operations $(SRCS_OP) \ test_operations.c ./test-operations diff --git a/tests/unit/test_operations.c b/tests/unit/test_operations.c index c60fa4f..bb098b8 100644 --- a/tests/unit/test_operations.c +++ b/tests/unit/test_operations.c @@ -28,187 +28,17 @@ #include "gluten.h" #include "operations.h" #include "common.h" -#include "util.h" +#include "testutil.h" #define MAX_TEST_PATH 250 -struct args_target { - long ret; - int err; - bool check_fd; - bool open_path; - int fd; - int nr; - void *args[6]; -}; - -struct seccomp_notif req; -int notifyfd; -struct args_target *at; -int pipefd[2]; -pid_t pid; -char path[] = "/tmp/test-seitan"; - -uint16_t tmp_data[TMP_DATA_SIZE]; - -static int install_notification_filter(int nr) -{ - 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), - }; - return install_filter( - &filter, (unsigned short)(sizeof(filter) / sizeof(filter[0]))); -} - -static int create_test_fd() -{ - return open("/tmp", O_RDWR | O_TMPFILE); -} - -static int target() -{ - int buf = 0; - if (install_notification_filter(at->nr) < 0) { - return -1; - } - - at->ret = syscall(at->nr, at->args[0], at->args[1], at->args[2], - at->args[3], at->args[4], at->args[5]); - at->err = errno; - if (at->open_path) { - if ((at->fd = open(path, O_CREAT | O_RDONLY)) < 0) { - perror("open"); - return -1; - } - } - 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() -{ - int ret; - - signal(SIGCHLD, target_exit); - ck_assert_int_ne(pipe(pipefd), -1); - 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 (!at->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 == at->nr, "filter syscall nr: %d", - (req.data).nr); -} - -void teardown() -{ - if (at != NULL) - munmap(at, sizeof(struct args_target)); -} - void setup_without_fd() { at = mmap(NULL, sizeof(struct args_target), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); at->check_fd = false; at->nr = __NR_getpid; + at->install_filter = install_notification_filter; setup(); } void setup_fd() @@ -217,6 +47,7 @@ void setup_fd() MAP_SHARED | MAP_ANONYMOUS, -1, 0); at->check_fd = true; at->nr = __NR_getpid; + at->install_filter = install_notification_filter; setup(); } @@ -226,6 +57,7 @@ void setup_path() MAP_SHARED | MAP_ANONYMOUS, -1, 0); at->open_path = true; at->nr = __NR_getpid; + at->install_filter = install_notification_filter; setup(); } @@ -248,6 +80,7 @@ void setup_target_connect() at->args[0] = (void *)(long)fd; at->args[1] = (void *)&addr; at->args[2] = (void *)(long)len; + at->install_filter = install_notification_filter; setup(); } diff --git a/tests/unit/testutil.h b/tests/unit/testutil.h new file mode 100644 index 0000000..896d690 --- /dev/null +++ b/tests/unit/testutil.h @@ -0,0 +1,45 @@ +#ifndef TESTUTIL_H +#define TESTUTIL_H + +#include <stdint.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stddef.h> + +#include <check.h> + +#define STACK_SIZE (1024 * 1024 / 8) + +struct args_target { + long ret; + int err; + bool check_fd; + bool open_path; + int fd; + int nr; + void *args[6]; + int (*install_filter)(struct args_target *at); +}; + +extern struct seccomp_notif req; +extern int notifyfd; +extern struct args_target *at; +extern int pipefd[2]; +extern pid_t pid; +extern char path[100]; + +extern uint16_t tmp_data[TMP_DATA_SIZE]; + +int target(); +pid_t do_clone(int (*fn)(void *), void *arg); +int create_test_fd(); +int get_fd_notifier(pid_t pid); +void target_exit(); +void check_target_fd(int pid, int fd); +bool has_fd(int pid, int fd); +void check_target_result(long ret, int err, bool ignore_ret); +void setup(); +void teardown(); +int install_notification_filter(struct args_target *at); + +#endif /* TESTUTIL_H */ diff --git a/tests/unit/util.c b/tests/unit/util.c new file mode 100644 index 0000000..d4109b0 --- /dev/null +++ b/tests/unit/util.c @@ -0,0 +1,184 @@ +#define _GNU_SOURCE + +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <sched.h> +#include <unistd.h> +#include <signal.h> +#include <limits.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/prctl.h> +#include <sys/syscall.h> +#include <sys/ioctl.h> +#include <sys/wait.h> +#include <linux/audit.h> +#include <linux/filter.h> +#include <linux/seccomp.h> +#include <sys/mman.h> +#include <sys/un.h> +#include <sys/socket.h> + +#include "testutil.h" +#include "common.h" +#include "filter.h" + +struct seccomp_notif req; +int notifyfd; +struct args_target *at; +int pipefd[2]; +pid_t pid; +char path[] = "/tmp/test-seitan"; +uint16_t tmp_data[TMP_DATA_SIZE]; + +int install_notification_filter(struct args_target *at) +{ + /* 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, at->nr, 0, 1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), + BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW), + }; + return install_filter( + filter, (unsigned short)(sizeof(filter) / sizeof(filter[0]))); +} + +int target() +{ + int buf = 0; + if (at->install_filter(at) < 0) { + return -1; + } + + at->ret = syscall(at->nr, at->args[0], at->args[1], at->args[2], + at->args[3], at->args[4], at->args[5]); + at->err = errno; + if (at->open_path) { + if ((at->fd = open(path, O_CREAT | O_RDONLY)) < 0) { + perror("open"); + return -1; + } + } + if (at->check_fd) + read(pipefd[0], &buf, 1); + + close(pipefd[0]); + + write(pipefd[1], &buf, 1); + close(pipefd[1]); + exit(0); +} + +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 create_test_fd() +{ + return open("/tmp", O_RDWR | O_TMPFILE); +} + +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; +} + +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); + } +} + +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]); +} + +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; +} + +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 setup() +{ + int ret; + + signal(SIGCHLD, target_exit); + ck_assert_int_ne(pipe(pipefd), -1); + 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 (!at->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 == at->nr, "filter syscall nr: %d", + (req.data).nr); +} + +void teardown() +{ + if (at != NULL) + munmap(at, sizeof(struct args_target)); +} |