Ejemplo n.º 1
0
 def get_fs(self) -> List[FilesystemAccessRule]:
     home = self.runtime_dict.get('%s_home' %
                                  self.get_executor_name().lower())
     fs = super().get_fs() + [ExactFile(self._code)]
     if home is not None:
         fs += [RecursiveDir(home)]
     return fs
Ejemplo n.º 2
0
class Executor(NullStdoutMixin, CompiledExecutor):
    ext = 'hs'
    command = 'ghc'
    compiler_read_fs = [
        RecursiveDir('/proc/self/task'),
        RecursiveDir('/var/lib/ghc'),
    ]

    test_program = """\
main = do
    a <- getContents
    putStr a
"""

    def get_compile_args(self):
        return [self.get_command(), '-O2', '-o', self.problem, self._code]
Ejemplo n.º 3
0
class Executor(StripCarriageReturnsMixin, CompiledExecutor):
    ext = 'zig'
    command = 'zig'
    compiler_time_limit = 30
    compiler_read_fs = [
        RecursiveDir('~/.cache'),
    ]
    compiler_write_fs = compiler_read_fs
    compiler_required_dirs = ['~/.cache']
    test_program = """
const std = @import("std");

pub fn main() !void {
    const io = std.io;
    const stdin = std.io.getStdIn().inStream();
    const stdout = std.io.getStdOut().outStream();

    var line_buf: [50]u8 = undefined;
    while (try stdin.readUntilDelimiterOrEof(&line_buf, '\n')) |line| {
        if (line.len == 0) break;
        try stdout.print("{}", .{line});
    }
}"""

    def get_compile_args(self):
        return [
            self.get_command(), 'build-exe', self._code, '--release-safe',
            '--name', self.problem
        ]

    @classmethod
    def get_version_flags(cls, command):
        return ['version']
Ejemplo n.º 4
0
class Executor(ScriptExecutor):
    ext = 'pl'
    command = 'perl'
    fs = [RecursiveDir('/etc/perl')]
    test_program = 'print<>'
    syscalls = ['umtx_op']

    def get_cmdline(self, **kwargs):
        return ['perl', '-Mre=eval', self._code]
Ejemplo n.º 5
0
class Executor(CompiledExecutor):
    ext = 'rkt'
    name = 'RKT'
    fs = [RecursiveDir('/etc/racket'), ExactFile('/etc/passwd')]
    compiler_read_fs = [
        RecursiveDir('/etc/racket'),
        RecursiveDir('~/.local/share/racket'),
    ]

    command = 'racket'

    syscalls = ['epoll_create', 'epoll_create1', 'epoll_wait', 'epoll_pwait']
    # Racket SIGABRTs under low-memory conditions before actually crossing the memory limit,
    # so give it a bit of headroom to be properly marked as MLE.
    data_grace = 4096
    address_grace = 131072

    test_program = """\
#lang racket
(displayln (read-line))
"""

    def get_compile_args(self):
        return [self.runtime_dict['raco'], 'make', self._code]

    def get_cmdline(self, **kwargs):
        return [self.get_command(), self._code]

    def get_executable(self):
        return self.get_command()

    @classmethod
    def initialize(cls):
        if 'raco' not in cls.runtime_dict:
            return False
        return super().initialize()

    @classmethod
    def get_versionable_commands(cls):
        return [('racket', cls.get_command())]

    @classmethod
    def get_find_first_mapping(cls):
        return {'racket': ['racket'], 'raco': ['raco']}
Ejemplo n.º 6
0
 def get_fs(self) -> List[FilesystemAccessRule]:
     fs = super().get_fs()
     if self.use_qemu:
         assert self._executable is not None
         fs += [
             ExactFile('/proc/sys/vm/mmap_min_addr'),
             RecursiveDir('/etc/qemu-binfmt'),
             ExactFile(self._executable),
         ]
     return fs
Ejemplo n.º 7
0
    def test_recursive_dir(self):
        self.fs = FilesystemPolicy([RecursiveDir('/usr')])

        self.checkTrue('/usr')
        self.checkTrue('/usr/lib')
        self.checkTrue('/usr/lib/a/b/c/d/e')

        self.checkFalse('/')
        self.checkFalse('/etc')
        self.checkFalse('/us')
        self.checkFalse('/usr2')
