* [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.