diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go index 0c8c5836d..9e882a592 100644 --- a/libcontainer/rootfs_linux.go +++ b/libcontainer/rootfs_linux.go @@ -1348,6 +1348,7 @@ func maskPaths(paths []string, mountLabel string) error { procSelfFd, closer := utils.ProcThreadSelf("fd/") defer closer() + maskedPaths := make(map[string]struct{}) for _, path := range paths { // Open the target path; skip if it doesn't exist. dstFh, err := os.OpenFile(path, unix.O_PATH|unix.O_CLOEXEC, 0) @@ -1362,6 +1363,14 @@ func maskPaths(paths []string, mountLabel string) error { dstFh.Close() return fmt.Errorf("can't mask path %q: %w", path, err) } + // skip duplicate masked paths. + cleanPath := pathrs.LexicallyCleanPath(path) + if _, ok := maskedPaths[cleanPath]; ok { + dstFh.Close() + continue + } + maskedPaths[cleanPath] = struct{}{} + var dstType string if st.IsDir() { // Destination is a directory: bind mount a ro tmpfs over it. diff --git a/tests/integration/mask.bats b/tests/integration/mask.bats index 5783332ea..6cc9339df 100644 --- a/tests/integration/mask.bats +++ b/tests/integration/mask.bats @@ -55,6 +55,20 @@ function teardown() { [[ "${output}" == *"Operation not permitted"* ]] } +@test "mask paths [duplicate paths]" { + update_config '(.. | select(.maskedPaths? != null)) .maskedPaths += ["/testdir", "/testfile"]' + runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox + [ "$status" -eq 0 ] + + runc exec test_busybox sh -c "mount | grep /testdir -c" + [ "$status" -eq 0 ] + [[ "${output}" == "1" ]] + + runc exec test_busybox sh -c "mount | grep /testfile -c" + [ "$status" -eq 0 ] + [[ "${output}" == "1" ]] +} + @test "mask paths [prohibit symlink /proc]" { ln -s /symlink rootfs/proc runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox