From: "Hugo SIMELIERE (Schneider Electric)" <[email protected]>

Pick patch from [1] as 2.39.x upstream backport of [2] mentioned in Debian 
report in [3].

[1] 
https://github.com/util-linux/util-linux/commit/79164668a412b71fcb1495c7d299cc5e9741fa30
[2] 
https://github.com/util-linux/util-linux/commit/0ba0f14caa812349424df0da00ac2d97fee9d972
[3] https://security-tracker.debian.org/tracker/CVE-2026-27456

Signed-off-by: Hugo SIMELIERE (Schneider Electric) 
<[email protected]>
Reviewed-by: Bruno VERNAY <[email protected]>
---
 meta/recipes-core/util-linux/util-linux.inc   |   1 +
 .../util-linux/CVE-2026-27456.patch           | 115 ++++++++++++++++++
 2 files changed, 116 insertions(+)
 create mode 100644 meta/recipes-core/util-linux/util-linux/CVE-2026-27456.patch

diff --git a/meta/recipes-core/util-linux/util-linux.inc 
b/meta/recipes-core/util-linux/util-linux.inc
index 4797682c5d..8380419634 100644
--- a/meta/recipes-core/util-linux/util-linux.inc
+++ b/meta/recipes-core/util-linux/util-linux.inc
@@ -46,6 +46,7 @@ SRC_URI = 
"${KERNELORG_MIRROR}/linux/utils/util-linux/v${MAJOR_VERSION}/util-lin
            file://sys-utils-hwclock-rtc-fix-pointer-usage.patch \
            file://CVE-2025-14104-01.patch \
            file://CVE-2025-14104-02.patch \
+           file://CVE-2026-27456.patch \
            "
 
 SRC_URI[sha256sum] = 
