* [meta-virtualization][wrynose][PATCH] runc: fix CVE-2026-41579
@ 2026-07-10 12:25 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-07-20 21:26 ` Bruce Ashfield
0 siblings, 1 reply; 2+ messages in thread
From: Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-07-10 12:25 UTC (permalink / raw)
To: meta-virtualization
From: Deepak Rathore <deeratho@cisco.com>
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 <deeratho@cisco.com>
---
.../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 <aleksa@amutable.com>
+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 <aleksa@amutable.com>
+(cherry picked from commit 864db8042dbb191028676f80addf8c35f348aee2)
+Signed-off-by: Aleksa Sarai <aleksa@amutable.com>
+(cherry picked from commit 4504accbbaebd198f1fce296b39cf0d6f20a17e1)
+Signed-off-by: Deepak Rathore <deeratho@cisco.com>
+---
+ 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
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [meta-virtualization][wrynose][PATCH] runc: fix CVE-2026-41579
2026-07-10 12:25 [meta-virtualization][wrynose][PATCH] runc: fix CVE-2026-41579 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-07-20 21:26 ` Bruce Ashfield
0 siblings, 0 replies; 2+ messages in thread
From: Bruce Ashfield @ 2026-07-20 21:26 UTC (permalink / raw)
To: deeratho, meta-virtualization
merged
Bruce
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-20 21:27 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-10 12:25 [meta-virtualization][wrynose][PATCH] runc: fix CVE-2026-41579 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-07-20 21:26 ` Bruce Ashfield
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.