Skip to content

Commit

Permalink
Use isolate's /box directory for sandbox files
Browse files Browse the repository at this point in the history
  • Loading branch information
prandla committed Nov 27, 2023
1 parent 8324378 commit 33b2994
Showing 1 changed file with 28 additions and 27 deletions.
55 changes: 28 additions & 27 deletions cms/grading/Sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import os
import resource
import select
import shutil
import stat
import tempfile
import time
Expand Down Expand Up @@ -529,7 +530,7 @@ def archive(self):

# Archive the working directory
sandbox_archive_filename = "sandbox.zip"
sandbox_archive = self.relative_path(sandbox_archive_filename)
sandbox_archive = os.path.join(self.get_root_path(), sandbox_archive_filename)
content_path = self.get_root_path()
with zipfile.ZipFile(sandbox_archive, "w") as zip_file:
for root, dirs, files in os.walk(content_path):
Expand Down Expand Up @@ -893,6 +894,7 @@ def __init__(self, file_cacher, name=None, temp_dir=None):
else:
box_id = IsolateSandbox.next_id % 10
IsolateSandbox.next_id += 1
self.box_id = box_id

# We create a directory "home" inside the outer temporary directory,
# that will be bind-mounted to "/tmp" inside the sandbox (some
Expand All @@ -903,10 +905,6 @@ def __init__(self, file_cacher, name=None, temp_dir=None):
# outer directory exists with no read permissions.
self._outer_dir = tempfile.mkdtemp(dir=self.temp_dir,
prefix="cms-%s-" % (self.name))
self._home = os.path.join(self._outer_dir, "home")
self._home_dest = "/tmp"
os.mkdir(self._home)
self.allow_writing_all()

self.exec_name = 'isolate'
self.box_exec = self.detect_box_executable()
Expand All @@ -916,12 +914,21 @@ def __init__(self, file_cacher, name=None, temp_dir=None):
self.log = None
self.exec_num = -1
self.cmd_file = os.path.join(self._outer_dir, "commands.log")
self.cgroup = config.use_cgroups

# Tell isolate to get the sandbox ready. We do our best to cleanup
# after ourselves, but we might have missed something if a previous
# worker was interrupted in the middle of an execution, so we issue an
# idempotent cleanup.
self.cleanup(initial=True)
self._home = os.path.join(self.initialize_isolate(), "box")
self._home_dest = "/box"
self.allow_writing_all()

logger.debug("Sandbox in `%s' created, using box `%s'.",
self._home, self.box_exec)

# Default parameters for isolate
self.box_id = box_id # -b
self.cgroup = config.use_cgroups # --cg
self.chdir = self._home_dest # -c
self.dirs = [] # -d
self.preserve_env = False # -e
Expand All @@ -938,9 +945,6 @@ 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._home, dest=self._home_dest, options="rw")

# Set common environment variables.
# Specifically needed by Python, that searches the home for
# packages.
Expand All @@ -955,13 +959,6 @@ def __init__(self, file_cacher, name=None, temp_dir=None):
# particular, the System.Native assembly.
self.maybe_add_mapped_directory("/etc/mono", options="noexec")

# Tell isolate to get the sandbox ready. We do our best to cleanup
# after ourselves, but we might have missed something if a previous
# worker was interrupted in the middle of an execution, so we issue an
# idempotent cleanup.
self.cleanup()
self.initialize_isolate()

def add_mapped_directory(self, src, dest=None, options=None,
ignore_if_not_existing=False):
"""Add src to the directory to be mapped inside the sandbox.
Expand Down Expand Up @@ -989,6 +986,7 @@ def allow_writing_all(self):
"""Set permissions in such a way that any operation is allowed.
"""
return
os.chmod(self._home, 0o777)
for filename in os.listdir(self._home):
os.chmod(os.path.join(self._home, filename), 0o777)
Expand All @@ -997,6 +995,7 @@ def allow_writing_none(self):
"""Set permissions in such a way that the user cannot write anything.
"""
return
os.chmod(self._home, 0o755)
for filename in os.listdir(self._home):
os.chmod(os.path.join(self._home, filename), 0o755)
Expand All @@ -1016,6 +1015,7 @@ def allow_writing_only(self, inner_paths):
outside the home directory are ignored.
"""
return
outer_paths = []
for inner_path in inner_paths:
abs_inner_path = \
Expand Down Expand Up @@ -1435,12 +1435,13 @@ def initialize_isolate(self):
+ (["--cg"] if self.cgroup else [])
+ ["--box-id=%d" % self.box_id, "--init"])
try:
subprocess.check_call(init_cmd)
return subprocess.run(init_cmd, check=True,
capture_output=True, encoding="utf-8").stdout.strip()
except subprocess.CalledProcessError as e:
raise SandboxInterfaceException(
"Failed to initialize sandbox") from e

def cleanup(self, delete=False):
def cleanup(self, delete=False, initial=False):
"""See Sandbox.cleanup()."""
# The user isolate assigns within the sandbox might have created
# subdirectories and files therein, making the user outside the sandbox
Expand All @@ -1453,14 +1454,14 @@ def cleanup(self, delete=False):
+ (["--cg"] if self.cgroup else []) \
+ ["--box-id=%d" % self.box_id]

if delete:
# Ignore exit status as some files may be owned by our user
subprocess.call(
exe + [
"--dir=%s=%s:rw" % (self._home_dest, self._home),
"--run", "--",
"/bin/chmod", "777", "-R", self._home_dest],
stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
if not initial and not delete:
# cleanup() called in order to actually remove the sandbox,
# but we want to keep its /box dir.
# so copy it out separately
# If our home doesn't exist anymore, silently ignore it as
# cleanup() should be idempotent
if os.path.exists(self._home):
shutil.copytree(self._home, os.path.join(self._outer_dir, "box"))

# Tell isolate to cleanup the sandbox.
subprocess.check_call(
Expand Down

0 comments on commit 33b2994

Please sign in to comment.