[meta-virtualization][scarthgap][PATCH] runc-opencontainers: fix CVE-2026-41579

"Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)" <[email protected]> Wed, 15 Jul 2026 22:57:56 +0530
Newsgroups org.yoctoproject.lists.meta-virtualization
Message-ID <[email protected]>
From: Deepak Rathore <[email protected]>

This patch applies a Scarthgap backport for CVE-2026-41579.
The upstream release-1.3 fix commit is referenced in [1], and
the public security advisory is referenced in [2]. The target
release lacks the newer internal/pathrs helpers, so the embedded
patch records the equivalent fd-based adaptation.

[1] https://github.com/opencontainers/runc/commit/a8e53f2c6d6d25cb3dd643cc514f118aab44b097
[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           | 109 ++++++++++++++++++
 .../runc/runc-opencontainers_git.bb           |   1 +
 2 files changed, 110 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..0d30c942
--- /dev/null
+++ b/recipes-containers/runc/files/CVE-2026-41579.patch
@@ -0,0 +1,109 @@
+From 4662e4af4680996b7d9b7100b5970b75ee6d0048 Mon Sep 17 00:00:00 2001
+From: Deepak Rathore <[email protected]>
+Date: Mon, 6 Jul 2026 00:44:51 -0700
+Subject: [PATCH] rootfs: make /dev initialisation code fd-based
+
+These code paths operate on host-visible paths before pivot_root(2),
+so a malicious image with /dev as a symlink can make runc operate on
+host paths while setting up /dev symlinks and /dev/ptmx.
+
+Use fd-relative unlinkat(2) and symlinkat(2) against a no-follow
+opened rootfs/dev directory so the final /dev component cannot be a
+symlink to a host directory.
+
+CVE: CVE-2026-41579
+Upstream-Status: Backport [https://github.com/opencontainers/runc/commit/a8e53f2c6d6d25cb3dd643cc514f118aab44b097]
+
+Backport Changes:
+- Scarthgap runc 1.1.14 does not contain the internal/pathrs helpers
+  used by the upstream release-1.3 fix.
+- Added a minimal openDevInRoot helper and used unlinkat/symlinkat
+  directly to keep the same fd-scoped security behavior.
+- Omitted internal/pathrs changes because that package is absent from
+  release-1.1 and the CVE path only needs fixed /dev entry handling.
+
+Signed-off-by: Aleksa Sarai <[email protected]>
+(cherry picked from commit a8e53f2c6d6d25cb3dd643cc514f118aab44b097)
+Signed-off-by: Deepak Rathore <[email protected]>
+---
+ src/import/libcontainer/rootfs_linux.go | 45 ++++++++++++++++++++++++++----------
+ 1 file changed, 33 insertions(+), 12 deletions(-)
+
+diff --git a/src/import/libcontainer/rootfs_linux.go b/src/import/libcontainer/rootfs_linux.go
+index 78b6998c..b8f9b238 100644
+--- a/src/import/libcontainer/rootfs_linux.go
++++ b/src/import/libcontainer/rootfs_linux.go
+@@ -658,25 +658,40 @@ func checkProcMount(rootfs, dest string, m *configs.Mount, source string) error
+ 	return fmt.Errorf("%q cannot be mounted because it is inside /proc", dest)
+ }
+ 
++func openDevInRoot(rootfs string) (int, error) {
++	devPath := filepath.Join(rootfs, "dev")
++	devFd, err := unix.Open(devPath, unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)
++	if err != nil {
++		return -1, &os.PathError{Op: "open", Path: devPath, Err: err}
++	}
++	return devFd, nil
++}
++
+ func setupDevSymlinks(rootfs string) error {
++	devFd, err := openDevInRoot(rootfs)
++	if err != nil {
++		return err
++	}
++	defer unix.Close(devFd) //nolint: errcheck
++
+ 	links := [][2]string{
+-		{"/proc/self/fd", "/dev/fd"},
+-		{"/proc/self/fd/0", "/dev/stdin"},
+-		{"/proc/self/fd/1", "/dev/stdout"},
+-		{"/proc/self/fd/2", "/dev/stderr"},
++		{"/proc/self/fd", "fd"},
++		{"/proc/self/fd/0", "stdin"},
++		{"/proc/self/fd/1", "stdout"},
++		{"/proc/self/fd/2", "stderr"},
+ 	}
+ 	// kcore support can be toggled with CONFIG_PROC_KCORE; only create a symlink
+ 	// in /dev if it exists in /proc.
+ 	if _, err := os.Stat("/proc/kcore"); err == nil {
+-		links = append(links, [2]string{"/proc/kcore", "/dev/core"})
++		links = append(links, [2]string{"/proc/kcore", "core"})
+ 	}
+ 	for _, link := range links {
+ 		var (
+ 			src = link[0]
+-			dst = filepath.Join(rootfs, link[1])
++			dst = link[1]
+ 		)
+-		if err := os.Symlink(src, dst); err != nil && !os.IsExist(err) {
+-			return err
++		if err := unix.Symlinkat(src, devFd, dst); err != nil && err != unix.EEXIST {
++			return &os.PathError{Op: "symlinkat", Path: filepath.Join(rootfs, "dev", dst), Err: err}
+ 		}
+ 	}
+ 	return nil
+@@ -886,12 +901,18 @@ func setReadonly() error {
+ }
+ 
+ func setupPtmx(config *configs.Config) error {
+-	ptmx := filepath.Join(config.Rootfs, "dev/ptmx")
+-	if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) {
++	devFd, err := openDevInRoot(config.Rootfs)
++	if err != nil {
+ 		return err
+ 	}
+-	if err := os.Symlink("pts/ptmx", ptmx); err != nil {
+-		return err
++	defer unix.Close(devFd) //nolint: errcheck
++
++	ptmx := filepath.Join(config.Rootfs, "dev", "ptmx")
++	if err := unix.Unlinkat(devFd, "ptmx", 0); err != nil && err != unix.ENOENT {
++		return &os.PathError{Op: "unlinkat", Path: ptmx, Err: err}
++	}
++	if err := unix.Symlinkat("pts/ptmx", devFd, "ptmx"); err != nil {
++		return &os.PathError{Op: "symlinkat", Path: ptmx, Err: err}
+ 	}
+ 	return nil
+ }
+-- 
+2.35.6
diff --git a/recipes-containers/runc/runc-opencontainers_git.bb b/recipes-containers/runc/runc-opencontainers_git.bb
index 4c831502..8e3dbc8d 100644
--- a/recipes-containers/runc/runc-opencontainers_git.bb
+++ b/recipes-containers/runc/runc-opencontainers_git.bb
@@ -5,6 +5,7 @@ SRC_URI = " \
     git://github.com/opencontainers/runc;branch=release-1.1;protocol=https \
     file://0001-Makefile-respect-GOBUILDFLAGS-for-runc-and-remove-re.patch \
     file://0001-Makefile-fix-typo-in-LDFLAGS_STATIC.patch \
+    file://CVE-2026-41579.patch \
     "
 RUNC_VERSION = "1.1.14"
 
-- 
2.35.6