Skip to content

Commit 5137349

Browse files
committed
test: Test --parent-cgroup specified but no --cgroup provided
The jailer's behavior when --parent-cgroup specified but no --cgroup provided varies depending on the existence and subtree_control configuration of the specified cgroup. Test all the cases for such a combination of parameters. Signed-off-by: Takahiro Itazuri <[email protected]>
1 parent ac03a22 commit 5137349

File tree

1 file changed

+69
-11
lines changed

1 file changed

+69
-11
lines changed

tests/integration_tests/security/test_jail.py

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,20 @@ def move_pid(self, cgname, pid):
194194
cg_pids = self.root.joinpath(f"{cgname}/cgroup.procs")
195195
cg_pids.write_text(f"{pid}\n", encoding="ascii")
196196

197+
def enable_controller_in_subtree(self, cgname, controller):
198+
"""Enable a controller in subtree_control of a cgroup and its ancestors"""
199+
# Enable the controller in all ancestors if not already enabled.
200+
parent_cg = self.root.joinpath(cgname).parent
201+
parent_subtree_control = parent_cg.joinpath("cgroup.subtree_control")
202+
if controller not in parent_subtree_control.read_text(encoding="ascii"):
203+
self.enable_controller_in_subtree(
204+
parent_cg.relative_to(self.root), controller
205+
)
206+
207+
subtree_control = self.root.joinpath(f"{cgname}/cgroup.subtree_control")
208+
subtree_control.write_text(f"+{controller}", encoding="ascii")
209+
assert controller in subtree_control.read_text(encoding="ascii")
210+
197211

198212
@pytest.fixture(scope="session", autouse=True)
199213
def cgroups_info():
@@ -230,11 +244,7 @@ def check_cgroups_v2(vm):
230244
cg_parent = cg.root / parent_cgroup
231245
cg_jail = cg_parent / vm.jailer.jailer_id
232246

233-
# if no cgroups were specified, then the jailer should move the FC process
234-
# to the parent group
235-
if len(vm.jailer.cgroups) == 0:
236-
procs = cg_parent.joinpath("cgroup.procs").read_text().splitlines()
237-
assert str(vm.firecracker_pid) in procs
247+
assert len(vm.jailer.cgroups) > 1
238248

239249
for cgroup in vm.jailer.cgroups:
240250
controller = cgroup.split(".")[0]
@@ -393,11 +403,23 @@ def test_v1_default_cgroups(uvm_plain, cgroups_info):
393403
check_cgroups_v1(test_microvm.jailer.cgroups, test_microvm.jailer.jailer_id)
394404

395405

396-
def test_cgroups_custom_parent_move(uvm_plain, cgroups_info):
406+
@pytest.mark.parametrize(
407+
"parent_exists,domain_controller_in_subtree",
408+
[(True, False), (True, True), (False, None)],
409+
)
410+
def test_cgroups_parent_cgroup_but_no_cgroup(
411+
uvm_plain, cgroups_info, parent_exists, domain_controller_in_subtree
412+
):
397413
"""
398-
Test cgroups when a custom parent cgroup is used and no cgroups are specified
414+
Test cgroups when `--parent-cgroup` is used but no `--cgroup` are specified.
399415
400-
In this case we just want to move under the parent cgroup
416+
If the cgroup specified with `--parent-cgroup` exists, the jailer should
417+
move to the specified cgroup instead of creating a new cgroup under it.
418+
However, if the specified cgroup has domain controllers (e.g. `memory`)
419+
enabled in `cgroup.subtree_control`, the move should fail.
420+
421+
If the specified cgroup does not exist, the jailer does not move the process
422+
to any cgroup and proceeds without error.
401423
"""
402424
if cgroups_info.version != 2:
403425
pytest.skip("cgroupsv2 only")
@@ -407,9 +429,45 @@ def test_cgroups_custom_parent_move(uvm_plain, cgroups_info):
407429
parent_cgroup = f"custom_cgroup/{test_microvm.id[:8]}"
408430
test_microvm.jailer.parent_cgroup = parent_cgroup
409431

410-
cgroups_info.new_cgroup(parent_cgroup)
411-
test_microvm.spawn()
412-
check_cgroups_v2(test_microvm)
432+
if parent_exists:
433+
# Create the parent cgroup.
434+
cgroups_info.new_cgroup(parent_cgroup)
435+
if domain_controller_in_subtree:
436+
# Enable "memory" controller in cgroup.subtree_control of the parent.
437+
cgroups_info.enable_controller_in_subtree(parent_cgroup, "memory")
438+
439+
# Check no --cgroups are specified just in case.
440+
assert len(test_microvm.jailer.cgroups) == 0
441+
442+
cg_parent = cgroups_info.root / parent_cgroup
443+
444+
if parent_exists:
445+
if domain_controller_in_subtree:
446+
# The jailer should have failed to move to the `parent_cgroup`
447+
# since it has domain controllers enabled in
448+
# `cgroup.subtree_control` due to the no internal process
449+
# constraint.
450+
# https://docs.kernel.org/admin-guide/cgroup-v2.html#no-internal-process-constraint
451+
with pytest.raises(
452+
ChildProcessError,
453+
match=(
454+
rf"Failed to move process to cgroup \({cg_parent}\) "
455+
r"due to no internal process constraint: "
456+
r"Resource busy \(os error 16\)"
457+
),
458+
):
459+
test_microvm.spawn()
460+
else:
461+
# The jailer should have moved to the `parent_cgroup` instead of
462+
# creating a new cgroup under it and move to the new cgroup.
463+
test_microvm.spawn()
464+
procs = cg_parent.joinpath("cgroup.procs").read_text().splitlines()
465+
assert str(test_microvm.firecracker_pid) in procs
466+
else:
467+
# The jailer should not have moved to any cgroup and the parent
468+
# still does not exist.
469+
test_microvm.spawn()
470+
assert not cg_parent.exists()
413471

414472

415473
def test_args_default_resource_limits(uvm_plain):

0 commit comments

Comments
 (0)