All of lore.kernel.org
 help / color / mirror / Atom feed
* [meta-virtualization][wrynose][PATCH 1/2] docker-moby: Fix CVE-2026-41568
@ 2026-07-09 11:35 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-09 11:35 UTC (permalink / raw)
  To: meta-virtualization

From: Deepak Rathore <deeratho@cisco.com>

This patch applies the upstream Moby v29 backport for CVE-2026-41568.
The upstream fix commit is referenced in [1], and the public CVE
advisory is referenced in [2].

[1] https://github.com/moby/moby/commit/64a22d80b93ddc1416b501b5145df02947312249
[2] https://nvd.nist.gov/vuln/detail/CVE-2026-41568

Signed-off-by: Deepak Rathore <deeratho@cisco.com>

diff --git a/recipes-containers/docker/docker-moby_git.bb b/recipes-containers/docker/docker-moby_git.bb
index 56d94675..31b8589d 100644
--- a/recipes-containers/docker/docker-moby_git.bb
+++ b/recipes-containers/docker/docker-moby_git.bb
@@ -54,6 +54,7 @@ SRC_URI = "\
         file://0001-cli-use-external-GO111MODULE-and-cross-compiler.patch \
         file://0001-dynbinary-use-go-cross-compiler.patch;patchdir=src/import \
         file://0001-check-config-make-CONFIG_MEMCG_SWAP-conditional.patch;patchdir=src/import \
+        file://CVE-2026-41568.patch;patchdir=src/import \
 	"
 
 DOCKER_COMMIT = "${SRCREV_moby}"
