diff --git a/libcontainer/mount_linux.go b/libcontainer/mount_linux.go index 5f49de964..948b6c0b4 100644 --- a/libcontainer/mount_linux.go +++ b/libcontainer/mount_linux.go @@ -1,6 +1,7 @@ package libcontainer import ( + "io/fs" "strconv" "golang.org/x/sys/unix" @@ -81,3 +82,20 @@ func unmount(target string, flags int) error { } return nil } + +// syscallMode returns the syscall-specific mode bits from Go's portable mode bits. +// Copy from https://cs.opensource.google/go/go/+/refs/tags/go1.20.7:src/os/file_posix.go;l=61-75 +func syscallMode(i fs.FileMode) (o uint32) { + o |= uint32(i.Perm()) + if i&fs.ModeSetuid != 0 { + o |= unix.S_ISUID + } + if i&fs.ModeSetgid != 0 { + o |= unix.S_ISGID + } + if i&fs.ModeSticky != 0 { + o |= unix.S_ISVTX + } + // No mapping for Go's ModeTemporary (plan9 only). + return +} diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go index 54520ad01..c701d6a2f 100644 --- a/libcontainer/rootfs_linux.go +++ b/libcontainer/rootfs_linux.go @@ -464,7 +464,7 @@ func mountToRootfs(m *configs.Mount, c *mountConfig) error { return err } } else { - dt := fmt.Sprintf("mode=%04o", stat.Mode()) + dt := fmt.Sprintf("mode=%04o", syscallMode(stat.Mode())) if m.Data != "" { dt = dt + "," + m.Data } diff --git a/tests/integration/run.bats b/tests/integration/run.bats index d2eae2a08..e59ccb4a4 100644 --- a/tests/integration/run.bats +++ b/tests/integration/run.bats @@ -59,6 +59,22 @@ function teardown() { [ "$status" -ne 0 ] } +# https://github.com/opencontainers/runc/issues/3952 +@test "runc run with tmpfs" { + requires root + + chmod 'a=rwx,ug+s,+t' rootfs/tmp # set all bits + mode=$(stat -c %A rootfs/tmp) + + # shellcheck disable=SC2016 + update_config '.process.args = ["sh", "-c", "stat -c %A /tmp"]' + update_config '.mounts += [{"destination": "/tmp", "type": "tmpfs", "source": "tmpfs", "options":["noexec","nosuid","nodev","rprivate"]}]' + + runc run test_tmpfs + [ "$status" -eq 0 ] + [ "$output" = "$mode" ] +} + @test "runc run with tmpfs perms" { # shellcheck disable=SC2016 update_config '.process.args = ["sh", "-c", "stat -c %a /tmp/test"]'