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 7300f4d
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 19 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
1 change: 1 addition & 0 deletions dmoj/cptbox/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def __call__(self, debugger: Debugger) -> bool:
def on_return():
debugger.errno = self.errno

print(debugger.syscall, self.error_name)
debugger.syscall = -1
debugger.on_return(on_return)
return True
Expand Down
11 changes: 9 additions & 2 deletions dmoj/cptbox/helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,16 @@ int cptbox_child_run(const struct child_config *config) {
}

for (int syscall = 0; syscall < MAX_SYSCALL; syscall++) {
if (config->seccomp_whitelist[syscall]) {
int handler = config->seccomp_handlers[syscall];
if (handler == 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 (handler > 0) {
if ((rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(handler), syscall, 0))) {
fprintf(stderr, "seccomp_rule_add(..., SCMP_ACT_ERRNO(%d), %d): %s\n",
handler, 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: 8 additions & 6 deletions 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 @@ -168,8 +168,8 @@ def __init__(
if self._spawn_error:
raise self._spawn_error

def _get_seccomp_whitelist(self):
whitelist = [False] * MAX_SYSCALL_NUMBER
def _get_seccomp_handlers(self):
handlers = [-1] * MAX_SYSCALL_NUMBER
index = _SYSCALL_INDICIES[NATIVE_ABI]
for i in range(SYSCALL_COUNT):
# Ensure at least one syscall traps.
Expand All @@ -180,9 +180,11 @@ def _get_seccomp_whitelist(self):
for call in translator[i][index]:
if call is None:
continue
if isinstance(handler, int):
whitelist[call] = handler == ALLOW
return whitelist
if isinstance(handler, int) and handler == ALLOW:
handlers[call] = 0
elif isinstance(handler, ErrnoHandlerCallback):
handlers[call] = handler.errno
return handlers

def wait(self):
self._died.wait()
Expand Down

0 comments on commit 7300f4d

Please sign in to comment.