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. --- actions.c | 274 -------------------------- actions.h | 19 -- gluten.h | 44 ++--- operations.c | 274 ++++++++++++++++++++++++++ operations.h | 19 ++ 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 ++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 980 insertions(+), 980 deletions(-) delete mode 100644 actions.c delete mode 100644 actions.h create mode 100644 operations.c create mode 100644 operations.h 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 diff --git a/actions.c b/actions.c deleted file mode 100644 index bfdd5b1..0000000 --- a/actions.c +++ /dev/null @@ -1,274 +0,0 @@ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gluten.h" -#include "actions.h" - -static bool is_cookie_valid(int notifyFd, uint64_t id) -{ - return ioctl(notifyFd, SECCOMP_IOCTL_NOTIF_ID_VALID, &id) == 0; -} - -static int send_target(const struct seccomp_notif_resp *resp, int notifyfd) -{ - if (!is_cookie_valid(notifyfd, resp->id)) { - fprintf(stderr, - "the response id isn't valid\ncheck if the targets has already terminated\n"); - return -1; - } - if (ioctl(notifyfd, SECCOMP_IOCTL_NOTIF_SEND, resp) < 0) { - if (errno != EINPROGRESS) { - perror("sending the response"); - return -1; - } - } - return 0; -} - -static int send_inject_target(const struct seccomp_notif_addfd *resp, - int notifyfd) -{ - if (!is_cookie_valid(notifyfd, resp->id)) { - fprintf(stderr, - "the response id isn't valid\ncheck if the targets has already terminated\n"); - return -1; - } - if (ioctl(notifyfd, SECCOMP_IOCTL_NOTIF_ADDFD, resp) < 0) { - if (errno != EINPROGRESS) { - perror("sending the response"); - return -1; - } - } - return 0; -} - -static void proc_ns_name(unsigned i, char *ns) -{ - switch (i) { - case NS_CGROUP: - snprintf(ns, PATH_MAX + 1, "cgroup"); - break; - case NS_IPC: - snprintf(ns, PATH_MAX + 1, "ipc"); - break; - case NS_NET: - snprintf(ns, PATH_MAX + 1, "net"); - break; - case NS_MOUNT: - snprintf(ns, PATH_MAX + 1, "mnt"); - break; - case NS_PID: - snprintf(ns, PATH_MAX + 1, "pid"); - break; - case NS_USER: - snprintf(ns, PATH_MAX + 1, "user"); - break; - case NS_UTS: - snprintf(ns, PATH_MAX + 1, "uts"); - break; - case NS_TIME: - snprintf(ns, PATH_MAX + 1, "time"); - break; - default: - fprintf(stderr, "unrecognized namespace index %d\n", i); - } -} - -static int set_namespaces(const struct act_call *a, int tpid) -{ - char path[PATH_MAX + 1]; - char ns_name[PATH_MAX / 2]; - struct ns_spec ns; - int fd; - unsigned int i; - - for (i = 0, ns = (a->context).ns[i]; i < sizeof(enum ns_type); - i++, ns = (a->context).ns[i]) { - proc_ns_name(i, ns_name); - switch (ns.type) { - case NS_NONE: - continue; - case NS_SPEC_TARGET: - snprintf(path, sizeof(path), "/proc/%d/ns/%s", tpid, - ns_name); - break; - case NS_SPEC_PID: - snprintf(path, sizeof(path), "/proc/%d/ns/%s", ns.pid, - ns_name); - break; - case NS_SPEC_PATH: - snprintf(path, sizeof(path), "%s", ns.path); - break; - } - - if ((fd = open(path, O_CLOEXEC)) < 0) { - fprintf(stderr, "open for file %s: %s", path, - strerror(errno)); - return -1; - } - - if (setns(fd, 0) != 0) { - perror("setns"); - return -1; - } - } - return 0; -} - -static int execute_syscall(void *args) -{ - struct arg_clone *a = (struct arg_clone *)args; - const struct act_call *c = a->args; - - if (set_namespaces(a->args, a->pid) < 0) { - exit(EXIT_FAILURE); - } - /* execute syscall */ - a->ret = syscall(c->nr, c->args[0], c->args[1], c->args[2], c->args[3], - c->args[4], c->args[5]); - a->err = errno; - if (a->ret < 0) { - perror("syscall"); - exit(EXIT_FAILURE); - } - exit(0); -} - -int do_call(struct arg_clone *c) -{ - char stack[STACK_SIZE]; - pid_t child; - - c->ret = -1; - c->err = 0; - - /* Create a process that will be moved to the namespace */ - child = clone(execute_syscall, stack + sizeof(stack), - CLONE_FILES | CLONE_VM | SIGCHLD, (void *)c); - if (child == -1) { - perror("clone"); - return -1; - } - wait(NULL); - return 0; -} - -static void set_inject_fields(uint64_t id, void *data, const struct action *a, - struct seccomp_notif_addfd *resp) -{ - const struct fd_type *new = &(a->inj).newfd; - const struct fd_type *old = &(a->inj).oldfd; - - resp->flags = SECCOMP_ADDFD_FLAG_SETFD; - resp->id = id; - if (new->type == IMMEDIATE) - resp->newfd = new->fd; - else - memcpy(&resp->srcfd, (uint16_t *)data + old->fd_off, - sizeof(resp->srcfd)); - if (old->type == IMMEDIATE) - resp->srcfd = old->fd; - else - memcpy(&resp->srcfd, (uint16_t *)data + old->fd_off, - sizeof(resp->srcfd)); - resp->newfd_flags = 0; -} - -int do_actions(void *data, struct action actions[], unsigned int n_actions, - int pid, int notifyfd, uint64_t id) -{ - struct seccomp_notif_addfd resp_fd; - struct seccomp_notif_resp resp; - struct arg_clone c; - unsigned int i; - - for (i = 0; i < n_actions; i++) { - switch (actions[i].type) { - case A_CALL: - resp.id = id; - resp.val = 0; - resp.flags = 0; - c.args = &actions[i].call; - c.pid = pid; - if (do_call(&c) == -1) { - resp.error = -1; - if (send_target(&resp, notifyfd) == -1) - return -1; - } - if (c.err != 0) { - resp.error = -1; - if (send_target(&resp, notifyfd) == -1) - return c.err; - } - /* - * The result of the call needs to be save as - * reference - */ - if (actions[i].call.has_ret) { - memcpy((uint16_t *)data + - actions[i].call.ret_off, - &c.ret, sizeof(c.ret)); - } - break; - case A_BLOCK: - resp.id = id; - resp.val = 0; - resp.flags = 0; - resp.error = actions[i].block.error; - if (send_target(&resp, notifyfd) == -1) - return -1; - break; - case A_RETURN: - resp.id = id; - resp.flags = 0; - resp.error = 0; - if (actions[i].ret.type == IMMEDIATE) - resp.val = actions[i].ret.value; - else - memcpy(&resp.val, - (uint16_t *)data + - actions[i].ret.value_off, - sizeof(resp.val)); - - if (send_target(&resp, notifyfd) == -1) - return -1; - break; - - case A_CONT: - resp.id = id; - resp.flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE; - resp.error = 0; - resp.val = 0; - if (send_target(&resp, notifyfd) == -1) - return -1; - break; - case A_INJECT_A: - set_inject_fields(id, data, &actions[i], &resp_fd); - resp_fd.flags |= SECCOMP_ADDFD_FLAG_SEND; - if (send_inject_target(&resp_fd, notifyfd) == -1) - return -1; - break; - case A_INJECT: - set_inject_fields(id, data, &actions[i], &resp_fd); - if (send_inject_target(&resp_fd, notifyfd) == -1) - return -1; - break; - default: - fprintf(stderr, "unknow action %d \n", actions[i].type); - } - } - return 0; -} diff --git a/actions.h b/actions.h deleted file mode 100644 index c3376b7..0000000 --- a/actions.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef ACIONS_H -#define ACTIONS_H - -#include - -#define STACK_SIZE (1024 * 1024 / 8) -#define NS_NUM (sizeof(enum ns_type)) - -struct arg_clone { - const struct act_call *args; - pid_t pid; - long ret; - int err; -}; - -int do_call(struct arg_clone *c); -int do_actions(void *data, struct action actions[], unsigned int n_actions, - int tpid, int notifyfd, uint64_t id); -#endif /* ACTIONS_H */ diff --git a/gluten.h b/gluten.h index e027923..1b6c3d1 100644 --- a/gluten.h +++ b/gluten.h @@ -36,20 +36,20 @@ enum ns_type { }; /* - * struct act_context - Description of the context where the call needs to be executed + * struct op_context - Description of the context where the call needs to be executed * @ns: Descrption of the each namespace where the call needs to be executed */ -struct act_context { +struct op_context { struct ns_spec ns[sizeof(enum ns_type)]; }; -enum action_type { - A_CALL, - A_BLOCK, - A_CONT, - A_INJECT, - A_INJECT_A, - A_RETURN, +enum op_type { + OP_CALL, + OP_BLOCK, + OP_CONT, + OP_INJECT, + OP_INJECT_A, + OP_RETURN, }; enum value_type { @@ -57,23 +57,23 @@ enum value_type { REFERENCE, }; -struct act_call { +struct op_call { long nr; bool has_ret; void *args[6]; - struct act_context context; + struct op_context context; uint16_t ret_off; }; -struct act_block { +struct op_block { int32_t error; }; -struct act_continue { +struct op_continue { bool cont; }; -struct act_return { +struct op_return { enum value_type type; union { int64_t value; @@ -89,19 +89,19 @@ struct fd_type { }; }; -struct act_inject { +struct op_inject { struct fd_type newfd; struct fd_type oldfd; }; -struct action { - enum action_type type; +struct op { + enum op_type type; union { - struct act_call call; - struct act_block block; - struct act_continue cont; - struct act_return ret; - struct act_inject inj; + struct op_call call; + struct op_block block; + struct op_continue cont; + struct op_return ret; + struct op_inject inj; }; }; #endif /* GLUTEN_H */ diff --git a/operations.c b/operations.c new file mode 100644 index 0000000..82b99a8 --- /dev/null +++ b/operations.c @@ -0,0 +1,274 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gluten.h" +#include "operations.h" + +static bool is_cookie_valid(int notifyFd, uint64_t id) +{ + return ioctl(notifyFd, SECCOMP_IOCTL_NOTIF_ID_VALID, &id) == 0; +} + +static int send_target(const struct seccomp_notif_resp *resp, int notifyfd) +{ + if (!is_cookie_valid(notifyfd, resp->id)) { + fprintf(stderr, + "the response id isn't valid\ncheck if the targets has already terminated\n"); + return -1; + } + if (ioctl(notifyfd, SECCOMP_IOCTL_NOTIF_SEND, resp) < 0) { + if (errno != EINPROGRESS) { + perror("sending the response"); + return -1; + } + } + return 0; +} + +static int send_inject_target(const struct seccomp_notif_addfd *resp, + int notifyfd) +{ + if (!is_cookie_valid(notifyfd, resp->id)) { + fprintf(stderr, + "the response id isn't valid\ncheck if the targets has already terminated\n"); + return -1; + } + if (ioctl(notifyfd, SECCOMP_IOCTL_NOTIF_ADDFD, resp) < 0) { + if (errno != EINPROGRESS) { + perror("sending the response"); + return -1; + } + } + return 0; +} + +static void proc_ns_name(unsigned i, char *ns) +{ + switch (i) { + case NS_CGROUP: + snprintf(ns, PATH_MAX + 1, "cgroup"); + break; + case NS_IPC: + snprintf(ns, PATH_MAX + 1, "ipc"); + break; + case NS_NET: + snprintf(ns, PATH_MAX + 1, "net"); + break; + case NS_MOUNT: + snprintf(ns, PATH_MAX + 1, "mnt"); + break; + case NS_PID: + snprintf(ns, PATH_MAX + 1, "pid"); + break; + case NS_USER: + snprintf(ns, PATH_MAX + 1, "user"); + break; + case NS_UTS: + snprintf(ns, PATH_MAX + 1, "uts"); + break; + case NS_TIME: + snprintf(ns, PATH_MAX + 1, "time"); + break; + default: + fprintf(stderr, "unrecognized namespace index %d\n", i); + } +} + +static int set_namespaces(const struct op_call *a, int tpid) +{ + char path[PATH_MAX + 1]; + char ns_name[PATH_MAX / 2]; + struct ns_spec ns; + int fd; + unsigned int i; + + for (i = 0, ns = (a->context).ns[i]; i < sizeof(enum ns_type); + i++, ns = (a->context).ns[i]) { + proc_ns_name(i, ns_name); + switch (ns.type) { + case NS_NONE: + continue; + case NS_SPEC_TARGET: + snprintf(path, sizeof(path), "/proc/%d/ns/%s", tpid, + ns_name); + break; + case NS_SPEC_PID: + snprintf(path, sizeof(path), "/proc/%d/ns/%s", ns.pid, + ns_name); + break; + case NS_SPEC_PATH: + snprintf(path, sizeof(path), "%s", ns.path); + break; + } + + if ((fd = open(path, O_CLOEXEC)) < 0) { + fprintf(stderr, "open for file %s: %s", path, + strerror(errno)); + return -1; + } + + if (setns(fd, 0) != 0) { + perror("setns"); + return -1; + } + } + return 0; +} + +static int execute_syscall(void *args) +{ + struct arg_clone *a = (struct arg_clone *)args; + const struct op_call *c = a->args; + + if (set_namespaces(a->args, a->pid) < 0) { + exit(EXIT_FAILURE); + } + /* execute syscall */ + a->ret = syscall(c->nr, c->args[0], c->args[1], c->args[2], c->args[3], + c->args[4], c->args[5]); + a->err = errno; + if (a->ret < 0) { + perror("syscall"); + exit(EXIT_FAILURE); + } + exit(0); +} + +int do_call(struct arg_clone *c) +{ + char stack[STACK_SIZE]; + pid_t child; + + c->ret = -1; + c->err = 0; + + /* Create a process that will be moved to the namespace */ + child = clone(execute_syscall, stack + sizeof(stack), + CLONE_FILES | CLONE_VM | SIGCHLD, (void *)c); + if (child == -1) { + perror("clone"); + return -1; + } + wait(NULL); + return 0; +} + +static void set_inject_fields(uint64_t id, void *data, const struct op *a, + struct seccomp_notif_addfd *resp) +{ + const struct fd_type *new = &(a->inj).newfd; + const struct fd_type *old = &(a->inj).oldfd; + + resp->flags = SECCOMP_ADDFD_FLAG_SETFD; + resp->id = id; + if (new->type == IMMEDIATE) + resp->newfd = new->fd; + else + memcpy(&resp->srcfd, (uint16_t *)data + old->fd_off, + sizeof(resp->srcfd)); + if (old->type == IMMEDIATE) + resp->srcfd = old->fd; + else + memcpy(&resp->srcfd, (uint16_t *)data + old->fd_off, + sizeof(resp->srcfd)); + resp->newfd_flags = 0; +} + +int do_operations(void *data, struct op operations[], unsigned int n_operations, + int pid, int notifyfd, uint64_t id) +{ + struct seccomp_notif_addfd resp_fd; + struct seccomp_notif_resp resp; + struct arg_clone c; + unsigned int i; + + for (i = 0; i < n_operations; i++) { + switch (operations[i].type) { + case OP_CALL: + resp.id = id; + resp.val = 0; + resp.flags = 0; + c.args = &operations[i].call; + c.pid = pid; + if (do_call(&c) == -1) { + resp.error = -1; + if (send_target(&resp, notifyfd) == -1) + return -1; + } + if (c.err != 0) { + resp.error = -1; + if (send_target(&resp, notifyfd) == -1) + return c.err; + } + /* + * The result of the call needs to be save as + * reference + */ + if (operations[i].call.has_ret) { + memcpy((uint16_t *)data + + operations[i].call.ret_off, + &c.ret, sizeof(c.ret)); + } + break; + case OP_BLOCK: + resp.id = id; + resp.val = 0; + resp.flags = 0; + resp.error = operations[i].block.error; + if (send_target(&resp, notifyfd) == -1) + return -1; + break; + case OP_RETURN: + resp.id = id; + resp.flags = 0; + resp.error = 0; + if (operations[i].ret.type == IMMEDIATE) + resp.val = operations[i].ret.value; + else + memcpy(&resp.val, + (uint16_t *)data + + operations[i].ret.value_off, + sizeof(resp.val)); + + if (send_target(&resp, notifyfd) == -1) + return -1; + break; + + case OP_CONT: + resp.id = id; + resp.flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE; + resp.error = 0; + resp.val = 0; + if (send_target(&resp, notifyfd) == -1) + return -1; + break; + case OP_INJECT_A: + set_inject_fields(id, data, &operations[i], &resp_fd); + resp_fd.flags |= SECCOMP_ADDFD_FLAG_SEND; + if (send_inject_target(&resp_fd, notifyfd) == -1) + return -1; + break; + case OP_INJECT: + set_inject_fields(id, data, &operations[i], &resp_fd); + if (send_inject_target(&resp_fd, notifyfd) == -1) + return -1; + break; + default: + fprintf(stderr, "unknow operation %d \n", operations[i].type); + } + } + return 0; +} diff --git a/operations.h b/operations.h new file mode 100644 index 0000000..aeb09d5 --- /dev/null +++ b/operations.h @@ -0,0 +1,19 @@ +#ifndef ACIONS_H +#define ACTIONS_H + +#include + +#define STACK_SIZE (1024 * 1024 / 8) +#define NS_NUM (sizeof(enum ns_type)) + +struct arg_clone { + const struct op_call *args; + pid_t pid; + long ret; + int err; +}; + +int do_call(struct arg_clone *c); +int do_operations(void *data, struct op operations[], unsigned int n_operations, + int tpid, int notifyfd, uint64_t id); +#endif /* ACTIONS_H */ 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