diff --git a/recipes-containers/docker/files/CVE-2026-41568.patch b/recipes-containers/docker/files/CVE-2026-41568.patch
new file mode 100644
index 00000000..a0a1b72a
--- /dev/null
+++ b/recipes-containers/docker/files/CVE-2026-41568.patch
@@ -0,0 +1,147 @@
+From 84ed1ac6753f538ab762f3ba81a0db1aeff6d048 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= <pawel.gronowski@docker.com>
+Date: Thu, 26 Mar 2026 18:49:45 +0100
+Subject: [PATCH] daemon/copy: Fix symlink escape in mount destination
+ creation
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Use os.Root to scope all filesystem operations in createIfNotExists to
+the container root directory.
+
+This prevents a TOCTOU attack where a container process swaps a path
+component with a symlink between GetResourcePath resolution and
+directory/file creation, which could allow writing to arbitrary host
+paths outside the container.
+
+CVE: CVE-2026-41568
+Upstream-Status: Backport [https://github.com/moby/moby/commit/64a22d80b93ddc1416b501b5145df02947312249]
+
+Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
+(cherry picked from commit 64a22d80b93ddc1416b501b5145df02947312249)
+Signed-off-by: Deepak Rathore <deeratho@cisco.com>
+---
+ daemon/containerfs_linux.go      | 41 +++++++++++++++++++-------------
+ daemon/containerfs_linux_test.go | 22 ++++++++++-------
+ 2 files changed, 38 insertions(+), 25 deletions(-)
+
+diff --git a/daemon/containerfs_linux.go b/daemon/containerfs_linux.go
+index 52a362ebbc..00516d2b96 100644
+--- a/daemon/containerfs_linux.go
++++ b/daemon/containerfs_linux.go
+@@ -91,6 +91,13 @@ func (daemon *Daemon) openContainerFS(ctr *container.Container) (_ *containerFSV
+ 			if err := mount.MakeRSlave("/"); err != nil {
+ 				return err
+ 			}
++
++			root, err := os.OpenRoot(ctr.BaseFS)
++			if err != nil {
++				return fmt.Errorf("open container root: %w", err)
++			}
++			defer root.Close()
++
+ 			for _, m := range mounts {
+ 				dest, err := ctr.GetResourcePath(m.Destination)
+ 				if err != nil {
+@@ -102,7 +109,7 @@ func (daemon *Daemon) openContainerFS(ctr *container.Container) (_ *containerFSV
+ 				if err != nil {
+ 					return err
+ 				}
+-				if err := createIfNotExists(dest, stat.IsDir()); err != nil {
++				if err := createIfNotExists(root, strings.TrimPrefix(m.Destination, "/"), stat.IsDir()); err != nil {
+ 					return err
+ 				}
+ 
+@@ -254,24 +261,24 @@ func (vw *containerFSView) Stat(ctx context.Context, path string) (*containertyp
+ }
+ 
+ // createIfNotExists creates a file or a directory only if it does not already exist.
+-func createIfNotExists(dest string, isDir bool) error {
+-	if _, err := os.Stat(dest); err != nil {
+-		// FIXME(thaJeztah): this ignores any other error (which may include "dest" is of the wrong type, or permission errors).
+-		if os.IsNotExist(err) {
+-			if isDir {
+-				return os.MkdirAll(dest, 0o755)
+-			}
+-			if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil {
+-				return err
+-			}
+-			f, err := os.OpenFile(dest, os.O_CREATE, 0o755)
+-			if err != nil {
+-				return err
+-			}
+-			_ = f.Close()
++// The path is scoped to root using [os.Root] to prevent symlink escape attacks.
++func createIfNotExists(root *os.Root, unsafePath string, isDir bool) error {
++	if isDir {
++		return root.MkdirAll(unsafePath, 0o755)
++	}
++
++	parent := filepath.Dir(unsafePath)
++	if parent != "." && parent != "/" {
++		if err := root.MkdirAll(parent, 0o755); err != nil {
++			return err
+ 		}
+ 	}
+-	return nil
++
++	f, err := root.OpenFile(unsafePath, os.O_CREATE|os.O_WRONLY, 0o755)
++	if err != nil {
++		return err
++	}
++	return f.Close()
+ }
+ 
+ // makeMountRRO makes the mount recursively read-only.
+diff --git a/daemon/containerfs_linux_test.go b/daemon/containerfs_linux_test.go
+index 7d436442d3..715c9bce0e 100644
+--- a/daemon/containerfs_linux_test.go
++++ b/daemon/containerfs_linux_test.go
+@@ -10,30 +10,36 @@ import (
+ 
+ func TestCreateIfNotExists(t *testing.T) {
+ 	t.Run("directory", func(t *testing.T) {
+-		toCreate := filepath.Join(t.TempDir(), "tocreate")
++		dir := t.TempDir()
++		root, err := os.OpenRoot(dir)
++		assert.NilError(t, err)
++		defer root.Close()
+ 
+-		err := createIfNotExists(toCreate, true)
++		err = createIfNotExists(root, "tocreate", true)
+ 		assert.NilError(t, err)
+ 
+-		fileinfo, err := os.Stat(toCreate)
++		fileinfo, err := os.Stat(filepath.Join(dir, "tocreate"))
+ 		assert.NilError(t, err, "Did not create destination")
+ 		assert.Assert(t, fileinfo.IsDir(), "Should have been a dir, seems it's not")
+ 
+-		err = createIfNotExists(toCreate, true)
++		err = createIfNotExists(root, "tocreate", true)
+ 		assert.NilError(t, err, "Should not fail if already exists")
+ 	})
+ 	t.Run("file", func(t *testing.T) {
+-		toCreate := filepath.Join(t.TempDir(), "file/to/create")
++		dir := t.TempDir()
++		root, err := os.OpenRoot(dir)
++		assert.NilError(t, err)
++		defer root.Close()
+ 
+-		err := createIfNotExists(toCreate, false)
++		err = createIfNotExists(root, "file/to/create", false)
+ 		assert.NilError(t, err)
+ 
+-		fileinfo, err := os.Stat(toCreate)
++		fileinfo, err := os.Stat(filepath.Join(dir, "file/to/create"))
+ 		assert.NilError(t, err, "Did not create destination")
+ 
+ 		assert.Assert(t, !fileinfo.IsDir(), "Should have been a file, but created a directory")
+ 
+-		err = createIfNotExists(toCreate, true)
++		err = createIfNotExists(root, "file/to/create", false)
+ 		assert.NilError(t, err, "Should not fail if already exists")
+ 	})
+ }
+-- 
+2.35.6
-- 
2.35.6



^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-07-20 21:26 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-09 11:35 [meta-virtualization][wrynose][PATCH 1/2] docker-moby: Fix CVE-2026-41568 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.