From 41bdb86ceef661292921de3c38c5a943cba9642c Mon Sep 17 00:00:00 2001 From: Junren Chen Date: Sun, 7 Jun 2026 09:51:34 +0000 Subject: [PATCH] test(jailer): regression test for chroot bind-mount propagation Bind mounts established on the host inside the chroot before jailer start must propagate into the jailer's mount namespace. The test bind-mounts kernel and rootfs onto empty placeholders in the chroot and drives the API directly (bypassing basic_config to avoid create_jailed_resource hardlinking over the mounts), then asserts a clean SSH boot. Signed-off-by: Junren Chen --- tests/integration_tests/security/test_jail.py | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tests/integration_tests/security/test_jail.py b/tests/integration_tests/security/test_jail.py index 1f31efa7739..4b896a9cd60 100644 --- a/tests/integration_tests/security/test_jail.py +++ b/tests/integration_tests/security/test_jail.py @@ -223,6 +223,63 @@ def test_arbitrary_usocket_location(uvm): ) +@pin_guest_kernel(GUEST_KERNEL_DEFAULT) +def test_jailer_bind_mount_propagation(uvm): + """ + Test that host bind-mounts inside the chroot propagate into the jail. + + Mounts that exist before the jailer starts, covering the recursive + self-bind fix (MS_BIND -> MS_BIND | MS_REC). + + Regression test for #1089. + """ + test_microvm = uvm + chroot = Path(test_microvm.chroot()) + + kernel_jail_name = "vmlinux.bin" + rootfs_jail_name = "rootfs.img" + kernel_mount_point = chroot / kernel_jail_name + rootfs_mount_point = chroot / rootfs_jail_name + kernel_mount_point.touch() + rootfs_mount_point.touch() + try: + subprocess.check_call( + ["mount", "--bind", str(test_microvm.kernel_file), str(kernel_mount_point)] + ) + subprocess.check_call( + ["mount", "--bind", str(test_microvm.rootfs_file), str(rootfs_mount_point)] + ) + test_microvm.spawn() + + # Drive the API directly: basic_config() would hardlink over our + # bind-mount points and mask what we are testing. + test_microvm.api.machine_config.put(vcpu_count=2, mem_size_mib=256) + test_microvm.boot_args = ( + "reboot=k panic=1 nomodule swiotlb=noforce console=ttyS0 cryptomgr.notests" + ) + if not test_microvm.pci_enabled: + test_microvm.boot_args += " pci=off" + test_microvm.api.boot.put( + kernel_image_path=f"/{kernel_jail_name}", + boot_args=test_microvm.boot_args, + ) + test_microvm.api.drive.put( + drive_id="rootfs", + path_on_host=f"/{rootfs_jail_name}", + is_root_device=True, + is_read_only=True, + ) + test_microvm.add_net_iface() + test_microvm.start() + test_microvm.ssh.check_output("true") + finally: + # Unmount before the framework's chroot rmtree so it never + # recurses into the bind-mount sources. + test_microvm.kill() + for mp in (rootfs_mount_point, kernel_mount_point): + subprocess.run(["umount", str(mp)], check=False) + + class Cgroups: """Helper class to work with cgroups"""