From fbc205bab17a25ae3dabef5a2ad97edcfcef9eef Mon Sep 17 00:00:00 2001 From: Alice Frosi Date: Tue, 16 May 2023 08:25:27 +0200 Subject: ops: fix op_call and tests --- common/gluten.h | 10 +- operations.c | 31 +++--- operations.h | 3 +- tests/unit/Makefile | 5 +- tests/unit/test_op_call.c | 256 +++++++++++++++++++++++++--------------------- tests/unit/testutil.h | 10 +- tests/unit/util.c | 70 ++++++++++--- 7 files changed, 234 insertions(+), 151 deletions(-) diff --git a/common/gluten.h b/common/gluten.h index 07f4148..9bd4689 100644 --- a/common/gluten.h +++ b/common/gluten.h @@ -32,6 +32,8 @@ extern struct seccomp_data anonymous_seccomp_data; #define OP_EMPTY { .block = { -1 } } #define NO_FIELD block +#define NS_NUM sizeof(enum ns_type) + enum gluten_offset_type { OFFSET_RO_DATA = 0, OFFSET_DATA = 1, @@ -183,7 +185,7 @@ struct op { } op; }; -#ifdef COOKER +#if defined(COOKER) || defined(SEITAN_TEST) # define GLUTEN_CONST #else # define GLUTEN_CONST const @@ -269,7 +271,11 @@ static inline const void *gluten_ptr(const struct seccomp_data *s, static inline bool check_gluten_limits(struct gluten_offset v, size_t size) { struct gluten_offset off = { v.type, v.offset + size }; - return is_offset_valid(off); + if (is_offset_valid(off)) + return true; + + err(" offset limits are invalid"); + return false; } static inline int gluten_write(struct gluten *g, struct gluten_offset dst, diff --git a/operations.c b/operations.c index 60c8410..8d8b926 100644 --- a/operations.c +++ b/operations.c @@ -34,10 +34,10 @@ static bool is_cookie_valid(int notifyFd, uint64_t id) static int send_target(const struct seccomp_notif_resp *resp, int notifier) { if (!is_cookie_valid(notifier, resp->id)) - ret_err(-1, "the response id isn't valid"); + ret_err(-1, " the response id isn't valid"); if (ioctl(notifier, SECCOMP_IOCTL_NOTIF_SEND, resp) < 0) if (errno != EINPROGRESS) - ret_err(-1, "sending the response"); + ret_err(-1, " sending the response"); return 0; } @@ -45,10 +45,10 @@ static int send_inject_target(const struct seccomp_notif_addfd *resp, int notifier) { if (!is_cookie_valid(notifier, resp->id)) - ret_err(-1, "the response id isn't valid"); + ret_err(-1, " the response id isn't valid"); if (ioctl(notifier, SECCOMP_IOCTL_NOTIF_ADDFD, resp) < 0) if (errno != EINPROGRESS) - ret_err(-1, "sending the response"); + ret_err(-1, " sending the response"); return 0; } @@ -98,9 +98,8 @@ static int prepare_arg_clone(const struct seccomp_notif *req, struct gluten *g, if (gluten_read(NULL, g, &c->nr, op->nr, sizeof(c->nr)) == -1) return -1; - for (i = 0; i < 6; i++) - if (gluten_read(NULL, g, &c->args[i], op->nr, + if (gluten_read(NULL, g, &c->args[i], op->args[i], sizeof(c->args[i])) == -1) return -1; @@ -112,19 +111,19 @@ static int prepare_arg_clone(const struct seccomp_notif *req, struct gluten *g, strncpy(c->ns[i].path, "", PATH_MAX); break; case NS_SPEC_TARGET: - snprintf(c->ns[i].path, PATH_MAX, "/proc/%d/ns/%s", req->pid, - ns_name); + snprintf(c->ns[i].path, PATH_MAX, "/proc/%d/ns/%s", + req->pid, ns_name); break; case NS_SPEC_PID: if (gluten_read(NULL, g, &pid, ns->id, ns->size) == -1) return -1; - snprintf(c->ns[i].path, PATH_MAX , "/proc/%d/ns/%s", pid, + snprintf(c->ns[i].path, PATH_MAX, "/proc/%d/ns/%s", pid, ns_name); break; case NS_SPEC_PATH: if (gluten_read(NULL, g, &p, ns->id, ns->size) == -1) return -1; - snprintf(c->ns[i].path, PATH_MAX , "%s", p); + snprintf(c->ns[i].path, PATH_MAX, "%s", p); break; } } @@ -137,7 +136,7 @@ static int set_namespaces(struct arg_clone *c) int fd; for (i = 0; i < sizeof(enum ns_type); i++) { - if(strcmp(c->ns[i].path, "") == 0) + if (strcmp(c->ns[i].path, "") == 0) continue; if ((fd = open(c->ns[i].path, O_CLOEXEC)) < 0) ret_err(-1, "open for file %s", c->ns[i].path); @@ -160,7 +159,7 @@ static int execute_syscall(void *args) c->args[4], c->args[5]); c->err = errno; if (c->ret < 0) { - perror("syscall"); + perror(" syscall"); exit(EXIT_FAILURE); } exit(0); @@ -207,10 +206,9 @@ int do_call(struct arg_clone *c) /* Create a process that will be moved to the namespace */ child = clone(execute_syscall, stack + sizeof(stack), - CLONE_FILES | CLONE_VM | SIGCHLD, (void *)c); + CLONE_FILES | CLONE_VM | CLONE_VFORK | SIGCHLD, (void *)c); if (child == -1) ret_err(-1, "clone"); - wait(NULL); return 0; } @@ -225,13 +223,16 @@ int op_call(const struct seccomp_notif *req, int notifier, struct gluten *g, resp.flags = 0; resp.error = 0; - prepare_arg_clone(req, g, op, &c); + if (prepare_arg_clone(req, g, op, &c) == -1) + return -1; + debug(" op_call: execute syscall nr=%ld", c.nr); if (do_call(&c) == -1) { resp.error = -1; if (send_target(&resp, notifier) == -1) return -1; } if (c.err != 0) { + err(" failed executing call: %s", strerror(c.err)); resp.error = -1; if (send_target(&resp, notifier) == -1) return -1; diff --git a/operations.h b/operations.h index b1d0ac2..d942ffa 100644 --- a/operations.h +++ b/operations.h @@ -13,7 +13,6 @@ #include "common/util.h" #define STACK_SIZE (1024 * 1024 / 8) -#define NS_NUM (sizeof(enum ns_type)) #define HANDLE_OP(code, call, type) \ case code: \ do { \ @@ -34,7 +33,7 @@ struct ns_path { struct arg_clone { long nr; void *args[6]; - struct ns_path ns[sizeof(enum ns_type)]; + struct ns_path ns[NS_NUM]; long ret; int err; }; diff --git a/tests/unit/Makefile b/tests/unit/Makefile index 966ae7d..6b8738a 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -16,8 +16,9 @@ SRCS_FILTER := $(COOKER_DIR)/filter.c $(COMMON_DIR)/common.c util.c \ HEADERS_FILTER := $(COOKER_DIR)/filter.h $(COMMON_DIR)/common.h \ $(DBG_DIR)/disasm.h testutil.h -HEADERS_OP_CALL := $(COMMON_DIR)/gluten.h $(OP_DIR)/operations.h -SRCS_OP_CALL := $(OP_DIR)/operations.c +HEADERS_OP_CALL := testutil.h $(COMMON_DIR)/gluten.h $(OP_DIR)/operations.h \ + $(COMMON_DIR)/common.h $(COMMON_DIR)/util.h +SRCS_OP_CALL := $(COMMON_DIR)/common.c $(OP_DIR)/operations.c util.c $(COMMON_DIR)/util.c HEADERS_OP := $(COMMON_DIR)/gluten.h $(OP_DIR)/operations.h \ $(COMMON_DIR)/common.h testutil.h $(COMMON_DIR)/util.h diff --git a/tests/unit/test_op_call.c b/tests/unit/test_op_call.c index 7f4b367..ea6f442 100644 --- a/tests/unit/test_op_call.c +++ b/tests/unit/test_op_call.c @@ -14,11 +14,13 @@ #include #include #include +#include #include #include "common/gluten.h" #include "operations.h" +#include "testutil.h" struct args_write_file { char *file; @@ -26,6 +28,13 @@ struct args_write_file { ssize_t size; }; +static long nr; + +static void write_arg(struct op_call *call, int i, long v) +{ + ck_write_gluten(gluten, call->args[i], v); +} + static void write_file(char *file, char *t, ssize_t size) { int fd; @@ -50,159 +59,174 @@ 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(); + install_single_syscall(SYS_getpid); + getpid(); return 0; } -static pid_t create_func_ns(int (*fn)(void *), void *arg, struct ns_spec ns[]) +static void set_ns_to_none(struct op_call *call) { - 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; + for (unsigned int i = 0; i < NS_NUM; i++) + call->context.ns[i].type = NS_NONE; } -START_TEST(test_with_open_read_ns) +static void setup_ns(struct args_write_file *args) { - 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].id.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); + at = mmap(NULL, sizeof(struct args_target), PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + at->nr = __NR_getpid; + at->target = write_file_clone; + at->tclone = (void *)args; + at->ns[NS_MOUNT] = true; + setup(); } -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; + struct op_call call = { + { OFFSET_RO_DATA, 0 }, + { + { OFFSET_DATA, 0 }, + { OFFSET_DATA, sizeof(long) }, + { OFFSET_DATA, sizeof(long) * 2 }, + }, + .has_ret = true, + .ret = { OFFSET_DATA, sizeof(long) * 3 }, + }; char buf[PATH_MAX]; - unsigned i; - long count; - int fd, ret; + long count, ret; + int fd; - 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++) + for (unsigned int i = 0; i < NS_NUM; 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); + + nr = SYS_read; + ck_write_gluten(gluten, call.nr, nr); + write_arg(&call, 0, (long)fd); + write_arg(&call, 1, (long)&buf); + write_arg(&call, 2, (long)count); + nr = SYS_read; + + ck_assert_int_eq(op_call(&req, notifyfd, &gluten, &call), 0); + + ck_read_gluten(gluten, call.ret, ret); + ck_assert_msg(ret == count, "expect ret %ld to be %ld", 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; + struct op_call call = { + .nr = { OFFSET_RO_DATA, 0 }, + .has_ret = true, + .ret = { OFFSET_DATA, 0 }, + }; long pid = (long)getpid(); - int ret; + int ret = -1; - for (i = 0; i < sizeof(enum ns_type); i++) + for (unsigned int i = 0; i < NS_NUM; 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); + + nr = SYS_getppid; + ck_write_gluten(gluten, call.nr, nr); + + ck_assert_int_eq(op_call(&req, notifyfd, &gluten, &call), 0); + + ck_read_gluten(gluten, call.ret, ret); + ck_assert_msg(ret == pid, "expect ret %d to be equal to %ld", ret, pid); } END_TEST +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; + struct op ops[] = { + { OP_CALL, + { .call = { { OFFSET_RO_DATA, 0 }, /* open */ + { + { OFFSET_DATA, 0 }, + { OFFSET_DATA, sizeof(long) }, + { OFFSET_DATA, sizeof(long) * 2 }, + }, + .has_ret = true, + .ret = { OFFSET_DATA, sizeof(long) * 3 } } } }, + { OP_CALL, + { .call = { { OFFSET_RO_DATA, sizeof(long) }, /* read */ + { + { OFFSET_DATA, sizeof(long) * 3 }, /* ret of the previous call*/ + { OFFSET_DATA, sizeof(long) * 5 }, + { OFFSET_DATA, sizeof(long) * 6 }, + }, + .has_ret = true, + .ret = { OFFSET_DATA, sizeof(long) * 7 } } } }, + { OP_END, OP_EMPTY }, + + }; + int flags = O_RDWR; + char buf[PATH_MAX]; + long count, rcount; + + setup_ns(&args); + + /* Copy and configure op_calls */ + set_ns_to_none(&ops[0].op.call); + set_ns_to_none(&ops[1].op.call); + + nr = SYS_open; + call = &ops[0].op.call; + ck_write_gluten(gluten, call->nr, nr); + write_arg(call, 0, (long)&test_file); + write_arg(call, 1, (long)flags); + call->context.ns[NS_MOUNT].type = NS_SPEC_TARGET; + + nr = SYS_read; + call = &ops[1].op.call; + count = sizeof(buf); + ck_write_gluten(gluten, call->nr, nr); + write_arg(call, 1, (long)&buf); + write_arg(call, 2, (long)count); + call->context.ns[NS_MOUNT].type = NS_SPEC_TARGET; + + write_instr(gluten, ops); + + ck_assert_int_eq(eval(&gluten, &req, notifyfd), 0); + ck_read_gluten(gluten, ops[1].op.call.ret, rcount); + ck_assert_msg(rcount == count, "expect ret %ld to be %ld", rcount, count); + ck_assert_str_eq(t, buf); +} +END_TEST + + Suite *op_call_suite(void) { Suite *s; - TCase *tops; + TCase *tsimple, *tread, *treadns; + int timeout = 30; 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); + tsimple = tcase_create("getppid"); + tcase_add_test(tsimple, test_with_getppid); + suite_add_tcase(s, tsimple); + + tread = tcase_create("read"); + tcase_add_test(tread, test_with_read); + suite_add_tcase(s, tread); + + treadns = tcase_create("read ns"); - suite_add_tcase(s, tops); + tcase_add_checked_fixture(treadns, NULL, teardown); + tcase_set_timeout(treadns, timeout); + tcase_add_test(treadns, test_with_open_read_ns); + suite_add_tcase(s, treadns); return s; } diff --git a/tests/unit/testutil.h b/tests/unit/testutil.h index 4f104ac..b63d467 100644 --- a/tests/unit/testutil.h +++ b/tests/unit/testutil.h @@ -34,6 +34,12 @@ static inline void *test_gluten_write_ptr(struct gluten *g, } } +#define write_instr(gluten, ops) \ + do { \ + struct gluten_offset x = { OFFSET_INSTRUCTION, 0 }; \ + memcpy(test_gluten_write_ptr(&gluten, x), &ops, sizeof(ops)); \ + } while (0) + #define ck_write_gluten(gluten, value, ref) \ do { \ void *p = test_gluten_write_ptr(&gluten, value); \ @@ -58,6 +64,8 @@ struct args_target { bool filter_args[6]; struct bpf_arg args[6]; void *targs[6]; + void *tclone; + bool ns[NS_NUM]; int (*install_filter)(struct args_target *at); int (*target)(void *); }; @@ -74,7 +82,6 @@ extern char stdout_buff[BUFSIZ]; extern struct gluten gluten; 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(); @@ -91,4 +98,5 @@ void check_target_result_nonegative(); void ck_error_msg(char *s); void ck_stderr(); void ck_stdout(); +int install_single_syscall(long nr); #endif /* TESTUTIL_H */ diff --git a/tests/unit/util.c b/tests/unit/util.c index 5c36c54..66448bb 100644 --- a/tests/unit/util.c +++ b/tests/unit/util.c @@ -33,7 +33,7 @@ struct gluten gluten; char stderr_buff[BUFSIZ]; char stdout_buff[BUFSIZ]; -int install_notification_filter(struct args_target *at) +int install_single_syscall(long nr) { /* filter a single syscall for the tests */ struct sock_filter filter[] = { @@ -42,12 +42,17 @@ int install_notification_filter(struct args_target *at) 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_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]))); + +} +int install_notification_filter(struct args_target *at) +{ + return install_single_syscall(at->nr); } int target() @@ -76,18 +81,17 @@ int target() exit(0); } -pid_t do_clone(int (*fn)(void *), void *arg) +pid_t do_clone(int (*fn)(void *), void *arg, int flags) { - char stack[STACK_SIZE]; - pid_t child; - int flags = SIGCHLD; + char stack[STACK_SIZE]; + pid_t child; - child = clone(fn, stack + sizeof(stack), flags, arg); - if (child == -1) { - perror("clone"); - return -1; - } - return child; + child = clone(fn, stack + sizeof(stack), flags, arg); + if (child == -1) { + perror("clone"); + return -1; + } + return child; } int create_test_fd() @@ -203,6 +207,46 @@ void set_args_no_check(struct args_target *at) at->args[i].cmp = NO_CHECK; } +static int set_ns_flags(bool ns[], int flags) +{ + unsigned int i; + + for (i = 0; i < NS_NUM; i++) { + if (!ns[i] || i == 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); + } + } + return flags; +} + void setup() { int ret; @@ -211,7 +255,7 @@ void setup() ck_assert_int_ne(pipe(pipefd), -1); if (at->target == NULL) at->target = target; - pid = do_clone(at->target, NULL); + pid = do_clone(at->target, at->tclone, set_ns_flags(at->ns, SIGCHLD)); ck_assert_int_ge(pid, 0); /* Use write pipe to sync the target for checking the existance of the fd */ -- cgit v1.2.3