From 11a998368cc2f0a7722de7840e5bba46ecc052a4 Mon Sep 17 00:00:00 2001 From: lifubang Date: Tue, 3 Feb 2026 03:44:54 +0000 Subject: [PATCH] libct/specconv: fix partial clear of atime mount flags When parsing mount options into recAttrSet and recAttrClr, the code sets attr_clr to individual atime flags (e.g. MOUNT_ATTR_NOATIME or MOUNT_ATTR_STRICTATIME) when clearing atime attributes. However, this violates the kernel's requirement documented in mount_setattr(2)[1]: > Note that, since the access-time values are an enumeration > rather than bit values, a caller wanting to transition to a > different access-time setting cannot simply specify the > access-time setting in attr_set, but must also include > MOUNT_ATTR__ATIME in the attr_clr field. The kernel will > verify that MOUNT_ATTR__ATIME isn't partially set in > attr_clr (i.e., either all bits in the MOUNT_ATTR__ATIME > bit field are either set or clear), and that attr_set > doesn't have any access-time bits set if MOUNT_ATTR__ATIME > isn't set in attr_clr. Passing only a single atime flag (e.g. MOUNT_ATTR_RELATIME) in attr_clr causes mount_setattr() to fail with EINVAL. This change ensures that whenever an atime mode is updated, attr_clr includes MOUNT_ATTR__ATIME to properly reset the entire access-time attribute field before applying the new mode. [1] https://man7.org/linux/man-pages/man2/mount_setattr.2.html Signed-off-by: lifubang (cherry picked from commit 5560d55bfd84a49441c6812140412f1bcf863a1a) Signed-off-by: lifubang --- libcontainer/specconv/spec_linux.go | 10 ++--- tests/integration/mounts_recursive.bats | 52 ++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/libcontainer/specconv/spec_linux.go b/libcontainer/specconv/spec_linux.go index 957011b4519..162195d8e4e 100644 --- a/libcontainer/specconv/spec_linux.go +++ b/libcontainer/specconv/spec_linux.go @@ -1164,11 +1164,11 @@ func parseMountOptions(options []string) *configs.Mount { } else { recAttrSet |= f.flag recAttrClr &= ^f.flag - if f.flag&unix.MOUNT_ATTR__ATIME == f.flag { - // https://man7.org/linux/man-pages/man2/mount_setattr.2.html - // "cannot simply specify the access-time setting in attr_set, but must also include MOUNT_ATTR__ATIME in the attr_clr field." - recAttrClr |= unix.MOUNT_ATTR__ATIME - } + } + if f.flag&unix.MOUNT_ATTR__ATIME == f.flag { + // https://man7.org/linux/man-pages/man2/mount_setattr.2.html + // "cannot simply specify the access-time setting in attr_set, but must also include MOUNT_ATTR__ATIME in the attr_clr field." + recAttrClr |= unix.MOUNT_ATTR__ATIME } } else if f, exists := extensionFlags[o]; exists { if f.clear { diff --git a/tests/integration/mounts_recursive.bats b/tests/integration/mounts_recursive.bats index b3ce579fc02..fdb9daba901 100644 --- a/tests/integration/mounts_recursive.bats +++ b/tests/integration/mounts_recursive.bats @@ -23,7 +23,7 @@ function teardown_volume() { function setup() { setup_volume - setup_busybox + setup_debian } function teardown() { @@ -76,3 +76,53 @@ function teardown() { [ "$status" -eq 1 ] [[ "${output}" == *"Read-only file system"* ]] } + +# https://github.com/opencontainers/runc/issues/5095 +@test "runc run [ check rbind,r*atime mounts]" { + requires_kernel 5.12 + update_config ".mounts += [{source: \"${TESTVOLUME}\" , destination: \"/mnt1\", options: [\"rbind\",\"ratime\"]}]" + update_config ".mounts += [{source: \"${TESTVOLUME}\" , destination: \"/mnt2\", options: [\"rbind\",\"rnoatime\"]}]" + update_config ".mounts += [{source: \"${TESTVOLUME}\" , destination: \"/mnt3\", options: [\"rbind\",\"rstrictatime\"]}]" + update_config ".mounts += [{source: \"${TESTVOLUME}\" , destination: \"/mnt4\", options: [\"rbind\",\"rnostrictatime\"]}]" + update_config ".mounts += [{source: \"${TESTVOLUME}\" , destination: \"/mnt5\", options: [\"rbind\",\"rrelatime\"]}]" + update_config ".mounts += [{source: \"${TESTVOLUME}\" , destination: \"/mnt6\", options: [\"rbind\",\"rnorelatime\"]}]" + + runc run -d --console-socket "$CONSOLE_SOCKET" test_rbind_ratime + [ "$status" -eq 0 ] + + runc exec test_rbind_ratime findmnt --noheadings -o options /mnt1 + [[ "${output}" == "rw,relatime,"* ]] + + runc exec test_rbind_ratime findmnt --noheadings -o options /mnt1/subvol + [[ "${output}" == "rw,relatime,"* ]] + + runc exec test_rbind_ratime findmnt --noheadings -o options /mnt2 + [[ "${output}" == "rw,noatime,"* ]] + + runc exec test_rbind_ratime findmnt --noheadings -o options /mnt2/subvol + [[ "${output}" == "rw,noatime,"* ]] + + runc exec test_rbind_ratime findmnt --noheadings -o options /mnt3 + [[ "${output}" == "rw,"* ]] + + runc exec test_rbind_ratime findmnt --noheadings -o options /mnt3/subvol + [[ "${output}" == "rw,"* ]] + + runc exec test_rbind_ratime findmnt --noheadings -o options /mnt4 + [[ "${output}" == "rw,relatime,"* ]] + + runc exec test_rbind_ratime findmnt --noheadings -o options /mnt4/subvol + [[ "${output}" == "rw,relatime,"* ]] + + runc exec test_rbind_ratime findmnt --noheadings -o options /mnt5 + [[ "${output}" == "rw,relatime,"* ]] + + runc exec test_rbind_ratime findmnt --noheadings -o options /mnt5/subvol + [[ "${output}" == "rw,relatime,"* ]] + + runc exec test_rbind_ratime findmnt --noheadings -o options /mnt6 + [[ "${output}" == "rw,relatime,"* ]] + + runc exec test_rbind_ratime findmnt --noheadings -o options /mnt6/subvol + [[ "${output}" == "rw,relatime,"* ]] +}