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 7, 2021
1 parent 1131feb commit af14308
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 4 deletions.
16 changes: 14 additions & 2 deletions dmoj/cptbox/_cptbox.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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 *)
Expand Down Expand Up @@ -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
Expand All @@ -501,19 +506,26 @@ 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 = <bint*>malloc(sizeof(bint) * MAX_SYSCALL)
if not config.seccomp_whitelist:
config.seccomp_errnolist = <int*>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')
finally:
free(config.argv)
free(config.envp)
free(config.seccomp_whitelist)
free(config.seccomp_errnolist)

cpdef _monitor(self):
cdef int exitcode
Expand Down
8 changes: 7 additions & 1 deletion dmoj/cptbox/helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
}
}
Expand Down
1 change: 1 addition & 0 deletions dmoj/cptbox/helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ struct child_config {
int stderr_;
bool use_seccomp;
int *seccomp_whitelist;
int *seccomp_errnolist;
};

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 af14308

Please sign in to comment.