Skip to content

Commit edbed61

Browse files
authored
Merge pull request #5200 from xujihui1985/fix/rootfs-propagation
fix(libcontainer): preserve rootfs slave propagation for rslave containers
2 parents 506a568 + 38245cc commit edbed61

File tree

2 files changed

+91
-9
lines changed

2 files changed

+91
-9
lines changed

libcontainer/rootfs_linux.go

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,8 @@ func prepareRootfs(pipe *syncSocket, iConfig *initConfig) (err error) {
241241
// to the current root ("/"), and not to the old rootfs before it becomes "/". Applying the
242242
// flag in prepareRoot would affect the host mount namespace if the container's
243243
// root mount is shared.
244-
// MS_PRIVATE is skipped as rootfsParentMountPrivate() is already called.
245-
if config.RootPropagation != 0 && config.RootPropagation&unix.MS_PRIVATE == 0 {
244+
// MS_PRIVATE or MS_SLAVE is skipped as rootfsParentMountPropagation() is already called.
245+
if config.RootPropagation != 0 && config.RootPropagation&(unix.MS_PRIVATE|unix.MS_SLAVE) == 0 {
246246
if err := mount("", "/", "", uintptr(config.RootPropagation), ""); err != nil {
247247
return fmt.Errorf("unable to apply root propagation flags: %w", err)
248248
}
@@ -1061,19 +1061,27 @@ func mknodDevice(destDir *os.File, destName string, node *devices.Device) error
10611061
return nil
10621062
}
10631063

1064-
// rootfsParentMountPrivate ensures rootfs parent mount is private.
1064+
func rootfsParentMountPropagationFlags(rootPropagation int) uintptr {
1065+
if rootPropagation&unix.MS_SLAVE != 0 {
1066+
return unix.MS_SLAVE
1067+
}
1068+
return unix.MS_PRIVATE
1069+
}
1070+
1071+
// rootfsParentMountPropagation ensures rootfs parent mount is not shared.
10651072
// This is needed for two reasons:
10661073
// - pivot_root() will fail if parent mount is shared;
1067-
// - when we bind mount rootfs, if its parent is not private, the new mount
1074+
// - when we bind mount rootfs, if its parent is (r)shared, the new mount
10681075
// will propagate (leak!) to parent namespace and we don't want that.
1069-
func rootfsParentMountPrivate(path string) error {
1076+
func rootfsParentMountPropagation(path string, rootPropagation int) error {
10701077
var err error
1078+
flags := rootfsParentMountPropagationFlags(rootPropagation)
10711079
// Assuming path is absolute and clean (this is checked in
10721080
// libcontainer/validate). Any error other than EINVAL means we failed,
10731081
// and EINVAL means this is not a mount point, so traverse up until we
10741082
// find one.
10751083
for {
1076-
err = unix.Mount("", path, "", unix.MS_PRIVATE, "")
1084+
err = unix.Mount("", path, "", flags, "")
10771085
if err == nil {
10781086
return nil
10791087
}
@@ -1083,9 +1091,9 @@ func rootfsParentMountPrivate(path string) error {
10831091
path = filepath.Dir(path)
10841092
}
10851093
return &mountError{
1086-
op: "remount-private",
1094+
op: "remount-propagation",
10871095
target: path,
1088-
flags: unix.MS_PRIVATE,
1096+
flags: flags,
10891097
err: err,
10901098
}
10911099
}
@@ -1099,7 +1107,7 @@ func prepareRoot(config *configs.Config) error {
10991107
return err
11001108
}
11011109

1102-
if err := rootfsParentMountPrivate(config.Rootfs); err != nil {
1110+
if err := rootfsParentMountPropagation(config.Rootfs, config.RootPropagation); err != nil {
11031111
return err
11041112
}
11051113

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/usr/bin/env bats
2+
3+
load helpers
4+
5+
function require_mount_namespace_tools() {
6+
command -v unshare >/dev/null || skip "test requires unshare"
7+
command -v nsenter >/dev/null || skip "test requires nsenter"
8+
}
9+
10+
function in_mount_namespace() {
11+
local cwd
12+
cwd="$(pwd)"
13+
nsenter --mount="$ISOLATED_MNTNS" -- sh -c "cd \"\$1\" && shift && exec \"\$@\"" sh "$cwd" "$@"
14+
}
15+
16+
function setup_isolated_mount_namespace() {
17+
ISOLATED_MNTNS_DIR="$(mktemp -d "$BATS_RUN_TMPDIR/mntns.XXXXXX")"
18+
mount --bind "$ISOLATED_MNTNS_DIR" "$ISOLATED_MNTNS_DIR"
19+
mount --make-private "$ISOLATED_MNTNS_DIR"
20+
21+
ISOLATED_MNTNS="$ISOLATED_MNTNS_DIR/testns"
22+
touch "$ISOLATED_MNTNS"
23+
if ! unshare --mount="$ISOLATED_MNTNS" mount --make-rprivate /; then
24+
rm -f "$ISOLATED_MNTNS"
25+
umount "$ISOLATED_MNTNS_DIR" 2>/dev/null || true
26+
rmdir "$ISOLATED_MNTNS_DIR" 2>/dev/null || true
27+
fail "failed to bind isolated mount namespace"
28+
fi
29+
}
30+
31+
function teardown_isolated_mount_namespace() {
32+
if [ -n "${ISOLATED_MNTNS_DIR:-}" ]; then
33+
umount -l "$ISOLATED_MNTNS_DIR" 2>/dev/null || true
34+
rmdir "$ISOLATED_MNTNS_DIR" 2>/dev/null || true
35+
fi
36+
}
37+
38+
function __runc_in_mount_namespace() {
39+
setup_runc_cmdline
40+
in_mount_namespace "${RUNC_CMDLINE[@]}" "$@"
41+
}
42+
43+
function make_rootfs_shared() {
44+
in_mount_namespace mount --make-rshared /
45+
}
46+
47+
function runc_in_mount_namespace() {
48+
CMDNAME="$(basename "$RUNC")" sane_run __runc_in_mount_namespace "$@"
49+
}
50+
51+
function setup() {
52+
requires root
53+
require_mount_namespace_tools
54+
55+
setup_isolated_mount_namespace
56+
make_rootfs_shared
57+
setup_debian
58+
}
59+
60+
function teardown() {
61+
teardown_bundle
62+
teardown_isolated_mount_namespace
63+
}
64+
65+
@test "runc run [rootfsPropagation slave]" {
66+
# make sure the rootfs mount is slave before running the test
67+
update_config ' .linux.rootfsPropagation = "slave" '
68+
69+
update_config ' .process.args = ["findmnt", "--noheadings", "-o", "PROPAGATION", "/"] '
70+
71+
runc_in_mount_namespace run test_slave_rootfs
72+
[ "$status" -eq 0 ]
73+
[ "$output" = "private,slave" ]
74+
}

0 commit comments

Comments
 (0)