From 6464c7a2c9a0eae2ff67a16cc6f87a69c0a89958 Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Sun, 1 Feb 2026 23:39:04 +0530 Subject: [PATCH 1/3] ci: use common cri-tools version for windows tests Signed-off-by: Akhil Mohan --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b579e32d4381..46a0338ebf56e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -241,6 +241,7 @@ jobs: echo "${{ github.workspace }}/bin" >> $GITHUB_PATH echo "${{ github.workspace }}/src/github.com/containerd/containerd/bin" >> $GITHUB_PATH echo "${{ github.workspace }}/src/github.com/kubernetes-sigs/cri-tools/build/bin/windows/amd64" >> $GITHUB_PATH + echo "CRITOOLS_VERSION=$(cat script/setup/critools-version)" >> $GITHUB_ENV - run: script/setup/install-dev-tools @@ -251,9 +252,8 @@ jobs: run: | set -o xtrace mingw32-make.exe binaries - CRITEST_VERSION=$(cat script/setup/critools-version) cd ../../kubernetes-sigs/cri-tools - git checkout "${CRITEST_VERSION}" + git checkout "${CRITOOLS_VERSION}" make critest - run: script/setup/install-cni-windows @@ -278,7 +278,7 @@ jobs: shell: powershell run: | # Get critctl tool. Used for cri-integration tests - $CRICTL_DOWNLOAD_URL="https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.26.0/crictl-v1.26.0-windows-amd64.tar.gz" + $CRICTL_DOWNLOAD_URL="https://github.com/kubernetes-sigs/cri-tools/releases/download/$env:CRITOOLS_VERSION/crictl-$env:CRITOOLS_VERSION-windows-amd64.tar.gz" curl.exe -L $CRICTL_DOWNLOAD_URL -o c:\crictl.tar.gz tar -xvf c:\crictl.tar.gz mv crictl.exe "${{ github.workspace }}/bin/crictl.exe" # Move crictl somewhere in path From 5d3b3447c7667d826eec59f9580c947ff24ecec6 Mon Sep 17 00:00:00 2001 From: "user.email" <123011167+lukefr09@users.noreply.github.com> Date: Wed, 25 Feb 2026 21:48:51 -0600 Subject: [PATCH 2/3] core/mount: fix getUnprivilegedMountFlags iterating over indices instead of values The loop `for flag := range unprivilegedFlags` iterates over slice indices (0,1,2,3,4,5,6) rather than the actual flag values (MS_RDONLY, MS_NODEV, etc). This was a porting error from moby/moby where the data structure was a map (where `for k := range m` yields keys/values). As a result, MS_NOEXEC, MS_NOATIME, MS_RELATIME, and MS_NODIRATIME are never detected or preserved. In user namespaces, this causes bind-mount remounts to fail with EPERM when any of these flags are locked on the parent mount, because the kernel requires all CL_UNPRIVILEGED locked flags to be preserved during remount. MS_RDONLY (0x1), MS_NOSUID (0x2), and MS_NODEV (0x4) happened to work by coincidence because their values equal low index numbers. Fix by using `for _, flag := range` to iterate over values. Signed-off-by: Luke Hinds --- core/mount/mount_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/mount/mount_linux.go b/core/mount/mount_linux.go index 2f33ea983132a..de813053cfbe1 100644 --- a/core/mount/mount_linux.go +++ b/core/mount/mount_linux.go @@ -231,7 +231,7 @@ func getUnprivilegedMountFlags(path string) (int, error) { } var flags int - for flag := range unprivilegedFlags { + for _, flag := range unprivilegedFlags { if int(statfs.Flags)&flag == flag { flags |= flag } From 1466c531960cdbc7cb5e2837fa0b209deb432d83 Mon Sep 17 00:00:00 2001 From: "user.email" <123011167+lukefr09@users.noreply.github.com> Date: Wed, 25 Feb 2026 21:54:00 -0600 Subject: [PATCH 3/3] core/mount: add test for getUnprivilegedMountFlags Mounts a tmpfs with MS_NOEXEC, MS_NOATIME, and MS_NODIRATIME and verifies that getUnprivilegedMountFlags detects all of them. These three flags were the ones missed by the range-over-indices bug. Also verifies that flags not present on the mount (MS_NOSUID, MS_NODEV, MS_RDONLY) are not falsely reported. Signed-off-by: Luke Hinds --- core/mount/mount_linux_test.go | 44 ++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/core/mount/mount_linux_test.go b/core/mount/mount_linux_test.go index d060de387167f..8f45bdff4b974 100644 --- a/core/mount/mount_linux_test.go +++ b/core/mount/mount_linux_test.go @@ -404,6 +404,50 @@ func TestDoPrepareIDMappedOverlay(t *testing.T) { } } +func TestGetUnprivilegedMountFlags(t *testing.T) { + testutil.RequiresRoot(t) + + td := t.TempDir() + target := filepath.Join(td, "mnt") + require.NoError(t, os.Mkdir(target, 0755)) + + // Mount a tmpfs with noexec,noatime,nodiratime -- these are the flags + // that were previously missed due to iterating over slice indices + // instead of values. + require.NoError(t, unix.Mount("tmpfs", target, "tmpfs", unix.MS_NOEXEC|unix.MS_NOATIME|unix.MS_NODIRATIME, "")) + defer unix.Unmount(target, unix.MNT_DETACH) + + flags, err := getUnprivilegedMountFlags(target) + require.NoError(t, err) + + for _, tc := range []struct { + flag int + name string + }{ + {unix.MS_NOEXEC, "MS_NOEXEC"}, + {unix.MS_NOATIME, "MS_NOATIME"}, + {unix.MS_NODIRATIME, "MS_NODIRATIME"}, + } { + if flags&tc.flag != tc.flag { + t.Errorf("expected %s (0x%x) to be set in flags 0x%x", tc.name, tc.flag, flags) + } + } + + // MS_NOSUID and MS_NODEV should NOT be set since we didn't mount with them. + for _, tc := range []struct { + flag int + name string + }{ + {unix.MS_NOSUID, "MS_NOSUID"}, + {unix.MS_NODEV, "MS_NODEV"}, + {unix.MS_RDONLY, "MS_RDONLY"}, + } { + if flags&tc.flag != 0 { + t.Errorf("expected %s (0x%x) to NOT be set in flags 0x%x", tc.name, tc.flag, flags) + } + } +} + func setupMounts(t *testing.T) (target string, mounts []Mount) { dir1 := t.TempDir() dir2 := t.TempDir()