diff --git a/SPECS/kubernetes/CVE-2024-45310.patch b/SPECS/kubernetes/CVE-2024-45310.patch new file mode 100644 index 00000000000..5fc2db275f6 --- /dev/null +++ b/SPECS/kubernetes/CVE-2024-45310.patch @@ -0,0 +1,673 @@ +From 1a2caa8128efd0e7de0a2100791482430e3ae37a Mon Sep 17 00:00:00 2001 +From: Sreenivasulu Malavathula +Date: Tue, 10 Jun 2025 12:42:28 -0500 +Subject: [PATCH] Address CVE-2024-45310 +Upstream Patch Reference: https://github.com/opencontainers/runc/commit/f0b652ea61ff6750a8fcc69865d45a7abf37accf, https://github.com/opencontainers/runc/commit/8781993968fd964ac723ff5f360b6f259e809a3e +New Function syscallMode Reference: https://cs.opensource.google/go/go/+/refs/tags/go1.20.7:src/os/file_posix.go;l=61-75 + +--- + .../runc/libcontainer/container_linux.go | 30 +-- + .../runc/libcontainer/mount_linux.go | 18 ++ + .../runc/libcontainer/rootfs_linux.go | 247 +++++++++--------- + .../runc/libcontainer/system/linux.go | 41 +++ + .../runc/libcontainer/utils/utils_unix.go | 129 +++++++++ + 5 files changed, 325 insertions(+), 140 deletions(-) + +diff --git a/vendor/github.com/opencontainers/runc/libcontainer/container_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/container_linux.go +index 3280c66e..7ed98b32 100644 +--- a/vendor/github.com/opencontainers/runc/libcontainer/container_linux.go ++++ b/vendor/github.com/opencontainers/runc/libcontainer/container_linux.go +@@ -1272,8 +1272,7 @@ func (c *linuxContainer) restoreNetwork(req *criurpc.CriuReq, criuOpts *CriuOpts + // restore using CRIU. This function is inspired from the code in + // rootfs_linux.go + func (c *linuxContainer) makeCriuRestoreMountpoints(m *configs.Mount) error { +- switch m.Device { +- case "cgroup": ++ if m.Device == "cgroup" { + // No mount point(s) need to be created: + // + // * for v1, mount points are saved by CRIU because +@@ -1282,28 +1281,13 @@ func (c *linuxContainer) makeCriuRestoreMountpoints(m *configs.Mount) error { + // * for v2, /sys/fs/cgroup is a real mount, but + // the mountpoint appears as soon as /sys is mounted + return nil +- case "bind": +- // The prepareBindMount() function checks if source +- // exists. So it cannot be used for other filesystem types. +- // TODO: pass something else than nil? Not sure if criu is +- // impacted by issue #2484 +- if err := prepareBindMount(m, c.config.Rootfs, nil); err != nil { +- return err +- } +- default: +- // for all other filesystems just create the mountpoints +- dest, err := securejoin.SecureJoin(c.config.Rootfs, m.Destination) +- if err != nil { +- return err +- } +- if err := checkProcMount(c.config.Rootfs, dest, ""); err != nil { +- return err +- } +- if err := os.MkdirAll(dest, 0o755); err != nil { +- return err +- } + } +- return nil ++ // TODO: pass something else than nil? Not sure if criu is ++ // impacted by issue #2484 ++ if _, err := createMountpoint(c.config.Rootfs, m, nil, ""); err != nil { ++ return fmt.Errorf("create criu restore mount for %s mount: %w", m.Destination, err) ++ } ++ return nil + } + + // isPathInPrefixList is a small function for CRIU restore to make sure +diff --git a/vendor/github.com/opencontainers/runc/libcontainer/mount_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/mount_linux.go +index 5f49de96..48600008 100644 +--- a/vendor/github.com/opencontainers/runc/libcontainer/mount_linux.go ++++ b/vendor/github.com/opencontainers/runc/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. ++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/vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go +index c3f88fc7..19b147c5 100644 +--- a/vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go ++++ b/vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go +@@ -224,36 +224,6 @@ func mountCmd(cmd configs.Command) error { + return nil + } + +-func prepareBindMount(m *configs.Mount, rootfs string, mountFd *int) error { +- source := m.Source +- if mountFd != nil { +- source = "/proc/self/fd/" + strconv.Itoa(*mountFd) +- } +- +- stat, err := os.Stat(source) +- if err != nil { +- // error out if the source of a bind mount does not exist as we will be +- // unable to bind anything to it. +- return err +- } +- // ensure that the destination of the bind mount is resolved of symlinks at mount time because +- // any previous mounts can invalidate the next mount's destination. +- // this can happen when a user specifies mounts within other mounts to cause breakouts or other +- // evil stuff to try to escape the container's rootfs. +- var dest string +- if dest, err = securejoin.SecureJoin(rootfs, m.Destination); err != nil { +- return err +- } +- if err := checkProcMount(rootfs, dest, source); err != nil { +- return err +- } +- if err := createIfNotExists(dest, stat.IsDir()); err != nil { +- return err +- } +- +- return nil +-} +- + func mountCgroupV1(m *configs.Mount, c *mountConfig) error { + binds, err := getCgroupMounts(m) + if err != nil { +@@ -282,7 +252,8 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error { + for _, b := range binds { + if c.cgroupns { + subsystemPath := filepath.Join(c.root, b.Destination) +- if err := os.MkdirAll(subsystemPath, 0o755); err != nil { ++ subsystemName := filepath.Base(b.Destination) ++ if err := utils.MkdirAllInRoot(c.root, subsystemPath, 0o755); err != nil { + return err + } + if err := utils.WithProcfd(c.root, b.Destination, func(procfd string) error { +@@ -292,7 +263,7 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error { + } + var ( + source = "cgroup" +- data = filepath.Base(subsystemPath) ++ data = subsystemName + ) + if data == "systemd" { + data = cgroups.CgroupNamePrefix + data +@@ -322,14 +293,7 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error { + } + + func mountCgroupV2(m *configs.Mount, c *mountConfig) error { +- dest, err := securejoin.SecureJoin(c.root, m.Destination) +- if err != nil { +- return err +- } +- if err := os.MkdirAll(dest, 0o755); err != nil { +- return err +- } +- err = utils.WithProcfd(c.root, m.Destination, func(procfd string) error { ++ err := utils.WithProcfd(c.root, m.Destination, func(procfd string) error { + return mount(m.Source, m.Destination, procfd, "cgroup2", uintptr(m.Flags), m.Data) + }) + if err == nil || !(errors.Is(err, unix.EPERM) || errors.Is(err, unix.EBUSY)) { +@@ -411,6 +375,81 @@ func doTmpfsCopyUp(m *configs.Mount, rootfs, mountLabel string) (Err error) { + }) + } + ++var errRootfsToFile = errors.New("config tries to change rootfs to file") ++ ++func createMountpoint(rootfs string, m *configs.Mount, mountFd *int, source string) (string, error) { ++ dest, err := securejoin.SecureJoin(rootfs, m.Destination) ++ if err != nil { ++ return "", err ++ } ++ if err := checkProcMount(rootfs, dest, m, source); err != nil { ++ return "", fmt.Errorf("check proc-safety of %s mount: %w", m.Destination, err) ++ } ++ ++ switch m.Device { ++ case "bind": ++ source := m.Source ++ if mountFd != nil { ++ source = "/proc/self/fd/" + strconv.Itoa(*mountFd) ++ } ++ ++ fi, err := os.Stat(source) ++ if err != nil { ++ // Error out if the source of a bind mount does not exist as we ++ // will be unable to bind anything to it. ++ return "", fmt.Errorf("bind mount source stat: %w", err) ++ } ++ // If the original source is not a directory, make the target a file. ++ if !fi.IsDir() { ++ // Make sure we aren't tricked into trying to make the root a file. ++ if rootfs == dest { ++ return "", fmt.Errorf("%w: file bind mount over rootfs", errRootfsToFile) ++ } ++ // Make the parent directory. ++ destDir, destBase := filepath.Split(dest) ++ destDirFd, err := utils.MkdirAllInRootOpen(rootfs, destDir, 0o755) ++ if err != nil { ++ return "", fmt.Errorf("make parent dir of file bind-mount: %w", err) ++ } ++ defer destDirFd.Close() ++ // Make the target file. We want to avoid opening any file that is ++ // already there because it could be a "bad" file like an invalid ++ // device or hung tty that might cause a DoS, so we use mknodat. ++ // destBase does not contain any "/" components, and mknodat does ++ // not follow trailing symlinks, so we can safely just call mknodat ++ // here. ++ if err := unix.Mknodat(int(destDirFd.Fd()), destBase, unix.S_IFREG|0o644, 0); err != nil { ++ // If we get EEXIST, there was already an inode there and ++ // we can consider that a success. ++ if !errors.Is(err, unix.EEXIST) { ++ err = &os.PathError{Op: "mknod regular file", Path: dest, Err: err} ++ return "", fmt.Errorf("create target of file bind-mount: %w", err) ++ } ++ } ++ // Nothing left to do. ++ return dest, nil ++ } ++ ++ case "tmpfs": ++ // If the original target exists, copy the mode for the tmpfs mount. ++ if stat, err := os.Stat(dest); err == nil { ++ dt := fmt.Sprintf("mode=%04o", syscallMode(stat.Mode())) ++ if m.Data != "" { ++ dt = dt + "," + m.Data ++ } ++ m.Data = dt ++ ++ // Nothing left to do. ++ return dest, nil ++ } ++ } ++ ++ if err := utils.MkdirAllInRoot(rootfs, dest, 0o755); err != nil { ++ return "", err ++ } ++ return dest, nil ++} ++ + func mountToRootfs(m *configs.Mount, c *mountConfig) error { + rootfs := c.root + +@@ -435,57 +474,34 @@ func mountToRootfs(m *configs.Mount, c *mountConfig) error { + } else if !fi.IsDir() { + return fmt.Errorf("filesystem %q must be mounted on ordinary directory", m.Device) + } +- if err := os.MkdirAll(dest, 0o755); err != nil { ++ if err := utils.MkdirAllInRoot(rootfs, dest, 0o755); err != nil { + return err + } + // Selinux kernels do not support labeling of /proc or /sys. + return mountPropagate(m, rootfs, "", nil) + } + +- mountLabel := c.label + mountFd := c.fd +- dest, err := securejoin.SecureJoin(rootfs, m.Destination) ++ dest, err := createMountpoint(rootfs, m, mountFd, m.Source) + if err != nil { +- return err +- } ++ return fmt.Errorf("create mount destination for %s mount: %w", m.Destination, err) ++ } ++ mountLabel := c.label + + switch m.Device { + case "mqueue": +- if err := os.MkdirAll(dest, 0o755); err != nil { +- return err +- } + if err := mountPropagate(m, rootfs, "", nil); err != nil { + return err + } + return label.SetFileLabel(dest, mountLabel) + case "tmpfs": +- stat, err := os.Stat(dest) +- if err != nil { +- if err := os.MkdirAll(dest, 0o755); err != nil { +- return err +- } +- } +- + if m.Extensions&configs.EXT_COPYUP == configs.EXT_COPYUP { + err = doTmpfsCopyUp(m, rootfs, mountLabel) + } else { + err = mountPropagate(m, rootfs, mountLabel, nil) + } +- +- if err != nil { +- return err +- } +- +- if stat != nil { +- if err = os.Chmod(dest, stat.Mode()); err != nil { +- return err +- } +- } +- return nil ++ return err + case "bind": +- if err := prepareBindMount(m, rootfs, mountFd); err != nil { +- return err +- } + if err := mountPropagate(m, rootfs, mountLabel, mountFd); err != nil { + return err + } +@@ -513,12 +529,6 @@ func mountToRootfs(m *configs.Mount, c *mountConfig) error { + } + return mountCgroupV1(m, c) + default: +- if err := checkProcMount(rootfs, dest, m.Source); err != nil { +- return err +- } +- if err := os.MkdirAll(dest, 0o755); err != nil { +- return err +- } + return mountPropagate(m, rootfs, mountLabel, mountFd) + } + if err := setRecAttr(m, rootfs); err != nil { +@@ -561,11 +571,17 @@ func getCgroupMounts(m *configs.Mount) ([]*configs.Mount, error) { + return binds, nil + } + +-// checkProcMount checks to ensure that the mount destination is not over the top of /proc. +-// dest is required to be an abs path and have any symlinks resolved before calling this function. ++// Taken from . If a file is on a filesystem of type ++// PROC_SUPER_MAGIC, we're guaranteed that only the root of the superblock will ++// have this inode number. ++const procRootIno = 1 ++ ++// checkProcMount checks to ensure that the mount destination is not over the ++// top of /proc. dest is required to be an abs path and have any symlinks ++// resolved before calling this function. + // +-// if source is nil, don't stat the filesystem. This is used for restore of a checkpoint. +-func checkProcMount(rootfs, dest, source string) error { ++// source is "" when doing criu restores. ++func checkProcMount(rootfs, dest string, m *configs.Mount, source string) error { + const procPath = "/proc" + path, err := filepath.Rel(filepath.Join(rootfs, procPath), dest) + if err != nil { +@@ -576,18 +592,39 @@ func checkProcMount(rootfs, dest, source string) error { + return nil + } + if path == "." { +- // an empty source is pasted on restore ++ // Skip this check for criu restores. ++ // NOTE: This is a special case kept from the original implementation, ++ // only present for the 1.1.z branch to avoid any possible breakage in ++ // a patch release. This check was removed in commit cdff09ab8751 ++ // ("rootfs: fix 'can we mount on top of /proc' check") in 1.2, because ++ // it doesn't make sense with the new IsBind()-based checks. + if source == "" { + return nil + } +- // only allow a mount on-top of proc if it's source is "proc" +- isproc, err := isProc(source) +- if err != nil { +- return err +- } +- // pass if the mount is happening on top of /proc and the source of +- // the mount is a proc filesystem +- if isproc { ++ // Only allow bind-mounts on top of /proc, and only if the source is a ++ // procfs mount. ++ if m.IsBind() { ++ var fsSt unix.Statfs_t ++ if err := unix.Statfs(source, &fsSt); err != nil { ++ return &os.PathError{Op: "statfs", Path: source, Err: err} ++ } ++ if fsSt.Type == unix.PROC_SUPER_MAGIC { ++ var uSt unix.Stat_t ++ if err := unix.Stat(source, &uSt); err != nil { ++ return &os.PathError{Op: "stat", Path: source, Err: err} ++ } ++ if uSt.Ino != procRootIno { ++ // We cannot error out in this case, because we've ++ // supported these kinds of mounts for a long time. ++ // However, we would expect users to bind-mount the root of ++ // a real procfs on top of /proc in the container. We might ++ // want to block this in the future. ++ logrus.Warnf("bind-mount %v (source %v) is of type procfs but is not the root of a procfs (inode %d). Future versions of runc might block this configuration -- please report an issue to if you see this warning.", dest, source, uSt.Ino) ++ } ++ return nil ++ } ++ } else if m.Device == "proc" { ++ // Fresh procfs-type mounts are always safe to mount on top of /proc. + return nil + } + return fmt.Errorf("%q cannot be mounted because it is not of type proc", dest) +@@ -606,6 +643,7 @@ func checkProcMount(rootfs, dest, source string) error { + "/proc/slabinfo", + "/proc/net/dev", + "/proc/sys/kernel/ns_last_pid", ++ "/proc/sys/crypto/fips_enabled", + } + for _, valid := range validProcMounts { + path, err := filepath.Rel(filepath.Join(rootfs, valid), dest) +@@ -620,14 +658,6 @@ func checkProcMount(rootfs, dest, source string) error { + return fmt.Errorf("%q cannot be mounted because it is inside /proc", dest) + } + +-func isProc(path string) (bool, error) { +- var s unix.Statfs_t +- if err := unix.Statfs(path, &s); err != nil { +- return false, &os.PathError{Op: "statfs", Path: path, Err: err} +- } +- return s.Type == unix.PROC_SUPER_MAGIC, nil +-} +- + func setupDevSymlinks(rootfs string) error { + links := [][2]string{ + {"/proc/self/fd", "/dev/fd"}, +@@ -729,7 +759,10 @@ func createDeviceNode(rootfs string, node *devices.Device, bind bool) error { + if err != nil { + return err + } +- if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil { ++ if dest == rootfs { ++ return fmt.Errorf("%w: mknod over rootfs", errRootfsToFile) ++ } ++ if err := utils.MkdirAllInRoot(rootfs, filepath.Dir(dest), 0o755); err != nil { + return err + } + if bind { +@@ -995,26 +1028,6 @@ func chroot() error { + return nil + } + +-// createIfNotExists creates a file or a directory only if it does not already exist. +-func createIfNotExists(path string, isDir bool) error { +- if _, err := os.Stat(path); err != nil { +- if os.IsNotExist(err) { +- if isDir { +- return os.MkdirAll(path, 0o755) +- } +- if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { +- return err +- } +- f, err := os.OpenFile(path, os.O_CREATE, 0o755) +- if err != nil { +- return err +- } +- _ = f.Close() +- } +- } +- return nil +-} +- + // readonlyPath will make a path read only. + func readonlyPath(path string) error { + if err := mount(path, path, "", "", unix.MS_BIND|unix.MS_REC, ""); err != nil { +diff --git a/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go b/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go +index e1d6eb18..0f970452 100644 +--- a/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go ++++ b/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go +@@ -6,6 +6,8 @@ package system + import ( + "os" + "os/exec" ++ "runtime" ++ "strings" + "unsafe" + + "golang.org/x/sys/unix" +@@ -102,3 +104,42 @@ func GetSubreaper() (int, error) { + + return int(i), nil + } ++ ++func prepareAt(dir *os.File, path string) (int, string) { ++ if dir == nil { ++ return unix.AT_FDCWD, path ++ } ++ ++ // Rather than just filepath.Join-ing path here, do it manually so the ++ // error and handle correctly indicate cases like path=".." as being ++ // relative to the correct directory. The handle.Name() might end up being ++ // wrong but because this is (currently) only used in MkdirAllInRoot, that ++ // isn't a problem. ++ dirName := dir.Name() ++ if !strings.HasSuffix(dirName, "/") { ++ dirName += "/" ++ } ++ fullPath := dirName + path ++ ++ return int(dir.Fd()), fullPath ++} ++ ++func Openat(dir *os.File, path string, flags int, mode uint32) (*os.File, error) { ++ dirFd, fullPath := prepareAt(dir, path) ++ fd, err := unix.Openat(dirFd, path, flags, mode) ++ if err != nil { ++ return nil, &os.PathError{Op: "openat", Path: fullPath, Err: err} ++ } ++ runtime.KeepAlive(dir) ++ return os.NewFile(uintptr(fd), fullPath), nil ++} ++ ++func Mkdirat(dir *os.File, path string, mode uint32) error { ++ dirFd, fullPath := prepareAt(dir, path) ++ err := unix.Mkdirat(dirFd, path, mode) ++ if err != nil { ++ err = &os.PathError{Op: "mkdirat", Path: fullPath, Err: err} ++ } ++ runtime.KeepAlive(dir) ++ return err ++} +diff --git a/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go b/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go +index bf3237a2..460b94ce 100644 +--- a/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go ++++ b/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go +@@ -4,11 +4,17 @@ + package utils + + import ( ++ "errors" + "fmt" + "os" ++ "path/filepath" + "strconv" ++ "strings" + _ "unsafe" // for go:linkname + ++ "github.com/opencontainers/runc/libcontainer/system" ++ ++ securejoin "github.com/cyphar/filepath-securejoin" + "golang.org/x/sys/unix" + ) + +@@ -115,3 +121,126 @@ func NewSockPair(name string) (parent *os.File, child *os.File, err error) { + } + return os.NewFile(uintptr(fds[1]), name+"-p"), os.NewFile(uintptr(fds[0]), name+"-c"), nil + } ++ ++// IsLexicallyInRoot is shorthand for strings.HasPrefix(path+"/", root+"/"), ++// but properly handling the case where path or root are "/". ++// ++// NOTE: The return value only make sense if the path doesn't contain "..". ++func IsLexicallyInRoot(root, path string) bool { ++ if root != "/" { ++ root += "/" ++ } ++ if path != "/" { ++ path += "/" ++ } ++ return strings.HasPrefix(path, root) ++} ++ ++// MkdirAllInRootOpen attempts to make ++// ++// path, _ := securejoin.SecureJoin(root, unsafePath) ++// os.MkdirAll(path, mode) ++// os.Open(path) ++// ++// safer against attacks where components in the path are changed between ++// SecureJoin returning and MkdirAll (or Open) being called. In particular, we ++// try to detect any symlink components in the path while we are doing the ++// MkdirAll. ++// ++// NOTE: Unlike os.MkdirAll, mode is not Go's os.FileMode, it is the unix mode ++// (the suid/sgid/sticky bits are not the same as for os.FileMode). ++// ++// NOTE: If unsafePath is a subpath of root, we assume that you have already ++// called SecureJoin and so we use the provided path verbatim without resolving ++// any symlinks (this is done in a way that avoids symlink-exchange races). ++// This means that the path also must not contain ".." elements, otherwise an ++// error will occur. ++// ++// This is a somewhat less safe alternative to ++// , but it should ++// detect attempts to trick us into creating directories outside of the root. ++// We should migrate to securejoin.MkdirAll once it is merged. ++func MkdirAllInRootOpen(root, unsafePath string, mode uint32) (_ *os.File, Err error) { ++ // If the path is already "within" the root, use it verbatim. ++ fullPath := unsafePath ++ if !IsLexicallyInRoot(root, unsafePath) { ++ var err error ++ fullPath, err = securejoin.SecureJoin(root, unsafePath) ++ if err != nil { ++ return nil, err ++ } ++ } ++ subPath, err := filepath.Rel(root, fullPath) ++ if err != nil { ++ return nil, err ++ } ++ ++ // Check for any silly mode bits. ++ if mode&^0o7777 != 0 { ++ return nil, fmt.Errorf("tried to include non-mode bits in MkdirAll mode: 0o%.3o", mode) ++ } ++ ++ currentDir, err := os.OpenFile(root, unix.O_DIRECTORY|unix.O_CLOEXEC, 0) ++ if err != nil { ++ return nil, fmt.Errorf("open root handle: %w", err) ++ } ++ defer func() { ++ if Err != nil { ++ currentDir.Close() ++ } ++ }() ++ ++ for _, part := range strings.Split(subPath, string(filepath.Separator)) { ++ switch part { ++ case "", ".": ++ // Skip over no-op components. ++ continue ++ case "..": ++ return nil, fmt.Errorf("possible breakout detected: found %q component in SecureJoin subpath %s", part, subPath) ++ } ++ ++ nextDir, err := system.Openat(currentDir, part, unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) ++ switch { ++ case err == nil: ++ // Update the currentDir. ++ _ = currentDir.Close() ++ currentDir = nextDir ++ ++ case errors.Is(err, unix.ENOTDIR): ++ // This might be a symlink or some other random file. Either way, ++ // error out. ++ return nil, fmt.Errorf("cannot mkdir in %s/%s: %w", currentDir.Name(), part, unix.ENOTDIR) ++ ++ case errors.Is(err, os.ErrNotExist): ++ // Luckily, mkdirat will not follow trailing symlinks, so this is ++ // safe to do as-is. ++ if err := system.Mkdirat(currentDir, part, mode); err != nil { ++ return nil, err ++ } ++ // Open the new directory. There is a race here where an attacker ++ // could swap the directory with a different directory, but ++ // MkdirAll's fuzzy semantics mean we don't care about that. ++ nextDir, err := system.Openat(currentDir, part, unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) ++ if err != nil { ++ return nil, fmt.Errorf("open newly created directory: %w", err) ++ } ++ // Update the currentDir. ++ _ = currentDir.Close() ++ currentDir = nextDir ++ ++ default: ++ return nil, err ++ } ++ } ++ return currentDir, nil ++} ++ ++// MkdirAllInRoot is a wrapper around MkdirAllInRootOpen which closes the ++// returned handle, for callers that don't need to use it. ++func MkdirAllInRoot(root, unsafePath string, mode uint32) error { ++ f, err := MkdirAllInRootOpen(root, unsafePath, mode) ++ if err == nil { ++ _ = f.Close() ++ } ++ return err ++} +-- +2.45.2 + diff --git a/SPECS/kubernetes/kubernetes.spec b/SPECS/kubernetes/kubernetes.spec index ce29e33d4d1..7d072f85600 100644 --- a/SPECS/kubernetes/kubernetes.spec +++ b/SPECS/kubernetes/kubernetes.spec @@ -10,7 +10,7 @@ Summary: Microsoft Kubernetes Name: kubernetes Version: 1.28.4 -Release: 19%{?dist} +Release: 20%{?dist} License: ASL 2.0 Vendor: Microsoft Corporation Distribution: Mariner @@ -32,6 +32,7 @@ Patch10: CVE-2025-22869.patch Patch11: CVE-2025-30204.patch Patch12: CVE-2024-51744.patch Patch13: CVE-2025-22872.patch +Patch14: CVE-2024-45310.patch BuildRequires: flex-devel BuildRequires: glibc-static >= 2.35-7%{?dist} BuildRequires: golang @@ -277,6 +278,9 @@ fi %{_exec_prefix}/local/bin/pause %changelog +* Thu Sep 10 2025 Sreeniavsulu Malavathula - 1.28.4-20 +- Patch CVE-2024-45310 + * Thu Sep 04 2025 Akhila Guruju - 1.28.4-19 - Bump release to rebuild with golang