Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions cms/grading/Sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -894,22 +894,27 @@ def __init__(self, file_cacher, name=None, temp_dir=None):
box_id = IsolateSandbox.next_id % 10
IsolateSandbox.next_id += 1

# We create a directory "tmp" inside the outer temporary directory,
# We create a directory "box" inside the outer temporary directory,
# because the sandbox will bind-mount the inner one. The sandbox also
# runs code as a different user, and so we need to ensure that they can
# read and write to the directory. But we don't want everybody on the
# system to, which is why the outer directory exists with no read
# permissions.
self.inner_temp_dir = "/tmp"
# permissions to other than the user.
self.outer_temp_dir = tempfile.mkdtemp(
dir=self.temp_dir,
prefix="cms-%s-" % (self.name))
# Don't use os.path.join here, because the absoluteness of /tmp will
# bite you.
self.path = self.outer_temp_dir + self.inner_temp_dir
self.path = os.path.join(self.outer_temp_dir, "box")
os.mkdir(self.path)
self.allow_writing_all()

# self.path will be bind-mounted inside the sandbox as inner_temp_dir.
# We use a subdirectory of /tmp so that the sandbox will create a
# /tmp, which is sometimes used by compilers. We don't want /tmp
# itself because some tasktype might decide to bind-mount external
# temp directories to the same name inside the sandbox, and we having
# one mount path as a children of another is a recipe for disaster.
self.inner_temp_dir = "/cms"

self.exec_name = 'isolate'
self.box_exec = self.detect_box_executable()
self.info_basename = "run.log" # Used for -M
Expand All @@ -923,7 +928,6 @@ def __init__(self, file_cacher, name=None, temp_dir=None):
self.cgroup = config.use_cgroups # --cg
self.chdir = self.inner_temp_dir # -c
self.dirs = [] # -d
self.dirs += [(self.inner_temp_dir, self.path, "rw")]
self.preserve_env = False # -e
self.inherit_env = [] # -E
self.set_env = {} # -E
Expand All @@ -938,6 +942,8 @@ def __init__(self, file_cacher, name=None, temp_dir=None):
self.wallclock_timeout = None # -w
self.extra_timeout = None # -x

self.add_mapped_directory(self.path, inner=self.inner_temp_dir)

# Set common environment variables.
# Specifically needed by Python, that searches the home for
# packages.
Expand All @@ -955,14 +961,26 @@ def __init__(self, file_cacher, name=None, temp_dir=None):
self.cleanup()
self.initialize_isolate()

def add_mapped_directory(self, dir, inner=None, options="rw"):
"""Add dir to the external directory visible to the command

dir (string): directory to make visible.
inner (string|None): if not None, the inner path where to bind dir.
options (string|None): if not None, isolate directory rule options.

"""
if inner is None:
inner = dir
self.dirs.append((inner, dir, options))

def add_mapped_directories(self, dirs):
"""Add dirs to the external dirs visible to the sandboxed command.

dirs ([string]): list of dirs to make visible.

"""
for directory in dirs:
self.dirs.append((directory, None, "rw"))
self.add_mapped_directory(directory)

def allow_writing_all(self):
"""Set permissions in such a way that any operation is allowed.
Expand Down Expand Up @@ -1037,7 +1055,7 @@ def detect_box_executable(self):

def build_box_options(self):
"""Translate the options defined in the instance to a string
that can be postponed to mo-box as an arguments list.
that can be postponed to isolate as an arguments list.

return ([string]): the arguments list as strings.

Expand All @@ -1050,9 +1068,7 @@ def build_box_options(self):
if self.chdir is not None:
res += ["--chdir=%s" % self.chdir]
for in_name, out_name, options in self.dirs:
s = in_name
if out_name is not None:
s += "=" + out_name
s = in_name + "=" + out_name
if options is not None:
s += ":" + options
res += ["--dir=%s" % s]
Expand Down
4 changes: 3 additions & 1 deletion cms/grading/languages/haskell_ghc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-

# Contest Management System - http://cms-dev.github.io/
# Copyright © 2016 Stefano Maggiolo <s.maggiolo@gmail.com>
# Copyright © 2016-2018 Stefano Maggiolo <s.maggiolo@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
Expand Down Expand Up @@ -60,6 +60,8 @@ def get_compilation_commands(self,
for_evaluation=True):
"""See Language.get_compilation_commands."""
commands = []
# GHC requires a /tmp directory, so we create it if necessary.
commands.append(["/bin/mkdir", "-p", "/tmp"])
# Haskell module names are capitalized, so we change the source file
# names (except for the first one) to match the module's name.
# The first source file is, instead, the grader or the standalone
Expand Down
13 changes: 9 additions & 4 deletions cms/grading/languages/rust.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

# Contest Management System - http://cms-dev.github.io/
# Copyright © 2017 Dario Ostuni <dario.ostuni@gmail.com>
# Copyright © 2018 Stefano Maggiolo <s.maggiolo@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
Expand Down Expand Up @@ -52,7 +53,11 @@ def get_compilation_commands(self,
source_filenames, executable_filename,
for_evaluation=True):
"""See Language.get_compilation_commands."""
# In Rust only the source file containing the main function has
# to be passed to the compiler
return [["/usr/bin/rustc", "-O", "-o",
executable_filename, source_filenames[0]]]
return [
# rustc requires a /tmp directory, so we create it if necessary.
["/bin/mkdir", "-p", "/tmp"],
# In Rust only the source file containing the main function has
# to be passed to the compiler
["/usr/bin/rustc", "-O", "-o",
executable_filename, source_filenames[0]]
]
6 changes: 3 additions & 3 deletions cms/grading/steps/compilation.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,14 @@ def compilation_step(sandbox, commands):

"""
# Set sandbox parameters suitable for compilation.
sandbox.dirs += [("/etc", None, None)]
# We need to add "/var/lib/ghc" to the unrestricted dirs so GHC can access
sandbox.add_mapped_directory("/etc", options=None)
# We need to add "/var/lib/ghc" to the mapped dirs so GHC can access
# haskell's package database.
# GHC looks for it in "/usr/lib/ghc/package.conf.d", which is only a
# symlink to "/var/lib/ghc/package.conf.d"
ghc_dir = "/var/lib/ghc"
if os.path.exists(ghc_dir):
sandbox.dirs += [("/var/lib/ghc", None, None)]
sandbox.add_mapped_directory(ghc_dir, options=None)
sandbox.preserve_env = True
sandbox.max_processes = config.compilation_sandbox_max_processes
sandbox.timeout = config.compilation_sandbox_max_time_s
Expand Down