Ejemplo n.º 8
0
 def get_fs(self):
     fs = super().get_fs()
     home = self.runtime_dict.get('%s_home' %
                                  self.get_executor_name().lower())
     if home is not None:
         fs.append(RecursiveDir(home))
         components = home.split('/')
         components.pop()
         while components and components[-1]:
             fs.append(ExactDir('/'.join(components)))
             components.pop()
     return fs
Ejemplo n.º 9
0
class Executor(CompiledExecutor):
    ext = 'swift'
    command = 'swiftc'
    compiler_read_fs = [
        RecursiveDir('~/.cache'),
    ]
    compiler_write_fs = compiler_read_fs
    compiler_required_dirs = ['~/.cache']
    test_program = 'print(readLine()!)'

    def get_compile_args(self):
        return [self.get_command(), self._code]
Ejemplo n.º 10
0
 def get_fs(self) -> List[FilesystemAccessRule]:
     fs = (super().get_fs() + [ExactFile(self._agent_file)] + [
         ExactDir(str(parent))
         for parent in PurePath(self._agent_file).parents
     ])
     vm = self.get_vm()
     assert vm is not None
     vm_parent = Path(os.path.realpath(vm)).parent.parent
     vm_config = Path(
         glob.glob(f'{vm_parent}/**/jvm.cfg', recursive=True)[0])
     if vm_config.is_symlink():
         fs += [RecursiveDir(os.path.dirname(os.path.realpath(vm_config)))]
     return fs
Ejemplo n.º 11
0
class Executor(CompiledExecutor):
    ext = 'rs'
    command = 'cargo'
    test_program = HELLO_WORLD_PROGRAM
    compiler_time_limit = 20
    compiler_read_fs = [
        RecursiveDir('/home'),
        ExactFile('/etc/resolv.conf'),
    ]
    compiler_write_fs = [
        RecursiveDir('~/.cargo'),
    ]

    def create_files(self, problem_id, source_code, *args, **kwargs):
        os.mkdir(self._file('src'))
        with open(self._file('src', 'main.rs'), 'wb') as f:
            f.write(source_code)

        with open(self._file('Cargo.toml'), 'wb') as f:
            f.write(CARGO_TOML)

        with open(self._file('Cargo.lock'), 'wb') as f:
            f.write(CARGO_LOCK)

    @classmethod
    def get_versionable_commands(cls):
        return [('rustc',
                 os.path.join(os.path.dirname(cls.get_command()), 'rustc'))]

    def get_compile_args(self):
        args = [self.get_command(), 'build', '--release']
        if bool_env('DMOJ_CARGO_OFFLINE'):
            args += ['--offline']
        return args

    def get_compiled_file(self):
        return self._file('target', 'release', 'user_submission')
Ejemplo n.º 12
0
class Executor(CompiledExecutor):
    ext = 'ml'
    name = 'OCAML'
    command = 'ocamlfind'
    compiler_read_fs = [
        RecursiveDir('~/.opam'),
    ]
    test_program = """
open! Base
open! Core
open! Stdio

let () = (In_channel.iter_lines Stdio.stdin ~f:print_endline)
"""

    def get_compile_args(self):
        # fmt: off
        return [
            self.runtime_dict['ocamlfind'],
            'opt',
            '-package',
            'str',
            '-package',
            'base',
            '-package',
            'core',
            '-package',
            'stdio',
            '-package',
            'zarith',
            '-thread',
            '-linkpkg',
            self._code,
            '-o',
            self.problem,
        ]
        # fmt: on

    @classmethod
    def get_version_flags(cls, command):
        return [('opt', '-version')]

    @classmethod
    def get_versionable_commands(cls):
        return [('ocaml', cls.get_command())]
Ejemplo n.º 13
0
class Executor(CompiledExecutor):
    ext = 'scm'
    name = 'SCM'
    command = 'chicken-csc'
    command_paths = ['chicken-csc', 'csc']
    compiler_read_fs = [
        RecursiveDir('/var/lib/chicken'),
    ]
    test_program = '(import chicken.io) (map print (read-lines))'

    def get_compile_args(self):
        return [self.get_command(), self._code]

    @classmethod
    def get_versionable_commands(cls):
        return (('csc', cls.get_command()), )

    @classmethod
    def get_version_flags(cls, command):
        return ['-version']
