[meta-virtualization][wrynose][PATCH] runc: fix CVE-2026-41579
"Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)" <[email protected]> Fri, 10 Jul 2026 17:55:53 +0530
| Newsgroups | org.yoctoproject.lists.meta-virtualization |
|---|---|
| Message-ID | <[email protected]> |
From: Deepak Rathore <[email protected]> This patch applies the upstream release-1.5 backport for CVE-2026-41579. The upstream fix commit is referenced in [1], and the public CVE advisory is referenced in [2]. [1] https://github.com/opencontainers/runc/commit/4504accbbaebd198f1fce296b39cf0d6f20a17e1 [2] https://github.com/opencontainers/runc/security/advisories/GHSA-xjvp-4fhw-gc47 Signed-off-by: Deepak Rathore <[email protected]> --- .../runc/files/CVE-2026-41579.patch | 182 ++++++++++++++++++ recipes-containers/runc/runc_git.bb | 1 + 2 files changed, 183 insertions(+) create mode 100644 recipes-containers/runc/files/CVE-2026-41579.patch diff --git a/recipes-containers/runc/files/CVE-2026-41579.patch b/recipes-containers/runc/files/CVE-2026-41579.patch new file mode 100644 index 00000000..f5adbba5 --- /dev/null +++ b/recipes-containers/runc/files/CVE-2026-41579.patch @@ -0,0 +1,182 @@ +From eaee40282ce6a653e4bfd1a34f74fd6c988ea005 Mon Sep 17 00:00:00 2001 +From: Aleksa Sarai <[email protected]> +Date: Wed, 15 Apr 2026 01:44:09 +1000 +Subject: [PATCH] rootfs: make /dev initialisation code fd-based + +These codepaths are very old and operate on pure paths but before +pivot_root(2), meaning that a bad image with a malicious /dev symlink +could cause us to operate on host paths instead. + +In practice this means that we could be tricked into removing a file +called "ptmx" (note that /dev/pts/ptmx and /dev/ptmx are both immune for +different reasons) or creating a very restricted set of symlinks (with +fixed targets and names). The scope of these bugs is thus quite limited, +but we definitely need to harden against it. + +These codepaths were unfortunately missed during the fd-based rework in +commit d40b3439a961 ("rootfs: switch to fd-based handling of mountpoint +targets") -- I must've assumed they were called after pivot_root(2)... + +Fixes: GHSA-xjvp-4fhw-gc47 +Fixes: CVE-2026-41579 +Fixes: d40b3439a961 ("rootfs: switch to fd-based handling of mountpoint targets") + +CVE: CVE-2026-41579 +Upstream-Status: Backport [https://github.com/opencontainers/runc/commit/4504accbbaebd198f1fce296b39cf0d6f20a17e1] + +Backport Changes: +- Wrynose runc 1.5.0-rc.1 still uses rootfs string pathrs + helpers rather than the newer root *os.File API expected by the + upstream release-1.5 fix. +- Kept fd-scoped unlinkat/symlinkat behavior by adding helpers that + use Wrynose's string-based OpenInRoot and MkdirAllParentInRoot APIs. + +Signed-off-by: Aleksa Sarai <[email protected]> +(cherry picked from commit 864db8042dbb191028676f80addf8c35f348aee2) +Signed-off-by: Aleksa Sarai <[email protected]> +(cherry picked from commit 4504accbbaebd198f1fce296b39cf0d6f20a17e1) +Signed-off-by: Deepak Rathore <[email protected]> +--- + internal/pathrs/mkdirall.go | 14 +++++++--- + internal/pathrs/root_pathrslite.go | 41 ++++++++++++++++++++++++++++++ + libcontainer/rootfs_linux.go | 19 +++++--------- + 3 files changed, 58 insertions(+), 16 deletions(-) + +diff --git a/src/import/internal/pathrs/mkdirall.go b/src/import/internal/pathrs/mkdirall.go +index 3a896f484..52fb97797 100644 +--- a/src/import/internal/pathrs/mkdirall.go ++++ b/src/import/internal/pathrs/mkdirall.go +@@ -24,6 +24,14 @@ import ( + "path/filepath" + ) + ++func splitPath(path string) (dirPath, filename string, err error) { ++ dirPath, filename = filepath.Split(path) ++ if filepath.Join("/", filename) == "/" { ++ return "", "", fmt.Errorf("root subpath %q has bad trailing component %q", path, filename) ++ } ++ return dirPath, filename, nil ++} ++ + // MkdirAllParentInRoot is like [MkdirAllInRoot] except that it only creates + // the parent directory of the target path, returning the trailing component so + // the caller has more flexibility around constructing the final inode. +@@ -41,9 +49,9 @@ func MkdirAllParentInRoot(root, unsafePath string, mode os.FileMode) (*os.File, + return nil, "", fmt.Errorf("failed to construct hallucinated target path: %w", err) + } + +- dirPath, filename := filepath.Split(unsafePath) +- if filepath.Join("/", filename) == "/" { +- return nil, "", fmt.Errorf("create parent dir in root subpath %q has bad trailing component %q", unsafePath, filename) ++ dirPath, filename, err := splitPath(unsafePath) ++ if err != nil { ++ return nil, "", fmt.Errorf("split path %q for mkdir parent: %w", unsafePath, err) + } + + dirFd, err := MkdirAllInRoot(root, dirPath, mode) +diff --git a/src/import/internal/pathrs/root_pathrslite.go b/src/import/internal/pathrs/root_pathrslite.go +index 51db77440..a925c0bd9 100644 +--- a/src/import/internal/pathrs/root_pathrslite.go ++++ b/src/import/internal/pathrs/root_pathrslite.go +@@ -19,7 +19,9 @@ + package pathrs + + import ( ++ "fmt" + "os" ++ "path/filepath" + + "github.com/cyphar/filepath-securejoin/pathrs-lite" + "golang.org/x/sys/unix" +@@ -65,3 +67,42 @@ func CreateInRoot(root, subpath string, flags int, fileMode uint32) (*os.File, e + } + return os.NewFile(uintptr(fd), root+"/"+subpath), nil + } ++ ++// UnlinkInRoot deletes the inode specified at the given subpath. If you pass ++// [unix.AT_REMOVEDIR] it will remove directories, otherwise it will remove ++// non-directory inodes. ++func UnlinkInRoot(root, subpath string, flags int) error { ++ dirPath, filename, err := splitPath(subpath) ++ if err != nil { ++ return fmt.Errorf("split path %q for unlink: %w", subpath, err) ++ } ++ ++ dirFd, err := OpenInRoot(root, dirPath, unix.O_DIRECTORY|unix.O_PATH) ++ if err != nil { ++ return fmt.Errorf("failed to open parent directory %q for unlink: %w", dirPath, err) ++ } ++ defer dirFd.Close() ++ ++ err = unix.Unlinkat(int(dirFd.Fd()), filename, flags) ++ if err != nil { ++ err = &os.PathError{Op: "unlinkat", Path: filepath.Join(dirFd.Name(), filename), Err: err} ++ } ++ return err ++} ++ ++// SymlinkInRoot creates a symlink inside a root with the given target (as well ++// as creating any missing parent directories). If the subpath already exists, ++// an error is returned. ++func SymlinkInRoot(linktarget, root, subpath string) error { ++ dirFd, filename, err := MkdirAllParentInRoot(root, subpath, 0o755) ++ if err != nil { ++ return err ++ } ++ defer dirFd.Close() ++ ++ err = unix.Symlinkat(linktarget, int(dirFd.Fd()), filename) ++ if err != nil { ++ err = &os.PathError{Op: "symlinkat", Path: filepath.Join(dirFd.Name(), filename), Err: err} ++ } ++ return err ++} +diff --git a/src/import/libcontainer/rootfs_linux.go b/src/import/libcontainer/rootfs_linux.go +index be7719211..a0c03b95e 100644 +--- a/src/import/libcontainer/rootfs_linux.go ++++ b/src/import/libcontainer/rootfs_linux.go +@@ -170,7 +170,7 @@ func prepareRootfs(pipe *syncSocket, iConfig *initConfig) (err error) { + if err := createDevices(config); err != nil { + return fmt.Errorf("error creating device nodes: %w", err) + } +- if err := setupPtmx(config); err != nil { ++ if err := setupPtmx(config.Rootfs); err != nil { + return fmt.Errorf("error setting up ptmx: %w", err) + } + if err := setupDevSymlinks(config.Rootfs); err != nil { +@@ -885,11 +885,8 @@ func setupDevSymlinks(rootfs string) error { + links = append(links, [2]string{"/proc/kcore", "/dev/core"}) + } + for _, link := range links { +- var ( +- src = link[0] +- dst = filepath.Join(rootfs, link[1]) +- ) +- if err := os.Symlink(src, dst); err != nil && !errors.Is(err, os.ErrExist) { ++ target, devName := link[0], link[1] ++ if err := pathrs.SymlinkInRoot(target, rootfs, devName); err != nil && !errors.Is(err, os.ErrExist) { + return err + } + } +@@ -1097,15 +1094,11 @@ func setReadonly() error { + return mount("", "/", "", flags, "") + } + +-func setupPtmx(config *configs.Config) error { +- ptmx := filepath.Join(config.Rootfs, "dev/ptmx") +- if err := os.Remove(ptmx); err != nil && !errors.Is(err, os.ErrNotExist) { ++func setupPtmx(rootfs string) error { ++ if err := pathrs.UnlinkInRoot(rootfs, "/dev/ptmx", 0); err != nil && !errors.Is(err, os.ErrNotExist) { + return err + } +- if err := os.Symlink("pts/ptmx", ptmx); err != nil { +- return err +- } +- return nil ++ return pathrs.SymlinkInRoot("pts/ptmx", rootfs, "/dev/ptmx") + } + + // pivotRoot will call pivot_root such that rootfs becomes the new root +-- +2.35.6 + diff --git a/recipes-containers/runc/runc_git.bb b/recipes-containers/runc/runc_git.bb index 6ae647ee..af211865 100644 --- a/recipes-containers/runc/runc_git.bb +++ b/recipes-containers/runc/runc_git.bb @@ -4,6 +4,7 @@ SRCREV = "e1c1378fc924f56ce53f4547b7204e2beda7dbf0" SRC_URI = " \ git://github.com/opencontainers/runc;branch=release-1.5;protocol=https;destsuffix=${GO_SRCURI_DESTSUFFIX} \ file://0001-Makefile-respect-GOBUILDFLAGS-for-runc-and-remove-re.patch \ + file://CVE-2026-41579.patch \ " RUNC_VERSION = "1.5.0-rc.1" -- 2.35.6