Skip to content

Commit

Permalink
cptbox: implement seccomp errno generation
Browse files Browse the repository at this point in the history
Fixes #793
  • Loading branch information
quantum5 committed Sep 8, 2021
1 parent 918790e commit 139897d
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 14 deletions.
22 changes: 12 additions & 10 deletions dmoj/cptbox/_cptbox.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ cdef extern from 'helper.h' nogil:
int stderr_
bool use_seccomp
int abi_for_seccomp
bint *seccomp_whitelist
int *seccomp_handlers

void cptbox_closefrom(int lowfd)
int cptbox_child_run(child_config *)
Expand Down Expand Up @@ -474,14 +474,14 @@ cdef class Process:
cpdef _cpu_time_exceeded(self):
pass

cpdef _get_seccomp_whitelist(self):
raise NotImplementedError()
cpdef _get_seccomp_handlers(self):
return [-1] * 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_handlers = NULL

try:
config.address_space = self._child_address
Expand All @@ -500,20 +500,22 @@ cdef class Process:

config.use_seccomp = self._use_seccomp()
if config.use_seccomp:
whitelist = self._get_seccomp_whitelist()
assert len(whitelist) == MAX_SYSCALL
config.seccomp_whitelist = <bint*>malloc(sizeof(bint) * MAX_SYSCALL)
if not config.seccomp_whitelist:
handlers = self._get_seccomp_handlers()
assert len(handlers) == MAX_SYSCALL

config.seccomp_handlers = <int*>malloc(sizeof(int) * MAX_SYSCALL)
if not config.seccomp_handlers:
PyErr_NoMemory()

for i in range(MAX_SYSCALL):
config.seccomp_whitelist[i] = whitelist[i]
config.seccomp_handlers[i] = handlers[i]

if self.process.spawn(pt_child, &config):
raise RuntimeError('failed to spawn child')
finally:
free(config.argv)
free(config.envp)
free(config.seccomp_whitelist)
free(config.seccomp_handlers)

cpdef _monitor(self):
cdef int exitcode
Expand Down
10 changes: 8 additions & 2 deletions dmoj/cptbox/helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,15 @@ int cptbox_child_run(const struct child_config *config) {
}

for (int syscall = 0; syscall < MAX_SYSCALL; syscall++) {
if (config->seccomp_whitelist[syscall]) {
if (config->seccomp_handlers[syscall] == 0) {
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_handlers[syscall] > 0) {
if ((rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(config->seccomp_handlers[syscall]), syscall, 0))) {
fprintf(stderr, "seccomp_rule_add(..., SCMP_ACT_ERRNO(%d), %d): %s\n",
config->seccomp_handlers[syscall], syscall, strerror(-rc));
// This failure is not fatal, it'll just cause the syscall to trap anyway.
}
}
Expand Down
2 changes: 1 addition & 1 deletion dmoj/cptbox/helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ struct child_config {
int stdout_;
int stderr_;
bool use_seccomp;
int *seccomp_whitelist;
int *seccomp_handlers;
};

void cptbox_closefrom(int lowfd);
Expand Down
14 changes: 13 additions & 1 deletion dmoj/cptbox/tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit 139897d

Please sign in to comment.