aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKazuki Yamaguchi <k@rhe.jp>2017-06-01 00:00:07 +0900
committerKazuki Yamaguchi <k@rhe.jp>2017-06-01 00:00:07 +0900
commitb13627a889e6ba2f6e9c136b6d0eb08ac0646658 (patch)
tree3642cc69611aa7f285cb70d0914162ac2cdfb622
parent62a1e1f1a4ece6808a1fb63c15fdfca127d958ab (diff)
downloadpoe-b13627a889e6ba2f6e9c136b6d0eb08ac0646658.tar.gz
wip
-rw-r--r--sandbox/Makefile16
-rw-r--r--sandbox/README.md12
-rw-r--r--sandbox/cgroup.c47
-rw-r--r--sandbox/child.c18
-rw-r--r--sandbox/main.c438
-rw-r--r--sandbox/playground.c14
-rw-r--r--sandbox/sandbox.h18
-rw-r--r--sandbox/utils.h69
8 files changed, 455 insertions, 177 deletions
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 <getopt.h>
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(&copyfiles, 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(&copyfiles);
+ error("files could not be copied to playground");
+ goto err;
+ }
+ list_free0(&copyfiles);
+
+ /* 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