From ce3ae3b51afe97e6d6b2c7aec1ff26dbbeb67e9f Mon Sep 17 00:00:00 2001 From: puhongyu Date: Mon, 22 Sep 2025 16:31:04 +0800 Subject: [PATCH] containers/storage to v1.51.2 to fix CVE-2024-9676 --- 0006-Fix-CVE-2024-9676.patch | 305 +++++++++++++++++++++++++++++++++++ skopeo.spec | 9 +- 2 files changed, 313 insertions(+), 1 deletion(-) create mode 100644 0006-Fix-CVE-2024-9676.patch diff --git a/0006-Fix-CVE-2024-9676.patch b/0006-Fix-CVE-2024-9676.patch new file mode 100644 index 0000000..1608a4c --- /dev/null +++ b/0006-Fix-CVE-2024-9676.patch @@ -0,0 +1,305 @@ +From f1f2f5ca9b7bca720e99df6fbf2a966095e8ae3f Mon Sep 17 00:00:00 2001 +From: Giuseppe Scrivano , Matt Heon +Date: Mon, 22 Sep 2025 15:57:43 +0800 +Subject: [PATCH] Fix-CVE-2024-9676 + +--- + go.mod | 2 +- + go.sum | 4 +- + .../github.com/containers/storage/.cirrus.yml | 2 +- + vendor/github.com/containers/storage/VERSION | 2 +- + .../storage/drivers/overlay/overlay.go | 42 +++++++-- + .../github.com/containers/storage/userns.go | 92 +++++++++++++------ + .../containers/storage/userns_unsupported.go | 14 +++ + vendor/modules.txt | 2 +- + 8 files changed, 118 insertions(+), 42 deletions(-) + create mode 100644 vendor/github.com/containers/storage/userns_unsupported.go + +diff --git a/go.mod b/go.mod +index 79c2046..c92f972 100644 +--- a/go.mod ++++ b/go.mod +@@ -6,7 +6,7 @@ require ( + github.com/containers/common v0.57.3 + github.com/containers/image/v5 v5.29.5 + github.com/containers/ocicrypt v1.1.9 +- github.com/containers/storage v1.51.0 ++ github.com/containers/storage v1.51.2 + github.com/docker/distribution v2.8.3+incompatible + github.com/opencontainers/go-digest v1.0.0 + github.com/opencontainers/image-spec v1.1.0-rc5 +diff --git a/go.sum b/go.sum +index e3e7f06..8acba12 100644 +--- a/go.sum ++++ b/go.sum +@@ -38,8 +38,8 @@ github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYgle + github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= + github.com/containers/ocicrypt v1.1.9 h1:2Csfba4jse85Raxk5HIyEk8OwZNjRvfkhEGijOjIdEM= + github.com/containers/ocicrypt v1.1.9/go.mod h1:dTKx1918d8TDkxXvarscpNVY+lyPakPNFN4jwA9GBys= +-github.com/containers/storage v1.51.0 h1:AowbcpiWXzAjHosKz7MKvPEqpyX+ryZA/ZurytRrFNA= +-github.com/containers/storage v1.51.0/go.mod h1:ybl8a3j1PPtpyaEi/5A6TOFs+5TrEyObeKJzVtkUlfc= ++github.com/containers/storage v1.51.2 h1:Xw8p1AG1A+Nh6dCsb1UOB3YKF5uzlCkI3uAP4fsFup4= ++github.com/containers/storage v1.51.2/go.mod h1:ybl8a3j1PPtpyaEi/5A6TOFs+5TrEyObeKJzVtkUlfc= + github.com/coreos/go-oidc/v3 v3.7.0 h1:FTdj0uexT4diYIPlF4yoFVI5MRO1r5+SEcIpEw9vC0o= + github.com/coreos/go-oidc/v3 v3.7.0/go.mod h1:yQzSCqBnK3e6Fs5l+f5i0F8Kwf0zpH9bPEsbY00KanM= + github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +diff --git a/vendor/github.com/containers/storage/.cirrus.yml b/vendor/github.com/containers/storage/.cirrus.yml +index c41dd5d..9e61509 100644 +--- a/vendor/github.com/containers/storage/.cirrus.yml ++++ b/vendor/github.com/containers/storage/.cirrus.yml +@@ -119,7 +119,7 @@ lint_task: + env: + CIRRUS_WORKING_DIR: "/go/src/github.com/containers/storage" + container: +- image: golang ++ image: golang:1.19 + modules_cache: + fingerprint_script: cat go.sum + folder: $GOPATH/pkg/mod +diff --git a/vendor/github.com/containers/storage/VERSION b/vendor/github.com/containers/storage/VERSION +index ba0a719..aa618f0 100644 +--- a/vendor/github.com/containers/storage/VERSION ++++ b/vendor/github.com/containers/storage/VERSION +@@ -1 +1 @@ +-1.51.0 ++1.51.2 +diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay.go b/vendor/github.com/containers/storage/drivers/overlay/overlay.go +index 04ecf87..08532fb 100644 +--- a/vendor/github.com/containers/storage/drivers/overlay/overlay.go ++++ b/vendor/github.com/containers/storage/drivers/overlay/overlay.go +@@ -1670,13 +1670,21 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO + } + + if err := idtools.MkdirAllAs(diffDir, perms, rootUID, rootGID); err != nil { +- return "", err ++ if !inAdditionalStore { ++ return "", err ++ } ++ // if it is in an additional store, do not fail if the directory already exists ++ if _, err2 := os.Stat(diffDir); err2 != nil { ++ return "", err ++ } + } + + mergedDir := path.Join(workDirBase, "merged") +- // Create the driver merged dir +- if err := idtools.MkdirAs(mergedDir, 0o700, rootUID, rootGID); err != nil && !os.IsExist(err) { +- return "", err ++ // Attempt to create the merged dir only if it doesn't exist. ++ if _, err := os.Stat(mergedDir); err != nil && os.IsNotExist(err) { ++ if err := idtools.MkdirAs(mergedDir, 0o700, rootUID, rootGID); err != nil && !os.IsExist(err) { ++ return "", err ++ } + } + if count := d.ctr.Increment(mergedDir); count > 1 { + return mergedDir, nil +@@ -1841,7 +1849,7 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO + + // Put unmounts the mount path created for the give id. + func (d *Driver) Put(id string) error { +- dir := d.dir(id) ++ dir, _, inAdditionalStore := d.dir2(id) + if _, err := os.Stat(dir); err != nil { + return err + } +@@ -1902,11 +1910,27 @@ func (d *Driver) Put(id string) error { + } + } + +- if err := unix.Rmdir(mountpoint); err != nil && !os.IsNotExist(err) { +- logrus.Debugf("Failed to remove mountpoint %s overlay: %s - %v", id, mountpoint, err) +- return fmt.Errorf("removing mount point %q: %w", mountpoint, err) +- } ++ if !inAdditionalStore { ++ uid, gid := int(0), int(0) ++ fi, err := os.Stat(mountpoint) ++ if err != nil { ++ return err ++ } ++ if stat, ok := fi.Sys().(*syscall.Stat_t); ok { ++ uid, gid = int(stat.Uid), int(stat.Gid) ++ } + ++ tmpMountpoint := path.Join(dir, "merged.1") ++ if err := idtools.MkdirAs(tmpMountpoint, 0o700, uid, gid); err != nil && !errors.Is(err, os.ErrExist) { ++ return err ++ } ++ // rename(2) can be used on an empty directory, as it is the mountpoint after umount, and it retains ++ // its atomic semantic. In this way the "merged" directory is never removed. ++ if err := unix.Rename(tmpMountpoint, mountpoint); err != nil { ++ logrus.Debugf("Failed to replace mountpoint %s overlay: %s - %v", id, mountpoint, err) ++ return fmt.Errorf("replacing mount point %q: %w", mountpoint, err) ++ } ++ } + return nil + } + +diff --git a/vendor/github.com/containers/storage/userns.go b/vendor/github.com/containers/storage/userns.go +index 32ae830..2c855da 100644 +--- a/vendor/github.com/containers/storage/userns.go ++++ b/vendor/github.com/containers/storage/userns.go +@@ -1,18 +1,21 @@ ++//go:build linux ++ + package storage + + import ( + "fmt" + "os" + "os/user" +- "path/filepath" + "strconv" + + drivers "github.com/containers/storage/drivers" + "github.com/containers/storage/pkg/idtools" + "github.com/containers/storage/pkg/unshare" + "github.com/containers/storage/types" ++ securejoin "github.com/cyphar/filepath-securejoin" + libcontainerUser "github.com/opencontainers/runc/libcontainer/user" + "github.com/sirupsen/logrus" ++ "golang.org/x/sys/unix" + ) + + // getAdditionalSubIDs looks up the additional IDs configured for +@@ -85,40 +88,59 @@ const nobodyUser = 65534 + // parseMountedFiles returns the maximum UID and GID found in the /etc/passwd and + // /etc/group files. + func parseMountedFiles(containerMount, passwdFile, groupFile string) uint32 { ++ var ( ++ passwd *os.File ++ group *os.File ++ size int ++ err error ++ ) + if passwdFile == "" { +- passwdFile = filepath.Join(containerMount, "etc/passwd") +- } +- if groupFile == "" { +- groupFile = filepath.Join(groupFile, "etc/group") ++ passwd, err = secureOpen(containerMount, "/etc/passwd") ++ } else { ++ // User-specified override from a volume. Will not be in ++ // container root. ++ passwd, err = os.Open(passwdFile) + } +- +- size := 0 +- +- users, err := libcontainerUser.ParsePasswdFile(passwdFile) + if err == nil { +- for _, u := range users { +- // Skip the "nobody" user otherwise we end up with 65536 +- // ids with most images +- if u.Name == "nobody" { +- continue +- } +- if u.Uid > size && u.Uid != nobodyUser { +- size = u.Uid +- } +- if u.Gid > size && u.Gid != nobodyUser { +- size = u.Gid ++ defer passwd.Close() ++ ++ users, err := libcontainerUser.ParsePasswd(passwd) ++ if err == nil { ++ for _, u := range users { ++ // Skip the "nobody" user otherwise we end up with 65536 ++ // ids with most images ++ if u.Name == "nobody" || u.Name == "nogroup" { ++ continue ++ } ++ if u.Uid > size && u.Uid != nobodyUser { ++ size = u.Uid + 1 ++ } ++ if u.Gid > size && u.Gid != nobodyUser { ++ size = u.Gid + 1 ++ } + } + } + } + +- groups, err := libcontainerUser.ParseGroupFile(groupFile) ++ if groupFile == "" { ++ group, err = secureOpen(containerMount, "/etc/group") ++ } else { ++ // User-specified override from a volume. Will not be in ++ // container root. ++ group, err = os.Open(groupFile) ++ } + if err == nil { +- for _, g := range groups { +- if g.Name == "nobody" { +- continue +- } +- if g.Gid > size && g.Gid != nobodyUser { +- size = g.Gid ++ defer group.Close() ++ ++ groups, err := libcontainerUser.ParseGroup(group) ++ if err == nil { ++ for _, g := range groups { ++ if g.Name == "nobody" || g.Name == "nogroup" { ++ continue ++ } ++ if g.Gid > size && g.Gid != nobodyUser { ++ size = g.Gid + 1 ++ } + } + } + } +@@ -309,3 +331,19 @@ func getAutoUserNSIDMappings( + gidMap := append(availableGIDs.zip(requestedContainerGIDs), additionalGIDMappings...) + return uidMap, gidMap, nil + } ++ ++// Securely open (read-only) a file in a container mount. ++func secureOpen(containerMount, file string) (*os.File, error) { ++ filePath, err := securejoin.SecureJoin(containerMount, file) ++ if err != nil { ++ return nil, err ++ } ++ ++ flags := unix.O_PATH | unix.O_CLOEXEC | unix.O_RDONLY ++ fileHandle, err := os.OpenFile(filePath, flags, 0) ++ if err != nil { ++ return nil, err ++ } ++ ++ return fileHandle, nil ++} +diff --git a/vendor/github.com/containers/storage/userns_unsupported.go b/vendor/github.com/containers/storage/userns_unsupported.go +new file mode 100644 +index 0000000..e37c18f +--- /dev/null ++++ b/vendor/github.com/containers/storage/userns_unsupported.go +@@ -0,0 +1,14 @@ ++//go:build !linux ++ ++package storage ++ ++import ( ++ "errors" ++ ++ "github.com/containers/storage/pkg/idtools" ++ "github.com/containers/storage/types" ++) ++ ++func (s *store) getAutoUserNS(_ *types.AutoUserNsOptions, _ *Image, _ rwLayerStore, _ []roLayerStore) ([]idtools.IDMap, []idtools.IDMap, error) { ++ return nil, nil, errors.New("user namespaces are not supported on this platform") ++} +diff --git a/vendor/modules.txt b/vendor/modules.txt +index 32edf7a..6dd226c 100644 +--- a/vendor/modules.txt ++++ b/vendor/modules.txt +@@ -161,7 +161,7 @@ github.com/containers/ocicrypt/keywrap/pkcs7 + github.com/containers/ocicrypt/spec + github.com/containers/ocicrypt/utils + github.com/containers/ocicrypt/utils/keyprovider +-# github.com/containers/storage v1.51.0 ++# github.com/containers/storage v1.51.2 + ## explicit; go 1.19 + github.com/containers/storage + github.com/containers/storage/drivers +-- +2.43.5 + diff --git a/skopeo.spec b/skopeo.spec index 67eeb76..1164f4f 100644 --- a/skopeo.spec +++ b/skopeo.spec @@ -12,7 +12,7 @@ ExcludeArch: ppc64 Name: skopeo Epoch: 1 Version: 1.14.2 -Release: 6 +Release: 7 Summary: Work with remote images registries - retrieving information, images, signing content License: Apache-2.0 URL: https://github.com/containers/skopeo @@ -24,6 +24,7 @@ Patch0002: 0002-fix-CVE-2024-28180.patch Patch0003: 0003-add-sw64-support.patch Patch0004: 0004-Bump-containers-image-v5-to-v5.29.5.patch Patch0005: 0005-fix-CVE-2025-27144.patch +Patch0006: 0006-Fix-CVE-2024-9676.patch BuildRequires: go-srpm-macros git-core pkgconfig(devmapper) make BuildRequires: golang >= 1.19 @@ -131,6 +132,12 @@ cp -pav systemtest/* %{buildroot}/%{_datadir}/%{name}/test/system/ %{_datadir}/%{name}/test %changelog +* Mon Sep 22 2025 Hongyu Pu - 1:1.14.2-7 +- Type:cve +- CVE:CVE-2024-9676 +- SUG:NA +- DESC: containers/storage to v1.51.2 to fix CVE-2024-9676 + * Mon Aug 25 2025 Jianmin - 1:1.14.2-6 - Fix CVE-2025-27144 -- Gitee