Ejemplo n.º 14
0
class Executor(CompiledExecutor):
    ext = 'dart'
    name = 'DART'
    nproc = -1  # Dart uses a really, really large number of threads
    command = 'dart'
    compiler_read_fs = [
        # Dart shells out...
        ExactFile('/bin/sh'),
        RecursiveDir('/proc/self/fd'),
    ]
    test_program = """
void main() {
    print("echo: Hello, World!");
}
"""
    address_grace = 128 * 1024

    syscalls = [
        'epoll_create',
        'epoll_create1',
        'epoll_ctl',
        'epoll_wait',
        'epoll_pwait',
        'timerfd_settime',
        'memfd_create',
        'ftruncate',
    ]

    def get_compile_args(self):
        return [
            self.get_command(),
            '--snapshot=%s' % self.get_compiled_file(), self._code
        ]

    def get_cmdline(self, **kwargs):
        return [self.get_command(), self.get_compiled_file()]

    def get_executable(self):
        return self.get_command()
Ejemplo n.º 15
0
class Executor(CompiledExecutor):
    ext = 'zig'
    name = 'ZIG'
    command = 'zig'
    compiler_read_fs = [
        RecursiveDir('~/.cache'),
    ]
    compiler_write_fs = compiler_read_fs
    test_program = """
const std = @import("std");

pub fn main() !void {
    const io = std.io;
    const stdin = std.io.getStdIn().inStream();
    const stdout = std.io.getStdOut().outStream();

    var line_buf: [50]u8 = undefined;
    while (try stdin.readUntilDelimiterOrEof(&line_buf, '\n')) |line| {
        if (line.len == 0) break;
        try stdout.print("{}", .{line});
    }
}"""

    def create_files(self, problem_id, source_code, *args, **kwargs):
        # This cleanup needs to happen because Zig refuses to compile carriage returns.
        # See <https://github.com/ziglang/zig/issues/544>.
        source_code = source_code.replace(b'\r\n', b'\r').replace(b'\r', b'\n')
        super().create_files(problem_id, source_code, *args, **kwargs)

    def get_compile_args(self):
        return [
            self.get_command(), 'build-exe', self._code, '--release-safe',
            '--name', self.problem
        ]

    @classmethod
    def get_version_flags(cls, command):
        return ['version']
Ejemplo n.º 16
0
class Executor(JavaExecutor):
    ext = 'scala'

    compiler = 'scalac'
    compiler_time_limit = 20
    compiler_read_fs = [
        ExactFile('/bin/uname'),
        ExactFile('/bin/readlink'),
        ExactFile('/bin/grep'),
        ExactFile('/bin/stty'),
        ExactFile('/bin/bash'),
        RecursiveDir('/etc/alternatives'),
    ]
    vm = 'scala_vm'

    test_program = """\
object self_test extends App {
     println("echo: Hello, World!")
}
"""

    def create_files(self, problem_id, source_code, *args, **kwargs):
        super().create_files(problem_id, source_code, *args, **kwargs)
        self._class_name = problem_id

    def get_cmdline(self, **kwargs):
        res = super().get_cmdline(**kwargs)

        # Simply run bash -x $(which scala) and copy all arguments after -Xmx and -Xms
        # and add it as a list in the configuration.
        res[-2:-1] = self.runtime_dict['scala_args']
        return res

    def get_compile_args(self):
        return [self.get_compiler(), self._code]

    @classmethod
    def get_versionable_commands(cls):
        return [('scalac', cls.get_compiler()), ('java', cls.get_vm())]

    @classmethod
    def autoconfig(cls):
        result = {}

        for key, files in {'scalac': ['scalac'], 'scala': ['scala']}.items():
            file = cls.find_command_from_list(files)
            if file is None:
                return result, False, 'Failed to find "%s"' % key
            result[key] = file

        scala = result.pop('scala')
        with open(os.devnull, 'w') as devnull:
            process = subprocess.Popen(
                ['bash', '-x', scala, '-usebootcp', '-version'],
                stdout=devnull,
                stderr=subprocess.PIPE)
        output = utf8text(process.communicate()[1])
        log = [
            i for i in output.split('\n')
            if 'scala.tools.nsc.MainGenericRunner' in i
        ]

        if not log:
            return result, False, 'Failed to parse: %s' % scala

        cmdline = log[-1].lstrip('+ ').split()
        result['scala_vm'] = cls.unravel_java(
            cls.find_command_from_list([cmdline[0]]))
        result['scala_args'] = [
            i for i in cmdline[1:-1] if not i.startswith(('-Xmx', '-Xms'))
        ]

        data = cls.autoconfig_run_test(result)
        if data[1]:
            data = data[:2] + ('Using %s' % scala, ) + data[3:]
        return data