"7b6605e48d1a49f43cc4b4cfc59f313d0dd5402fa40b96810bd572e167dfed0f"
diff --git a/meta/recipes-core/util-linux/util-linux/CVE-2026-27456.patch 
b/meta/recipes-core/util-linux/util-linux/CVE-2026-27456.patch
new file mode 100644
index 0000000000..4a5fef26d3
--- /dev/null
+++ b/meta/recipes-core/util-linux/util-linux/CVE-2026-27456.patch
@@ -0,0 +1,115 @@
+From af0b619f8eb15f738c69e33e0bb3a794e9cccf17 Mon Sep 17 00:00:00 2001
+From: Karel Zak <[email protected]>
+Date: Thu, 19 Feb 2026 13:59:46 +0100
+Subject: [PATCH] loopdev: add LOOPDEV_FL_NOFOLLOW to prevent symlink attacks
+
+Add a new LOOPDEV_FL_NOFOLLOW flag for loop device context that
+prevents symlink following in both path canonicalization and file open.
+
+When set:
+- loopcxt_set_backing_file() uses strdup() instead of
+  ul_canonicalize_path() (which calls realpath() and follows symlinks)
+- loopcxt_setup_device() adds O_NOFOLLOW to open() flags
+
+The flag is set for non-root (restricted) mount operations in
+libmount's loop device hook. This prevents a TOCTOU race condition
+where an attacker could replace the backing file (specified in
+/etc/fstab) with a symlink to an arbitrary root-owned file between
+path resolution and open().
+
+Vulnerable Code Flow:
+
+  mount /mnt/point (non-root, SUID)
+    mount.c: sanitize_paths() on user args (mountpoint only)
+    mnt_context_mount()
+      mnt_context_prepare_mount()
+        mnt_context_apply_fstab()           <-- source path from fstab
+        hooks run at MNT_STAGE_PREP_SOURCE
+          hook_loopdev.c: setup_loopdev()
+            backing_file = fstab source path ("/home/user/disk.img")
+            loopcxt_set_backing_file()       <-- calls realpath() as ROOT
+              ul_canonicalize_path()         <-- follows symlinks!
+            loopcxt_setup_device()
+              open(lc->filename, O_RDWR|O_CLOEXEC)  <-- no O_NOFOLLOW
+
+Two vulnerabilities in the path:
+
+1) loopcxt_set_backing_file() calls ul_canonicalize_path() which uses
+   realpath() -- this follows symlinks as euid=0. If the attacker swaps
+   the file to a symlink before this call, lc->filename becomes the
+   resolved target path (e.g., /root/secret.img).
+
+2) loopcxt_setup_device() opens lc->filename without O_NOFOLLOW. Even
+   if canonicalization happened correctly, the file can be swapped to a
+   symlink between canonicalize and open.
+
+CVE: CVE-2026-27456
+Upstream-Status: Backport 
[https://github.com/util-linux/util-linux/commit/79164668a412b71fcb1495c7d299cc5e9741fa30]
+
+Addresses: 
https://github.com/util-linux/util-linux/security/advisories/GHSA-qq4x-vfq4-9h9g
+Signed-off-by: Karel Zak <[email protected]>
+(cherry picked from commit 5e390467b26a3cf3fecc04e1a0d482dff3162fc4)
+(cherry picked from commit 79164668a412b71fcb1495c7d299cc5e9741fa30)
+Signed-off-by: Hugo SIMELIERE (Schneider Electric) 
<[email protected]>
+---
+ include/loopdev.h           | 3 ++-
+ lib/loopdev.c               | 7 ++++++-
+ libmount/src/hook_loopdev.c | 3 ++-
+ 3 files changed, 10 insertions(+), 3 deletions(-)
+
+diff --git a/include/loopdev.h b/include/loopdev.h
+index 903adc491..d03e9b65e 100644
+--- a/include/loopdev.h
++++ b/include/loopdev.h
+@@ -139,7 +139,8 @@ enum {
+       LOOPDEV_FL_NOIOCTL      = (1 << 6),
+       LOOPDEV_FL_DEVSUBDIR    = (1 << 7),
+       LOOPDEV_FL_CONTROL      = (1 << 8),     /* system with 
/dev/loop-control */
+-      LOOPDEV_FL_SIZELIMIT    = (1 << 9)
++      LOOPDEV_FL_SIZELIMIT    = (1 << 9),
++      LOOPDEV_FL_NOFOLLOW     = (1 << 10)     /* O_NOFOLLOW, don't follow 
symlinks */
+ };
+ 
+ /*
+diff --git a/lib/loopdev.c b/lib/loopdev.c
+index dd9ead3ee..4da251812 100644
+--- a/lib/loopdev.c
++++ b/lib/loopdev.c
+@@ -1193,7 +1193,10 @@ int loopcxt_set_backing_file(struct loopdev_cxt *lc, 
const char *filename)
+       if (!lc)
+               return -EINVAL;
+ 
+-      lc->filename = canonicalize_path(filename);
++      if (lc->flags & LOOPDEV_FL_NOFOLLOW)
++              lc->filename = strdup(filename);
++      else
++              lc->filename = canonicalize_path(filename);
+       if (!lc->filename)
+               return -errno;
+ 
+@@ -1332,6 +1335,8 @@ int loopcxt_setup_device(struct loopdev_cxt *lc)
+ 
+       if (lc->config.info.lo_flags & LO_FLAGS_DIRECT_IO)
+               flags |= O_DIRECT;
++      if (lc->flags & LOOPDEV_FL_NOFOLLOW)
++              flags |= O_NOFOLLOW;
+ 
+       if ((file_fd = open(lc->filename, mode | flags)) < 0) {
+               if (mode != O_RDONLY && (errno == EROFS || errno == EACCES))
+diff --git a/libmount/src/hook_loopdev.c b/libmount/src/hook_loopdev.c
+index 8c8f7f218..ce39a7a70 100644
+--- a/libmount/src/hook_loopdev.c
++++ b/libmount/src/hook_loopdev.c
+@@ -276,7 +276,8 @@ static int setup_loopdev(struct libmnt_context *cxt,
+       }
+ 
+       DBG(LOOP, ul_debugobj(cxt, "not found; create a new loop device"));
+-      rc = loopcxt_init(&lc, 0);
++      rc = loopcxt_init(&lc,
++                      mnt_context_is_restricted(cxt) ? LOOPDEV_FL_NOFOLLOW : 
0);
+       if (rc)
+               goto done_no_deinit;
+       if (mnt_opt_has_value(loopopt)) {
+-- 
+2.43.0
+
-- 
2.43.0

-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#237446): 
https://lists.openembedded.org/g/openembedded-core/message/237446
Mute This Topic: https://lists.openembedded.org/mt/119405872/21656
Group Owner: [email protected]
Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub 
[[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-

  • [OE-core][scarthgap][PATCH 1/2] ... Hugo Simeliere via lists.openembedded.org

Reply via email to