Skip to content

Commit

Permalink
Add cgroupv2 initialisation
Browse files Browse the repository at this point in the history
Ensure the cgroupv2 mount exists, subtree_control is not empty, and
swap is disabled.

Fix #126
Fix #102
  • Loading branch information
MarkKoz committed Dec 20, 2021
1 parent 6a861ea commit ce7d85a
Showing 1 changed file with 45 additions and 3 deletions.
48 changes: 45 additions & 3 deletions snekbox/nsjail.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ def __init__(self, nsjail_binary: str = NSJAIL_PATH):
self.config = self._read_config()
self.cgroup_version = self._get_cgroup_version()

if self.cgroup_version == 2:
self._init_cgroupv2()

log.info(f"Assuming cgroup version {self.cgroup_version}.")

def _get_cgroup_version(self) -> int:
Expand Down Expand Up @@ -147,6 +150,35 @@ def _create_dynamic_cgroups(self) -> str:

return cgroup

def _init_cgroupv2(self) -> None:
"""Ensure cgroupv2 children have controllers enabled and memory swapping is disabled."""
cgroup_mount = Path(self.config.cgroupv2_mount)

# Swap has to be disabled since NsJail doesn't do it.
(cgroup_mount / "memory.swap.max").write_text("0")

# If the root's subtree_control already has some controllers enabled,
# no further action is necessary.
if (cgroup_mount / "cgroup.subtree_control").read_text().strip():
return

# Move all processes from the cgroupv2 mount to a child cgroup.
# This is necessary to be able to write to subtree_control in the parent later.
# Otherwise, a write operation would yield a "device or resource busy" error.
init_cgroup = cgroup_mount / "init"
init_cgroup.mkdir(parents=True, exist_ok=True)

procs = (cgroup_mount / "cgroup.procs").read_text().split()
for proc in procs:
(init_cgroup / "cgroup.procs").write_text(proc)

# Enable all available controllers for child cgroups.
# This also retroactively enables controllers for children that already exist,
# including the "init" child created just before.
controllers = (cgroup_mount / "cgroup.controllers").read_text().split()
for controller in controllers:
(cgroup_mount / "cgroup.subtree_control").write_text(f"+{controller}")

@staticmethod
def _parse_log(log_lines: Iterable[str]) -> None:
"""Parse and log NsJail's log messages."""
Expand Down Expand Up @@ -226,7 +258,16 @@ def python3(
`py_args` are arguments to pass to the Python subprocess before the code,
which is the last argument. By default, it's "-c", which executes the code given.
"""
cgroup = self._create_dynamic_cgroups()
cgroup = None
if self.cgroup_version == 1:
cgroup = self._create_dynamic_cgroups()
nsjail_args = (
"--cgroup_mem_parent", cgroup,
"--cgroup_pids_parent", cgroup,
*nsjail_args,
)
else:
nsjail_args = ("--use_cgroupv2", *nsjail_args)

with NamedTemporaryFile() as nsj_log:
args = (
Expand Down Expand Up @@ -278,7 +319,8 @@ def python3(
log.info(f"nsjail return code: {returncode}")

# Remove the dynamically created cgroups once we're done
Path(self.config.cgroup_mem_mount, cgroup).rmdir()
Path(self.config.cgroup_pids_mount, cgroup).rmdir()
if self.cgroup_version == 1:
Path(self.config.cgroup_mem_mount, cgroup).rmdir()
Path(self.config.cgroup_pids_mount, cgroup).rmdir()

return CompletedProcess(args, returncode, output, None)

0 comments on commit ce7d85a

Please sign in to comment.