Ejemplo n.º 17
0
from dmoj.cptbox import IsolateTracer, TracedPopen, syscalls
from dmoj.cptbox.filesystem_policies import ExactDir, ExactFile, FilesystemAccessRule, RecursiveDir
from dmoj.cptbox.handlers import ALLOW
from dmoj.error import InternalError
from dmoj.judgeenv import env, skip_self_test
from dmoj.result import Result
from dmoj.utils import setbufsize_path
from dmoj.utils.ansi import print_ansi
from dmoj.utils.error import print_protection_fault
from dmoj.utils.unicode import utf8bytes, utf8text

version_cache: Dict[str, List[Tuple[str, Tuple[int, ...]]]] = {}

if os.path.isdir('/usr/home'):
    USR_DIR = [
        RecursiveDir(f'/usr/{d}') for d in os.listdir('/usr')
        if d != 'home' and os.path.isdir(f'/usr/{d}')
    ]
else:
    USR_DIR = [RecursiveDir('/usr')]

BASE_FILESYSTEM: List[FilesystemAccessRule] = [
    ExactFile('/dev/null'),
    ExactFile('/dev/tty'),
    ExactFile('/dev/zero'),
    ExactFile('/dev/urandom'),
    ExactFile('/dev/random'),
    *USR_DIR,
    RecursiveDir('/lib'),
    RecursiveDir('/lib32'),
    RecursiveDir('/lib64'),
Ejemplo n.º 18
0
 def get_fs(self) -> List[FilesystemAccessRule]:
     assert self._dir is not None
     return BASE_FILESYSTEM + self.fs + self._load_extra_fs() + [
         RecursiveDir(self._dir)
     ]
Ejemplo n.º 19
0
 def test_rule_type_check(self):
     self.assertRaises(AssertionError, FilesystemPolicy, [ExactFile('/usr/lib')])
     self.assertRaises(AssertionError, FilesystemPolicy, [ExactDir('/etc/passwd')])
     self.assertRaises(AssertionError, FilesystemPolicy, [RecursiveDir('/etc/passwd')])
Ejemplo n.º 20
0
 def test_build_checks(self):
     self.assertRaises(AssertionError, FilesystemPolicy, [ExactFile('not/an/absolute/path')])
     self.assertRaises(AssertionError, FilesystemPolicy, [ExactDir('/nota/./normalized/path')])
     self.assertRaises(AssertionError, FilesystemPolicy, [RecursiveDir('')])
Ejemplo n.º 21
0
def compile_with_auxiliary_files(
    filenames: Sequence[str],
    flags: List[str] = [],
    lang: Optional[str] = None,
    compiler_time_limit: Optional[int] = None,
    unbuffered: bool = False,
) -> 'BaseExecutor':
    from dmoj.executors import executors
    from dmoj.executors.compiled_executor import CompiledExecutor

    sources = {}

    for filename in filenames:
        with open(filename, 'rb') as f:
            sources[os.path.basename(filename)] = f.read()

    def find_runtime(languages):
        for grader in languages:
            if grader in executors:
                return grader
        return None

    use_cpp = any(
        map(lambda name: os.path.splitext(name)[1] in ['.cpp', '.cc'],
            filenames))
    use_c = any(
        map(lambda name: os.path.splitext(name)[1] in ['.c'], filenames))
    if lang is None:
        best_choices = ('CPP20', 'CPP17', 'CPP14', 'CPP11',
                        'CPP03') if use_cpp else ('C11', 'C')
        lang = find_runtime(best_choices)

    executor = executors.get(lang)
    if not executor:
        raise IOError('could not find an appropriate C++ executor')

    executor = executor.Executor

    kwargs = {'fs': executor.fs + [RecursiveDir(tempfile.gettempdir())]}

    if issubclass(executor, CompiledExecutor):
        kwargs['compiler_time_limit'] = compiler_time_limit

    if hasattr(executor, 'flags'):
        kwargs['flags'] = flags + list(executor.flags)

    # Optimize the common case.
    if use_cpp or use_c:
        # Some auxiliary files (like those using testlib.h) take an extremely long time to compile, so we cache them.
        executor = executor('_aux_file',
                            None,
                            aux_sources=sources,
                            cached=True,
                            unbuffered=unbuffered,
                            **kwargs)
    else:
        if len(sources) > 1:
            raise InternalError(
                'non-C/C++ auxilary programs cannot be multi-file')
        executor = executor('_aux_file',
                            list(sources.values())[0],
                            cached=True,
                            unbuffered=unbuffered,
                            **kwargs)

    return executor
Ejemplo n.º 22
0
    def __init__(self, *, tmpdir, read_fs, write_fs):
        read_fs += BASE_FILESYSTEM + [
            RecursiveDir(tmpdir),
            ExactFile('/bin/strip'),
            RecursiveDir('/usr/x86_64-linux-gnu'),
        ]
        write_fs += BASE_WRITE_FILESYSTEM + [RecursiveDir(tmpdir)]
        super().__init__(read_fs=read_fs, write_fs=write_fs)

        self.update({
            # Process spawning system calls
            sys_fork:
            ALLOW,
            sys_vfork:
            ALLOW,
            sys_execve:
            ALLOW,
            sys_getcpu:
            ALLOW,
            sys_getpgid:
            ALLOW,
            # Directory system calls
            sys_mkdir:
            self.handle_file_access(FilesystemSyscallKind.WRITE, file_reg=0),
            sys_mkdirat:
            self.handle_file_access_at(FilesystemSyscallKind.WRITE,
                                       dir_reg=0,
                                       file_reg=1),
            sys_rmdir:
            self.handle_file_access(FilesystemSyscallKind.WRITE, file_reg=0),
            # Linking system calls
            sys_link:
            self.handle_file_access(FilesystemSyscallKind.WRITE, file_reg=1),
            sys_linkat:
            self.handle_file_access_at(FilesystemSyscallKind.WRITE,
                                       dir_reg=2,
                                       file_reg=3),
            sys_unlink:
            self.handle_file_access(FilesystemSyscallKind.WRITE, file_reg=0),
            sys_unlinkat:
            self.handle_file_access_at(FilesystemSyscallKind.WRITE,
                                       dir_reg=0,
                                       file_reg=1),
            sys_symlink:
            self.handle_file_access(FilesystemSyscallKind.WRITE, file_reg=1),
            # Miscellaneous other filesystem system calls
            sys_chdir:
            self.handle_file_access(FilesystemSyscallKind.READ, file_reg=0),
            sys_chmod:
            self.handle_file_access(FilesystemSyscallKind.WRITE, file_reg=0),
            sys_utimensat:
            self.do_utimensat,
            sys_umask:
            ALLOW,
            sys_flock:
            ALLOW,
            sys_fsync:
            ALLOW,
            sys_fadvise64:
            ALLOW,
            sys_fchmodat:
            self.handle_file_access_at(FilesystemSyscallKind.WRITE,
                                       dir_reg=0,
                                       file_reg=1),
            sys_fchmod:
            self.handle_fchmod,
            sys_fallocate:
            ALLOW,
            sys_ftruncate:
            ALLOW,
            sys_rename:
            self.handle_rename,
            sys_renameat:
            self.handle_renameat,
            # I/O system calls
            sys_readv:
            ALLOW,
            sys_pwrite64:
            ALLOW,
            sys_sendfile:
            ALLOW,
            # Event loop system calls
            sys_epoll_create:
            ALLOW,
            sys_epoll_create1:
            ALLOW,
            sys_epoll_ctl:
            ALLOW,
            sys_epoll_wait:
            ALLOW,
            sys_epoll_pwait:
            ALLOW,
            sys_timerfd_settime:
            ALLOW,
            sys_eventfd2:
            ALLOW,
            sys_waitid:
            ALLOW,
            sys_wait4:
            ALLOW,
            # Network system calls, we don't sandbox these
            sys_socket:
            ALLOW,
            sys_socketpair:
            ALLOW,
            sys_connect:
            ALLOW,
            sys_setsockopt:
            ALLOW,
            sys_getsockname:
            ALLOW,
            sys_sendmmsg:
            ALLOW,
            sys_recvfrom:
            ALLOW,
            sys_sendto:
            ALLOW,
            # Miscellaneous other system calls
            sys_msync:
            ALLOW,
            sys_clock_nanosleep:
            ALLOW,
            sys_memfd_create:
            ALLOW,
            sys_rt_sigsuspend:
            ALLOW,
        })

        # FreeBSD-specific syscalls
        if 'freebsd' in sys.platform:
            self.update({
                sys_rfork:
                ALLOW,
                sys_procctl:
                ALLOW,
                sys_cap_rights_limit:
                ALLOW,
                sys_posix_fadvise:
                ALLOW,
                sys_posix_fallocate:
                ALLOW,
                sys_setrlimit:
                ALLOW,
                sys_cap_ioctls_limit:
                ALLOW,
                sys_cap_fcntls_limit:
                ALLOW,
                sys_cap_enter:
                ALLOW,
                sys_utimes:
                self.handle_file_access(FilesystemSyscallKind.WRITE,
                                        file_reg=0),
            })
Ejemplo n.º 23
0
class MonoExecutor(CompiledExecutor):
    name = 'MONO'
    nproc = -1
    address_grace = 262144
    # Give Mono access to 64mb more data segment memory. This is a hack, for
    # dealing with the fact that Mono behaves extremely poorly when handling
    # out-of-memory situations -- in many cases, it dumps an assertion to
    # standard error, then *exits with a 0 exit code*. Unfortunately, it will
    # only infrequently throw a System.OutOfMemoryException.
    #
    # The hope here is that if a problem allows X mb of memory and we cap at
    # X+64 mb, if an OOM situation occurs, it will occur at a point where
    # VmHWM >= X. Then, even if Mono exits poorly, the submission will still
    # get flagged as MLE.
    data_grace = 65536
    cptbox_popen_class = MonoTracedPopen
    fs = [RecursiveDir('/etc/mono')]
    compiler_read_fs = fs
    # Mono sometimes forks during its crashdump procedure, but continues even if
    # the call to fork fails.
    syscalls = [
        'wait4',
        'rt_sigsuspend',
        'msync',
        'fadvise64',
        'clock_nanosleep',
        ('fork', ACCESS_EAGAIN),
    ]

    def get_env(self) -> Dict[str, str]:
        env = super().get_env()
        # Disable Mono's usage of /dev/shm, so we don't have to deal with
        # its extremely messy access patterns to it.
        env['MONO_DISABLE_SHARED_AREA'] = '1'
        # Disable Mono's generation of core dump files (e.g. on MLE).
        env['MONO_CRASH_NOFILE'] = '1'
        return env

    def get_compiled_file(self) -> str:
        return self._file('%s.exe' % self.problem)

    def get_cmdline(self, **kwargs) -> List[str]:
        assert self._executable is not None
        return ['mono', self._executable]

    def get_executable(self) -> str:
        return self.runtime_dict['mono']

    @classmethod
    def get_find_first_mapping(cls) -> Optional[Dict[str, List[str]]]:
        res = super().get_find_first_mapping()
        if res:
            res['mono'] = ['mono']
        return res

    def populate_result(self, stderr: bytes, result: Result,
                        process: TracedPopen) -> None:
        super().populate_result(stderr, result, process)
        if process.is_ir and b'Garbage collector could not allocate' in stderr:
            result.result_flag |= Result.MLE

    def parse_feedback_from_stderr(self, stderr: bytes,
                                   process: TracedPopen) -> str:
        match = deque(reexception.finditer(utf8text(stderr, 'replace')),
                      maxlen=1)
        if not match:
            return ''

        exception = match[0].group(1)
        return exception

    @classmethod
    def initialize(cls) -> bool:
        if 'mono' not in cls.runtime_dict or not os.path.isfile(
                cls.runtime_dict['mono']):
            return False
        return super().initialize()