|
| 1 | +From e855129df22621a08a5436a5cb6e9bfff9a9ad0f Mon Sep 17 00:00:00 2001 |
| 2 | +From: Aleksa Sarai < [email protected]> |
| 3 | +Date: Fri, 7 Nov 2025 14:52:09 +1100 |
| 4 | +Subject: [PATCH] rootfs: only set mode= for tmpfs mount if target already |
| 5 | + existed |
| 6 | + |
| 7 | +This was always the intended behaviour but commit 72fbb34f5006 ("rootfs: |
| 8 | +switch to fd-based handling of mountpoint targets") regressed it when |
| 9 | +adding a mechanism to create a file handle to the target if it didn't |
| 10 | +already exist (causing the later stat to always succeed). |
| 11 | + |
| 12 | +A lot of people depend on this functionality, so add some tests to make |
| 13 | +sure we don't break it in the future. |
| 14 | + |
| 15 | +Fixes: 72fbb34f5006 ("rootfs: switch to fd-based handling of mountpoint targets") |
| 16 | +Signed-off-by: Aleksa Sarai < [email protected]> |
| 17 | +(cherry picked from commit 8d0921c4c76cdd527c86251c728e46f071c96337) |
| 18 | +Signed-off-by: Aleksa Sarai < [email protected]> |
| 19 | +--- |
| 20 | + libcontainer/rootfs_linux.go | 25 ++++++++-------- |
| 21 | + tests/integration/mounts.bats | 54 +++++++++++++++++++++++++++++++++++ |
| 22 | + 2 files changed, 66 insertions(+), 13 deletions(-) |
| 23 | + |
| 24 | +diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go |
| 25 | +index 204e6a80fc1..ab5a260dae8 100644 |
| 26 | +--- a/libcontainer/rootfs_linux.go |
| 27 | ++++ b/libcontainer/rootfs_linux.go |
| 28 | +@@ -511,6 +511,18 @@ func (m *mountEntry) createOpenMountpoint(rootfs string) (Err error) { |
| 29 | + _ = dstFile.Close() |
| 30 | + } |
| 31 | + }() |
| 32 | ++ if err == nil && m.Device == "tmpfs" { |
| 33 | ++ // If the original target exists, copy the mode for the tmpfs mount. |
| 34 | ++ stat, err := dstFile.Stat() |
| 35 | ++ if err != nil { |
| 36 | ++ return fmt.Errorf("check tmpfs source mode: %w", err) |
| 37 | ++ } |
| 38 | ++ dt := fmt.Sprintf("mode=%04o", syscallMode(stat.Mode())) |
| 39 | ++ if m.Data != "" { |
| 40 | ++ dt = dt + "," + m.Data |
| 41 | ++ } |
| 42 | ++ m.Data = dt |
| 43 | ++ } |
| 44 | + if err != nil { |
| 45 | + if !errors.Is(err, unix.ENOENT) { |
| 46 | + return fmt.Errorf("lookup mountpoint target: %w", err) |
| 47 | +@@ -551,19 +563,6 @@ func (m *mountEntry) createOpenMountpoint(rootfs string) (Err error) { |
| 48 | + } |
| 49 | + } |
| 50 | + |
| 51 | +- if m.Device == "tmpfs" { |
| 52 | +- // If the original target exists, copy the mode for the tmpfs mount. |
| 53 | +- stat, err := dstFile.Stat() |
| 54 | +- if err != nil { |
| 55 | +- return fmt.Errorf("check tmpfs source mode: %w", err) |
| 56 | +- } |
| 57 | +- dt := fmt.Sprintf("mode=%04o", syscallMode(stat.Mode())) |
| 58 | +- if m.Data != "" { |
| 59 | +- dt = dt + "," + m.Data |
| 60 | +- } |
| 61 | +- m.Data = dt |
| 62 | +- } |
| 63 | +- |
| 64 | + dstFullPath, err := procfs.ProcSelfFdReadlink(dstFile) |
| 65 | + if err != nil { |
| 66 | + return fmt.Errorf("get mount destination real path: %w", err) |
| 67 | +diff --git a/tests/integration/mounts.bats b/tests/integration/mounts.bats |
| 68 | +index 11fb2cfc63e..2da17df2706 100644 |
| 69 | +--- a/tests/integration/mounts.bats |
| 70 | ++++ b/tests/integration/mounts.bats |
| 71 | +@@ -234,6 +234,60 @@ function test_mount_order() { |
| 72 | + [[ "$(stat -c %a rootfs/setgid/a/b/c)" == 2755 ]] |
| 73 | + } |
| 74 | + |
| 75 | ++# https://github.com/opencontainers/runc/issues/4971 |
| 76 | ++@test "runc run [tmpfs mount mode= inherit]" { |
| 77 | ++ mkdir rootfs/tmpfs |
| 78 | ++ chmod "=0710" rootfs/tmpfs |
| 79 | ++ |
| 80 | ++ update_config '.mounts += [{ |
| 81 | ++ type: "tmpfs", |
| 82 | ++ source: "tmpfs", |
| 83 | ++ destination: "/tmpfs", |
| 84 | ++ options: ["rw", "nodev", "nosuid"] |
| 85 | ++ }]' |
| 86 | ++ update_config '.process.args = ["stat", "-c", "%a", "/tmpfs"]' |
| 87 | ++ |
| 88 | ++ runc run test_busybox |
| 89 | ++ [ "$status" -eq 0 ] |
| 90 | ++ [[ "$output" == "710" ]] |
| 91 | ++ |
| 92 | ++ update_config '.process.args = ["cat", "/proc/self/mounts"]' |
| 93 | ++ runc run test_busybox |
| 94 | ++ [ "$status" -eq 0 ] |
| 95 | ++ grep -Ex "tmpfs /tmpfs tmpfs [^ ]*\bmode=710\b[^ ]* .*" <<<"$output" |
| 96 | ++} |
| 97 | ++ |
| 98 | ++# https://github.com/opencontainers/runc/issues/4971 |
| 99 | ++@test "runc run [tmpfs mount mode=1777 default]" { |
| 100 | ++ update_config '.mounts += [{ |
| 101 | ++ type: "tmpfs", |
| 102 | ++ source: "tmpfs", |
| 103 | ++ destination: "/non-existent/foo/bar/baz", |
| 104 | ++ options: ["rw", "nodev", "nosuid"] |
| 105 | ++ }]' |
| 106 | ++ update_config '.process.args = ["stat", "-c", "%a", "/non-existent/foo/bar/baz"]' |
| 107 | ++ |
| 108 | ++ rm -rf rootfs/non-existent |
| 109 | ++ runc run test_busybox |
| 110 | ++ [ "$status" -eq 0 ] |
| 111 | ++ [[ "$output" == "1777" ]] |
| 112 | ++ |
| 113 | ++ update_config '.process.args = ["cat", "/proc/self/mounts"]' |
| 114 | ++ |
| 115 | ++ rm -rf rootfs/non-existent |
| 116 | ++ runc run test_busybox |
| 117 | ++ [ "$status" -eq 0 ] |
| 118 | ++ # We don't explicitly set a mode= in this case, it is just the tmpfs default. |
| 119 | ++ grep -Ex "tmpfs /non-existent/foo/bar/baz tmpfs .*" <<<"$output" |
| 120 | ++ run ! grep -Ex "tmpfs /non-existent/foo/bar/baz tmpfs [^ ]*\bmode=[0-7]+\b[^ ]* .*" <<<"$output" |
| 121 | ++ |
| 122 | ++ # Verify that the actual modes are *not* 1777. |
| 123 | ++ [[ "$(stat -c %a rootfs/non-existent)" == 755 ]] |
| 124 | ++ [[ "$(stat -c %a rootfs/non-existent/foo)" == 755 ]] |
| 125 | ++ [[ "$(stat -c %a rootfs/non-existent/foo/bar)" == 755 ]] |
| 126 | ++ [[ "$(stat -c %a rootfs/non-existent/foo/bar/baz)" == 755 ]] |
| 127 | ++} |
| 128 | ++ |
| 129 | + @test "runc run [ro /sys/fs/cgroup mounts]" { |
| 130 | + # Without cgroup namespace. |
| 131 | + update_config '.linux.namespaces -= [{"type": "cgroup"}]' |
0 commit comments