Skip to content

Commit 55c4cdf

Browse files
committed
libcontainer: skip EPERM from rootfsParentMountPrivate in userns
In a user namespace, mounts inherited from a more privileged mount namespace are locked by the kernel. Attempting to change their propagation to MS_PRIVATE returns EPERM. This is safe to ignore because prepareRoot() has already set MS_SLAVE recursively, which is sufficient for pivot_root() and prevents mount leaks. Signed-off-by: yksun <yksun@alauda.io>
1 parent 496b68a commit 55c4cdf

2 files changed

Lines changed: 42 additions & 0 deletions

File tree

libcontainer/rootfs_linux.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,15 @@ func rootfsParentMountPrivate(path string) error {
10581058
}
10591059
path = filepath.Dir(path)
10601060
}
1061+
// In a user namespace, mounts inherited from a more privileged mount
1062+
// namespace are "locked" and cannot be changed to MS_PRIVATE (the
1063+
// kernel returns EPERM). This is safe to ignore because prepareRoot()
1064+
// has already set the propagation to MS_SLAVE recursively, which is
1065+
// sufficient for pivot_root() to succeed and prevents mount events
1066+
// from leaking to the parent namespace.
1067+
if err == unix.EPERM && userns.RunningInUserNS() {
1068+
return nil
1069+
}
10611070
return &mountError{
10621071
op: "remount-private",
10631072
target: path,

tests/integration/userns.bats

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,39 @@ function teardown() {
264264
run ! ip link del dummy0
265265
}
266266

267+
# Regression test for https://github.com/opencontainers/runc/pull/XXXX.
268+
# In a user namespace, mounts inherited from a more privileged mount namespace
269+
# are locked and cannot have their propagation changed to MS_PRIVATE (EPERM).
270+
# rootfsParentMountPrivate should skip EPERM in this case.
271+
@test "userns with shared parent mount" {
272+
requires root
273+
274+
# Relocate the bundle into a shared mount. Using a separate tmpdir
275+
# (rather than making $ROOT/bundle shared in-place) avoids cleanup
276+
# ordering issues with teardown_bundle's rm -rf of $ROOT.
277+
shared_dir=$(mktemp -d "$BATS_RUN_TMPDIR/shared-parent.XXXXXX")
278+
chmod a+x "$shared_dir"
279+
mount --bind "$shared_dir" "$shared_dir"
280+
mount --make-shared "$shared_dir"
281+
echo "$shared_dir" >>"$to_umount_list"
282+
283+
mv "$ROOT/bundle" "$shared_dir/bundle"
284+
cd "$shared_dir/bundle"
285+
286+
update_config '.process.args = ["cat", "/proc/self/mountinfo"]'
287+
288+
runc run test_busybox
289+
[ "$status" -eq 0 ]
290+
291+
# Verify the EPERM-skip path was exercised: if rootfsParentMountPrivate
292+
# successfully changed the parent mount to MS_PRIVATE, the root mount
293+
# would be private (no propagation tag in mountinfo). If EPERM was hit
294+
# and skipped (the fix), the parent stays slave, so the root mount
295+
# inherits slave propagation (shown as "master:N" in mountinfo).
296+
root_mnt_line=$(grep ' / / ' <<<"$output" | head -1)
297+
[[ "$root_mnt_line" == *master:* ]]
298+
}
299+
267300
@test "userns with network interface renamed" {
268301
requires root
269302

0 commit comments

Comments
 (0)