From b13627a889e6ba2f6e9c136b6d0eb08ac0646658 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Thu, 1 Jun 2017 00:00:07 +0900 Subject: wip --- sandbox/Makefile | 16 +- sandbox/README.md | 12 ++ sandbox/cgroup.c | 47 ++++-- sandbox/child.c | 18 +-- sandbox/main.c | 438 +++++++++++++++++++++++++++++++++++---------------- sandbox/playground.c | 14 +- sandbox/sandbox.h | 18 ++- sandbox/utils.h | 69 ++++++++ 8 files changed, 455 insertions(+), 177 deletions(-) create mode 100644 sandbox/README.md diff --git a/sandbox/Makefile b/sandbox/Makefile index 47e10b1..b41b6e8 100644 --- a/sandbox/Makefile +++ b/sandbox/Makefile @@ -1,17 +1,23 @@ -CFLAGS ?= -std=c11 -g -O3 -D_GNU_SOURCE -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-all \ - -Wall -Wextra -Wpedantic -LDFLAGS ?= -pie -Wl,-z,relro,-z,now -lseccomp +CFLAGS = -std=c11 -g -O3 -D_GNU_SOURCE -D_FORTIFY_SOURCE=2 \ + -Wall -Wextra -Wno-unused-parameter -Wno-parentheses +LDFLAGS = +LIBS = -lseccomp DEPS := sandbox.h config.h utils.h OBJS := main.o playground.o seccomp.o cgroup.o child.o +ifndef V + QUIET_CC = @echo ' ' CC $@; + QUIET_LINK = @echo ' ' LINK $@; +endif + all: sandbox sandbox: $(OBJS) - $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) + $(QUIET_LINK)$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LIBS) %.o: %.c $(DEPS) - $(CC) -c -o $@ $< $(CFLAGS) + $(QUIET_CC)$(CC) -c -o $@ $< $(CFLAGS) runner: sandbox install -m 6755 -o root sandbox runner diff --git a/sandbox/README.md b/sandbox/README.md new file mode 100644 index 0000000..e81d37d --- /dev/null +++ b/sandbox/README.md @@ -0,0 +1,12 @@ +poe/sandbox +=========== + +A secure sandbox using namespaces, cgroups and seccomp. + +Example +------- + + cd poe/sandbox + make + sudo install -m 6755 -o root sandbox runner + make test diff --git a/sandbox/cgroup.c b/sandbox/cgroup.c index 5ceeddd..885824f 100644 --- a/sandbox/cgroup.c +++ b/sandbox/cgroup.c @@ -49,7 +49,7 @@ static void cgroup_destroy(unused int status, void *cgroup_) free(cgroup); } -warn_unused static int cgroup_find(bool *is_mounted) +static int cgroup_find(bool *is_mounted) { *is_mounted = false; @@ -72,32 +72,44 @@ warn_unused static int cgroup_find(bool *is_mounted) return 0; } -warn_unused int poe_cgroup_init(void) +struct cgroups *poe_cgroups_init(const char *tmpdir) { + struct cgroups *cg = xmalloc(sizeof(*cg)); + bool is_mounted; if (cgroup_find(&is_mounted)) - return -1; + return NULL; if (!is_mounted) { struct stat sb; if (stat(POE_CGROUP_ROOT, &sb)) { - if (errno != ENOENT) - return error("cgroup root dir not accessible"); + if (errno != ENOENT) { + error("cgroup root dir not accessible"); + return NULL; + } /* if not exist */ - if (mkdir(POE_CGROUP_ROOT, 0755)) - return error("can't create cgroup root dir"); + if (mkdir(POE_CGROUP_ROOT, 0755)) { + error("can't create cgroup root dir"); + return NULL; + } + } + if (mount("poe-cgroup2", POE_CGROUP_ROOT, "cgroup2", 0, NULL)) { + error("can't mount cgroup root dir"); + return NULL; } - if (mount("poe-cgroup2", POE_CGROUP_ROOT, "cgroup2", 0, NULL)) - return error("can't mount cgroup root dir"); } /* create sub group */ struct stat sb; if (stat(POE_CGROUP_ROOT"/poe", &sb)) { - if (errno != ENOENT) - return error("can't access sub cgroup"); + if (errno != ENOENT) { + error("can't access sub cgroup"); + return NULL; + } - if (mkdir(POE_CGROUP_ROOT"/poe", 0755)) - return error("can't create sub cgroup"); + if (mkdir(POE_CGROUP_ROOT"/poe", 0755)) { + error("can't create sub cgroup"); + return NULL; + } } /* then, configure it */ @@ -106,10 +118,15 @@ warn_unused int poe_cgroup_init(void) // 他の poe-sandbox が使用するかもしれないので、マウントした cgroup は削除しない // どうしようこれ - return 0; + return cg; +} + +void poe_cgroups_destroy(struct cgroups *cg) +{ + free(cg); } -warn_unused int poe_cgroup_add(pid_t pid) +int poe_cgroups_add(struct cgroups *cg, pid_t pid) { char *template = xstrdup(POE_CGROUP_ROOT"/poe/poe-sandbox-XXXXXX"); if (!mkdtemp(template)) diff --git a/sandbox/child.c b/sandbox/child.c index c52dc11..ff92d04 100644 --- a/sandbox/child.c +++ b/sandbox/child.c @@ -9,13 +9,8 @@ bug("CRITICAL: %s:%d %s", __FILE__, __LINE__, #s); \ } while (0) -noreturn void poe_child_do(struct playground *pg, - int stdout_fd[2], int stderr_fd[2], int child_fd[2]) +_Noreturn void poe_child_do(struct playground *pg, int child_fd) { - if (dup2(child_fd[1], STDERR_FILENO) < 0) - // 標準エラー出力に出ちゃうけどしかたない - bug("dup2 child_fd_w to stdout failed"); - if (atexit(abort)) bug("atexit failed"); if (syscall(SYS_getpid) != 1) @@ -49,12 +44,11 @@ noreturn void poe_child_do(struct playground *pg, if (poe_seccomp_init()) bug("seccomp init failed"); - // child_fd は exec によって close される - if (dup2(stdout_fd[1], STDOUT_FILENO) < 0 || close(stdout_fd[0]) || close(stdout_fd[1])) - bug("dup2/close stdout failed"); - if (dup2(stderr_fd[1], STDERR_FILENO) < 0 || close(stderr_fd[0]) || close(stderr_fd[1])) - bug("dup2/close stderr failed"); + /* FIXME */ + write(child_fd, "x", 1); + close(child_fd); + // child_fd は exec によって close される char *const env[] = { "PATH=/usr/bin", "USER=" POE_USERNAME, @@ -66,4 +60,6 @@ noreturn void poe_child_do(struct playground *pg, checked_syscall(execvpe(pg->command_line[0], pg->command_line, env)); bug("unreachable"); +err: + abort(); } diff --git a/sandbox/main.c b/sandbox/main.c index 44b1742..2e225c6 100644 --- a/sandbox/main.c +++ b/sandbox/main.c @@ -1,8 +1,9 @@ #include "sandbox.h" +#include static struct timespec start_timespec = { 0 }; -static noreturn void finish(enum poe_exit_reason reason, int status, const char *fmt, ...) +static _Noreturn void finish(enum poe_exit_reason reason, int status, const char *fmt, ...) { if (!start_timespec.tv_sec && !start_timespec.tv_nsec) bug("start_timespec not set?"); @@ -23,175 +24,332 @@ static noreturn void finish(enum poe_exit_reason reason, int status, const char exit(0); } -static void handle_stdout(int fd, int orig_fd) +static void handle_stdout(int in_fd, int fd) { - assert(PIPE_BUF % 4 == 0); - uint32_t buf[PIPE_BUF / 4 + 2]; - ssize_t n = read(fd, (char *)(buf + 2), PIPE_BUF); - if (n < 0) - bug("read from stdout/err pipe"); - buf[0] = (uint32_t)orig_fd; - buf[1] = (uint32_t)n; - if (write(STDOUT_FILENO, buf, n + 8) < 0) - bug("write to stdout failed"); + unsigned char buf[4 + 4 + PIPE_BUF]; + ssize_t n; + uint32_t un; + + n = read(in_fd, buf + 8, PIPE_BUF); + if (n > 0) { + buf[0] = (fd >> 0) & 0xff; + buf[1] = (fd >> 8) & 0xff; + buf[2] = (fd >> 16) & 0xff; + buf[3] = (fd >> 24) & 0xff; + + un = (uint32_t)n; + buf[4] = (un >> 0) & 0xff; + buf[5] = (un >> 8) & 0xff; + buf[6] = (un >> 16) & 0xff; + buf[7] = (un >> 24) & 0xff; + + if (write(1, buf, 8 + n) != 8 + n) + die("write failed"); + } } -static void handle_signal(pid_t mpid, struct signalfd_siginfo *si) +static void handle_signalfd(pid_t child_pid, int fd) { - if (si->ssi_signo == SIGINT || si->ssi_signo == SIGTERM || - si->ssi_signo == SIGHUP) - finish(POE_TIMEDOUT, -1, "Supervisor terminated"); - if (si->ssi_signo != SIGCHLD) - bug("unknown signal %d", si->ssi_signo); - + struct signalfd_siginfo si; int status; pid_t spid; - while ((spid = waitpid(-mpid, &status, WNOHANG | __WALL)) > 0) { - if (spid == mpid && WIFEXITED(status)) { + long syscalln; + + CHECK(read(fd, &si, sizeof(si) != sizeof(si))); + if (si.ssi_signo == SIGINT || + si.ssi_signo == SIGTERM || + si.ssi_signo == SIGHUP) + finish(POE_TIMEDOUT, -1, "Supervisor terminated"); + + CHECK(si.ssi_signo == SIGCHLD); + while ((spid = waitpid(-child_pid, &status, WNOHANG | __WALL)) > 0) { + if (spid == child_pid && WIFEXITED(status)) finish(POE_SUCCESS, WEXITSTATUS(status), NULL); - } else if (spid == mpid && WIFSIGNALED(status)) { + if (spid == child_pid && WIFSIGNALED(status)) { int sig = WTERMSIG(status); finish(POE_SIGNALED, -1, "Program terminated with signal %d (%s)", sig, strsignal(sig)); - } else if (WIFSTOPPED(status)) { - switch (status >> 16 & 0xff) { - case PTRACE_EVENT_SECCOMP: - errno = 0; - int syscalln = ptrace(PTRACE_PEEKUSER, spid, sizeof(long) * ORIG_RAX); - if (errno) - bug("ptrace(PTRACE_PEEKUSER) failed"); - char *name = poe_seccomp_syscall_resolve(syscalln); - finish(POE_SIGNALED, -1, "System call %s is blocked", name); - break; - case PTRACE_EVENT_CLONE: - case PTRACE_EVENT_FORK: - case PTRACE_EVENT_VFORK: - ptrace(PTRACE_CONT, spid, 0, 0); - break; - default: - ptrace(PTRACE_CONT, spid, 0, WSTOPSIG(status)); - break; - } + } + if (!WIFSTOPPED(status)) + continue; + + switch (status >> 16 & 0xff) { + case PTRACE_EVENT_SECCOMP: + errno = 0; + syscalln = ptrace(PTRACE_PEEKUSER, spid, 8 * ORIG_RAX); + if (errno) + die("ptrace(PTRACE_PEEKUSER) failed: %s", + strerror(errno)); + finish(POE_SIGNALED, -1, "System call %s is blocked", + poe_seccomp_syscall_resolve(syscalln)); + case PTRACE_EVENT_CLONE: + case PTRACE_EVENT_FORK: + case PTRACE_EVENT_VFORK: + ptrace(PTRACE_CONT, spid, 0, 0); + break; + default: + ptrace(PTRACE_CONT, spid, 0, WSTOPSIG(status)); + break; } } - if (spid < 0) { - if (errno == ECHILD) - bug("child dies too early (before raising SIGSTOP)"); - else - bug("waitpid failed"); - } + if (spid < 0) + die("waitpid failed: %s", strerror(errno)); } -int main(int argc, char *argv[]) +static int do_epoll_add(int epfd, int fd) { - if (argc < 5) - die("usage: runner basedir overlaydir sourcefile cmdl.."); + struct epoll_event event = { + .events = EPOLLRDHUP | EPOLLIN, + .data.fd = fd + }; - struct playground *pg = poe_playground_init(argv[1], argv[2]); - if (!pg) - die("playground init failed"); - if (poe_playground_init_command_line(pg, argv + 4, argv[3])) - die("copy program failed"); + return epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event); +} +static int do_spawn(struct playground *pg, struct cgroups *cg, char **cmdl) +{ int stdout_fd[2], stderr_fd[2], child_fd[2]; - if (pipe2(stdout_fd, O_DIRECT)) - bug("pipe2 failed"); - if (pipe2(stderr_fd, O_DIRECT)) - bug("pipe2 failed"); - if (pipe2(child_fd, O_DIRECT | O_CLOEXEC)) - bug("pipe2 failed"); + int pid; - // init cgroup: create root hierarchy and setup controllers - if (poe_cgroup_init()) - die("failed to init cgroup"); + if (pipe2(stdout_fd, O_DIRECT) || + pipe2(stderr_fd, O_DIRECT) || + pipe2(child_fd, O_DIRECT | O_CLOEXEC)) { + error("pipe2 failed"); + return -1; + } // TODO: CLONE_NEWUSER - pid_t pid = (pid_t)syscall(SYS_clone, SIGCHLD | CLONE_NEWIPC | CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWNET, 0); - if (pid < 0) - bug("clone failed"); - if (!pid) { - poe_child_do(pg, stdout_fd, stderr_fd, child_fd); - bug("unreachable"); + pid = (int)syscall(__NR_clone, SIGCHLD | + CLONE_NEWIPC | CLONE_NEWNS | CLONE_NEWPID | + CLONE_NEWUTS | CLONE_NEWNET, 0); + if (pid < 0) { + error("clone failed"); + return -1; } + if (pid) { + char buf[PIPE_BUF]; + ssize_t n; + sigset_t mask; + int signal_fd, timer_fd, epoll_fd; + struct itimerspec itspec = { + /* FIXME: make it configurable */ + .it_value.tv_sec = POE_TIME_LIMIT + }; + + /* Close the write-side of fds */ + if (close(stdout_fd[1]) || close(stderr_fd[1]) || close(child_fd[1])) { + error("close failed"); + goto bailout; + } + + /* limit memory, cpu and processes */ + if (poe_cgroups_add(cg, pid)) + goto bailout; + + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); /* ptrace */ + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGHUP); + sigprocmask(SIG_BLOCK, &mask, NULL); + signal_fd = signalfd(-1, &mask, 0); + if (signal_fd < 0) { + error("signalfd failed: %s", strerror(errno)); + goto bailout; + } + + timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); + if (timer_fd < 0 || + timerfd_settime(timer_fd, 0, &itspec, NULL)) { + error("timerfd failed: %s", strerror(errno)); + goto bailout; + } + + epoll_fd = epoll_create1(0); + if (epoll_fd < 0 || + do_epoll_add(epoll_fd, signal_fd) || + do_epoll_add(epoll_fd, timer_fd) || + do_epoll_add(epoll_fd, stdout_fd[0]) || + do_epoll_add(epoll_fd, stderr_fd[0])) { + error("epollfd failed: %s", strerror(errno)); + goto bailout; + } + + if (ptrace(PTRACE_SEIZE, pid, NULL, + PTRACE_O_TRACECLONE | + PTRACE_O_TRACEFORK | + PTRACE_O_TRACESECCOMP | + PTRACE_O_TRACEVFORK)) { + error("ptrace failed: %s", strerror(errno)); + goto bailout; + } + + n = read(child_fd[0], buf, sizeof(buf)); + if (n != 0) { + if (n < 0) + error("read from child failed: %s", strerror(errno)); + /* + * Something happened in the child process. Read the + * message and kill the process. + * + * NUL-terminated string is expected. + */ + error("error from child: %s", buf); + goto bailout; + } else { + /* The write-side of the pipe is closed by exec() */ + if (clock_gettime(CLOCK_MONOTONIC, &start_timespec)) { + error("clock_gettime failed: %s", strerror(errno)); + goto bailout; + } + } + + while (true) { + struct epoll_event events[10], *ev; + int n; + + n = epoll_wait(epoll_fd, events, numberof(events), -1); + if (n < 0) { + error("epoll_wait failed: %s", strerror(errno)); + goto bailout; + } - if (close(stdout_fd[1]) || close(stderr_fd[1]) || close(child_fd[1])) - bug("close child write pipe failed"); - - int epoll_fd = epoll_create1(0); - if (epoll_fd < 0) - bug("epoll_create1 failed"); - - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, SIGCHLD); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGTERM); - sigaddset(&mask, SIGHUP); - sigprocmask(SIG_BLOCK, &mask, NULL); - int signal_fd = signalfd(-1, &mask, 0); - if (signal_fd < 0) - bug("signalfd failed"); - - int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); - if (timer_fd < 0) - bug("timerfd_create failed"); - if (timerfd_settime(timer_fd, 0, &(struct itimerspec) { .it_value.tv_sec = POE_TIME_LIMIT }, NULL)) - bug("timerfd_settime failed"); - -#define ADD(_fd__) do if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, _fd__, &(struct epoll_event) { .data.fd = _fd__, .events = EPOLLRDHUP|EPOLLIN })) \ - bug("EPOLL_CTL_ADD failed"); while (0) - ADD(signal_fd); - ADD(timer_fd); - ADD(child_fd[0]); - ADD(stdout_fd[0]); - ADD(stderr_fd[0]); - - if (ptrace(PTRACE_SEIZE, pid, NULL, - PTRACE_O_TRACECLONE | - PTRACE_O_TRACEFORK | - PTRACE_O_TRACESECCOMP | - PTRACE_O_TRACEVFORK)) - bug("ptrace failed"); - - if (poe_cgroup_add(pid)) - die("failed cgroup add"); - - while (true) { - struct epoll_event events[10]; - int n = epoll_wait(epoll_fd, events, sizeof(events) / sizeof(events[0]), -1); - if (n < 0) - bug("epoll_wait failed"); - - for (int i = 0; i < n; i++) { - struct epoll_event *ev = &events[i]; - if (ev->events & EPOLLIN) { + for (ev = events; ev < events + n; ev++) { + if (!(ev->events & EPOLLIN)) { + error("unknown event from epoll"); + goto bailout; + } if (ev->data.fd == stdout_fd[0]) { - handle_stdout(ev->data.fd, STDOUT_FILENO); + handle_stdout(ev->data.fd, 1); } else if (ev->data.fd == stderr_fd[0]) { - handle_stdout(ev->data.fd, STDERR_FILENO); + handle_stdout(ev->data.fd, 2); } else if (ev->data.fd == signal_fd) { - struct signalfd_siginfo si; - if (sizeof(si) != read(signal_fd, &si, sizeof(si))) - die("partial read signalfd"); - handle_signal(pid, &si); + handle_signalfd(pid, signal_fd); } else if (ev->data.fd == timer_fd) { finish(POE_TIMEDOUT, -1, NULL); - } else if (ev->data.fd == child_fd[0]) { - char buf[PIPE_BUF]; - ssize_t nx = read(child_fd[0], buf, sizeof(buf)); - if (nx > 0) // TODO - die("child err: %s", strndupa(buf, nx)); + } else { + error("event for unknown fd from epoll"); + goto bailout; } } - if (ev->events & EPOLLERR || ev->events & EPOLLHUP || ev->events & EPOLLRDHUP) { - // fd closed - close(ev->data.fd); - if (ev->data.fd == child_fd[0]) - // exec succeeded - if (clock_gettime(CLOCK_MONOTONIC, &start_timespec)) - bug("clock_gettime failed"); - } } + return 0; + +bailout: + (void)close(child_fd[0]); + /* Kill the child process(es) and report failure */ + kill(pid, SIGKILL); + while (waitpid(-1, NULL, __WALL) != -1); + return -1; + } else { + /* Close the read-size fds */ + CHECK(!close(stdout_fd[0])); + CHECK(!close(stderr_fd[0])); + CHECK(!close(child_fd[0])); + /* Replace stdout and stderr */ + CHECK(dup2(stdout_fd[1], 1) == 1); + CHECK(dup2(stderr_fd[1], 2) == 2); + /* Close the original write-side fds */ + CHECK(!close(stdout_fd[1])); + CHECK(!close(stderr_fd[1])); + + /* Setup environemnt and do exec() */ + poe_child_do(pg, child_fd[1]); } - bug("unreachable"); + UNREACHABLE(); +} + +static void print_usage(void) +{ + /* FIXME */ +} + +int main(int argc, char **argv) +{ + const char *basedir = NULL, *overlaydir = NULL, *tmpdir = NULL; + list(const char *) copyfiles = LIST_INIT; + char **commandline; + struct playground *pg = NULL; + struct cgroups *cg = NULL; + int ret = 1; + + while (1) { + int c, lindex; + static const struct option opts[] = { + { "help", no_argument, NULL, 'h' }, + { "basedir", required_argument, NULL, 'b' }, + { "overlaydir", required_argument, NULL, 'o' }, + { "tmpdir", required_argument, NULL, 't' }, + { "copy", required_argument, NULL, 'c' }, + { NULL, 0, NULL, 0 } + }; + + c = getopt_long(argc, argv, "hb:o:c:", opts, &lindex); + if (c == -1) + break; + switch (c) { + case 'h': + print_usage(); + return 0; + case 'b': + basedir = optarg; + break; + case 'o': + overlaydir = optarg; + break; + case 't': + tmpdir = optarg; + break; + case 'c': + list_append(©files, optarg); + break; + default: + print_usage(); + return 1; + } + } + + if (optind == argc) + die("commands not specified"); + commandline = argv + optind; + + if (!basedir) + die("option --basedir not specified"); + if (!tmpdir) + die("option --tmpdir not specified"); + + /* initialize playground */ + pg = poe_playground_init(basedir, overlaydir, tmpdir); + if (!pg) { + error("playground could not be setup"); + goto err; + } + if (poe_playground_copy_files(pg, copyfiles.len, copyfiles.ptr)) { + list_free0(©files); + error("files could not be copied to playground"); + goto err; + } + list_free0(©files); + + /* initialize cgroups */ + cg = poe_cgroups_init(tmpdir); + if (!cg) { + error("cgroups could not be setup"); + goto err; + } + + /* setup child process and limit privileges */ + if (do_spawn(pg, cg, commandline)) { + error("child process could not be started"); + goto err; + } + + ret = 0; + +err: + if (pg) + poe_playground_destroy(pg); + if (cg) + poe_cgroups_destroy(cg); + return ret; } diff --git a/sandbox/playground.c b/sandbox/playground.c index 60c1b25..f7da865 100644 --- a/sandbox/playground.c +++ b/sandbox/playground.c @@ -39,7 +39,8 @@ static void destroy_playground(unused int status, void *pg_) free(pg); } -struct playground *poe_playground_init(const char *base, const char *env) +struct playground *poe_playground_init(const char *base, const char *env, + const char *tmpdir) { struct playground *pg = xcalloc(1, sizeof(struct playground)); if (on_exit(destroy_playground, pg)) @@ -93,6 +94,17 @@ struct playground *poe_playground_init(const char *base, const char *env) return pg; } +int poe_playground_copy_files(const struct playground *pg, size_t len, + const char **filespecs) +{ + for (size_t i = 0; i < len; i++) { + } +} + +void poe_playground_destroy(struct playground *pg) +{ +} + int poe_playground_init_command_line(struct playground *pg, char **cmdl, const char *progpath) { int src = open(progpath, O_RDONLY); diff --git a/sandbox/sandbox.h b/sandbox/sandbox.h index c38aca8..0a5a31f 100644 --- a/sandbox/sandbox.h +++ b/sandbox/sandbox.h @@ -55,6 +55,10 @@ struct playground { char **command_line; }; +struct cgroups { + int a; +}; + #include "config.h" #include "utils.h" @@ -63,13 +67,17 @@ warn_unused int poe_seccomp_init(void); char *poe_seccomp_syscall_resolve(int); // cgroup.c -warn_unused int poe_cgroup_init(void); -warn_unused int poe_cgroup_add(pid_t); +struct cgroups *poe_cgroups_init(const char *tmpdir); +int poe_cgroups_add(struct cgroups *, pid_t); +void poe_cgroups_destroy(struct cgroups *); // playground.c -warn_unused struct playground *poe_playground_init(const char *, const char *); -warn_unused int poe_playground_init_command_line(struct playground *, char **, const char *); +struct playground *poe_playground_init(const char *basedir, + const char *overlaydir, + const char *tmpdir); +int poe_playground_copy_files(const struct playground *, size_t, const char **); +void poe_playground_destroy(struct playground *); -noreturn void poe_child_do(struct playground *, int[2], int[2], int[2]); +_Noreturn void poe_child_do(struct playground *, int); #endif diff --git a/sandbox/utils.h b/sandbox/utils.h index 3438967..89198e0 100644 --- a/sandbox/utils.h +++ b/sandbox/utils.h @@ -1,6 +1,8 @@ #ifndef UTILS_H #define UTILS_H +#define numberof(ary) (sizeof(ary) / sizeof(ary[0])) + // errors static inline int verror(const char *prefix, const char *fmt, va_list ap) { @@ -63,6 +65,15 @@ static inline void *xcalloc(size_t nmemb, size_t size) return _mem_nonnull(calloc(nmemb, size)); } +static inline void *xrealloc(void *ptr, size_t size) +{ + void *new = realloc(ptr, size); + + if (!new) + die("not enough memory"); + return new; +} + static inline char *xstrdup(const char *orig) { return _mem_nonnull(strdup(orig)); @@ -79,4 +90,62 @@ static inline char *xformat(const char *fmt, ...) return new; } +/* + * list(type): A variable-length array implementation. + */ + +#define list(type) struct { size_t len, capa; type *ptr; } + +#define LIST_INIT { 0, 0, NULL } + +#define list_grow(l) do { \ + (l)->capa = (l)->capa ? (l)->capa * 2 : 8; \ + (l)->ptr = xrealloc((l)->ptr, sizeof(*(l)->ptr) * (l)->capa); \ +} while (0) + +#define list_append(l, i) do { \ + if ((l)->capa <= (l)->len) \ + list_grow(l); \ + (l)->ptr[(l)->len++] = (i); \ +} while (0) + +#define list_fetch(l, i) ((l)->ptr[i]) + +#define list_for_each(l, it, in) \ + for (in = 0; in < (l)->len ? (it = list_fetch(l, in)) || 1 : 0; in++) + +#define list_free0(l) do { \ + free((l)->ptr); \ + memset(l, 0, sizeof(*l)); \ +} while (0) + +#define list_free(l, f) do { \ + if (f) \ + for (size_t i = 0; i < (l)->len; i++) \ + f((l)->ptr[i]); \ + list_free0(l); \ +} while (0) + +/* + * CHECK(), CHECK_MSG() and UNREACHABLE(): Run-time assertion macros + */ +static inline void IR(int x) +{ + /* Suppress -Wunused-result */ + (void)x; +} +#define CHECK_MSG(x, m) do { \ + if (!(x)) { \ + /* Hopefully no errors occur... */ \ + char _b[512] = { 0 }; \ + int _p = (int)syscall(__NR_getpid); \ + IR(snprintf(_b, sizeof(_b), "[PID:%d] %s:%d (%s): %s\n",\ + _p, __FILE__, __LINE__, __func__, m)); \ + IR(write(2, _b, strlen(_b))); \ + abort(); \ + } \ +} while (0) +#define CHECK(x) CHECK_MSG(x, #x) +#define UNREACHABLE() CHECK_MSG(0, "unreachable") + #endif -- cgit v1.2.3