mirror of
https://gitlab.com/buildroot.org/buildroot.git
synced 2026-09-30 22:11:59 -09:00
package/util-linux: add patch for CVE-2026-27456
- CVE-2026-27456: mount(8) TOCTOU symlink attack via loop device. The SUID
mount follows symlinks when resolving loop backing file paths. On
systems where non-root users are permitted to mount loop devices (via
'user' option in fstab), this allows access to arbitrary files.
For more information, see:
- https://nvd.nist.gov/vuln/detail/CVE-2026-27456
- 5e390467b2
Signed-off-by: Titouan Christophe <titouan.christophe@mind.be>
(cherry picked from commit cd52acda14)
Signed-off-by: Thomas Perale <thomas.perale@mind.be>
This commit is contained in:
committed by
Thomas Perale
parent
dabbb7caa4
commit
eeb65cf0f6
111
package/util-linux/0006-add-loopdev-fl-nofollow.patch
Normal file
111
package/util-linux/0006-add-loopdev-fl-nofollow.patch
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
From 5e390467b26a3cf3fecc04e1a0d482dff3162fc4 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Karel Zak <kzak@redhat.com>
|
||||||
|
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.
|
||||||
|
|
||||||
|
Addresses: https://github.com/util-linux/util-linux/security/advisories/GHSA-qq4x-vfq4-9h9g
|
||||||
|
Signed-off-by: Karel Zak <kzak@redhat.com>
|
||||||
|
|
||||||
|
CVE: CVE-2026-27456
|
||||||
|
Upstream: https://github.com/util-linux/util-linux/commit/5e390467b26a3cf3fecc04e1a0d482dff3162fc4
|
||||||
|
[Titouan: Adapt patch to apply cleanly onto util-linux 2.40]
|
||||||
|
Signed-off-by: Titouan Christophe <titouan.christophe@mind.be>
|
||||||
|
---
|
||||||
|
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 d10bf7f37..0f85dd254 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 c72fb2c40..3d2274693 100644
|
||||||
|
--- a/lib/loopdev.c
|
||||||
|
+++ b/lib/loopdev.c
|
||||||
|
@@ -1267,7 +1267,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 = ul_canonicalize_path(filename);
|
||||||
|
if (!lc->filename)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
@@ -1408,6 +1411,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 597b9339a..4df1915a6 100644
|
||||||
|
--- a/libmount/src/hook_loopdev.c
|
||||||
|
+++ b/libmount/src/hook_loopdev.c
|
||||||
|
@@ -272,7 +272,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)) {
|
||||||
@@ -36,6 +36,9 @@ UTIL_LINUX_CPE_ID_VENDOR = kernel
|
|||||||
# 0001-libmount-ifdef-statx-call.patch
|
# 0001-libmount-ifdef-statx-call.patch
|
||||||
UTIL_LINUX_AUTORECONF = YES
|
UTIL_LINUX_AUTORECONF = YES
|
||||||
|
|
||||||
|
# 0006-add-loopdev-fl-nofollow.patch
|
||||||
|
UTIL_LINUX_IGNORE_CVES += CVE-2026-27456
|
||||||
|
|
||||||
UTIL_LINUX_INSTALL_STAGING = YES
|
UTIL_LINUX_INSTALL_STAGING = YES
|
||||||
UTIL_LINUX_DEPENDENCIES = \
|
UTIL_LINUX_DEPENDENCIES = \
|
||||||
host-pkgconf \
|
host-pkgconf \
|
||||||
|
|||||||
Reference in New Issue
Block a user