From af143089e1e226f5c842bd522c8683a042b14e98 Mon Sep 17 00:00:00 2001 From: Quantum Date: Tue, 7 Sep 2021 01:45:57 -0400 Subject: [PATCH] cptbox: implement seccomp errno generation Fixes #793 --- dmoj/cptbox/_cptbox.pyx | 16 ++++++++++++++-- dmoj/cptbox/helper.cpp | 8 +++++++- dmoj/cptbox/helper.h | 1 + dmoj/cptbox/tracer.py | 14 +++++++++++++- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/dmoj/cptbox/_cptbox.pyx b/dmoj/cptbox/_cptbox.pyx index cf36c9136..616042e36 100644 --- a/dmoj/cptbox/_cptbox.pyx +++ b/dmoj/cptbox/_cptbox.pyx @@ -121,6 +121,7 @@ cdef extern from 'helper.h' nogil: bool use_seccomp int abi_for_seccomp bint *seccomp_whitelist + int *seccomp_errnolist void cptbox_closefrom(int lowfd) int cptbox_child_run(child_config *) @@ -475,13 +476,17 @@ cdef class Process: pass cpdef _get_seccomp_whitelist(self): - raise NotImplementedError() + return [False] * MAX_SYSCALL + + cpdef _get_seccomp_errnolist(self): + return [0] * MAX_SYSCALL cpdef _spawn(self, file, args, env=(), chdir=''): cdef child_config config config.argv = NULL config.envp = NULL config.seccomp_whitelist = NULL + config.seccomp_errnolist = NULL try: config.address_space = self._child_address @@ -501,12 +506,18 @@ cdef class Process: config.use_seccomp = self._use_seccomp() if config.use_seccomp: whitelist = self._get_seccomp_whitelist() + errnolist = self._get_seccomp_errnolist() assert len(whitelist) == MAX_SYSCALL + assert len(errnolist) == MAX_SYSCALL + config.seccomp_whitelist = malloc(sizeof(bint) * MAX_SYSCALL) - if not config.seccomp_whitelist: + config.seccomp_errnolist = malloc(sizeof(int) * MAX_SYSCALL) + if not config.seccomp_whitelist or not config.seccomp_errnolist: PyErr_NoMemory() + for i in range(MAX_SYSCALL): config.seccomp_whitelist[i] = whitelist[i] + config.seccomp_errnolist[i] = errnolist[i] if self.process.spawn(pt_child, &config): raise RuntimeError('failed to spawn child') @@ -514,6 +525,7 @@ cdef class Process: free(config.argv) free(config.envp) free(config.seccomp_whitelist) + free(config.seccomp_errnolist) cpdef _monitor(self): cdef int exitcode diff --git a/dmoj/cptbox/helper.cpp b/dmoj/cptbox/helper.cpp index 9e660cafa..34c72f602 100644 --- a/dmoj/cptbox/helper.cpp +++ b/dmoj/cptbox/helper.cpp @@ -95,7 +95,13 @@ int cptbox_child_run(const struct child_config *config) { for (int syscall = 0; syscall < MAX_SYSCALL; syscall++) { if (config->seccomp_whitelist[syscall]) { if ((rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscall, 0))) { - fprintf(stderr, "seccomp_rule_add(..., %d): %s\n", syscall, strerror(-rc)); + fprintf(stderr, "seccomp_rule_add(..., SCMP_ACT_ALLOW, %d): %s\n", syscall, strerror(-rc)); + // This failure is not fatal, it'll just cause the syscall to trap anyway. + } + } else if (config->seccomp_errnolist[syscall]) { + if ((rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(config->seccomp_errnolist[syscall]), syscall, 0))) { + fprintf(stderr, "seccomp_rule_add(..., SCMP_ACT_ERRNO(%d), %d): %s\n", + config->seccomp_errnolist[syscall], syscall, strerror(-rc)); // This failure is not fatal, it'll just cause the syscall to trap anyway. } } diff --git a/dmoj/cptbox/helper.h b/dmoj/cptbox/helper.h index ff6219ff0..28aa77732 100644 --- a/dmoj/cptbox/helper.h +++ b/dmoj/cptbox/helper.h @@ -23,6 +23,7 @@ struct child_config { int stderr_; bool use_seccomp; int *seccomp_whitelist; + int *seccomp_errnolist; }; void cptbox_closefrom(int lowfd); diff --git a/dmoj/cptbox/tracer.py b/dmoj/cptbox/tracer.py index 257c81479..dccfaf4d5 100644 --- a/dmoj/cptbox/tracer.py +++ b/dmoj/cptbox/tracer.py @@ -9,7 +9,7 @@ from typing import Callable, List, Optional from dmoj.cptbox._cptbox import * -from dmoj.cptbox.handlers import ALLOW, DISALLOW, _CALLBACK +from dmoj.cptbox.handlers import ALLOW, DISALLOW, ErrnoHandlerCallback, _CALLBACK from dmoj.cptbox.syscalls import SYSCALL_COUNT, by_id, sys_exit, sys_exit_group, sys_getpid, translator from dmoj.utils.communicate import safe_communicate as _safe_communicate from dmoj.utils.os_ext import OOM_SCORE_ADJ_MAX, oom_score_adj @@ -184,6 +184,18 @@ def _get_seccomp_whitelist(self): whitelist[call] = handler == ALLOW return whitelist + def _get_seccomp_errnolist(self): + errnolist = [0] * MAX_SYSCALL_NUMBER + index = _SYSCALL_INDICIES[NATIVE_ABI] + for i in range(SYSCALL_COUNT): + handler = self._security.get(i, DISALLOW) + for call in translator[i][index]: + if call is None: + continue + if isinstance(handler, ErrnoHandlerCallback): + errnolist[call] = handler.errno + return errnolist + def wait(self): self._died.wait() if not self.was_initialized: