Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions SPECS/util-linux/CVE-2026-76642.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
From 56a7e403d2782f674937df89b1c1eff0981ecf42 Mon Sep 17 00:00:00 2001
From: AllSpark <allspark@microsoft.com>
Date: Mon, 7 Sep 2026 15:16:10 +0000
Subject: [PATCH] libmount: skip post-mount hooks after failed mount helper
[CVE-2026-76642]

Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: AI Backport of https://github.com/util-linux/util-linux/commit/a15c00a9e545aa8b9cf6ec0f888ff6c7b3eaeedc.patch
---
libmount/src/context_mount.c | 23 ++++++++++++++++++++---
libmount/src/hook_loopdev.c | 13 +++++++++++--
2 files changed, 31 insertions(+), 5 deletions(-)

diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c
index f9d610f..b5c7c94 100644
--- a/libmount/src/context_mount.c
+++ b/libmount/src/context_mount.c
@@ -575,6 +575,22 @@ static int is_success_status(struct libmnt_context *cxt)
return 0;
}

+/* Check if the mount stage explicitly failed (helper or syscall returned
+ * an error). Unlike is_success_status(), this treats "nothing happened"
+ * as not-failed -- the MOUNT stage may be a no-op for operations like
+ * bind/move with the new mount API where open_tree() runs in PREP and
+ * move_mount() is deferred to MOUNT_POST. */
+static int is_mount_stage_failed(struct libmnt_context *cxt)
+{
+ if (mnt_context_helper_executed(cxt))
+ return mnt_context_get_helper_status(cxt) != 0;
+
+ if (mnt_context_syscall_called(cxt))
+ return mnt_context_get_status(cxt) != 1;
+
+ return 0;
+}
+
/* try mount(2) for all items in comma separated list of the filesystem @types */
static int do_mount_by_types(struct libmnt_context *cxt, const char *types)
{
@@ -868,8 +884,9 @@ int mnt_context_do_mount(struct libmnt_context *cxt)
} else
res = do_mount_by_pattern(cxt, cxt->fstype_pattern);

- /* after mount stage */
- if (res == 0) {
+ /* after mount stage -- the post-mount hooks are commit-path only,
+ * skip them if the mount helper or syscall has failed */
+ if (res == 0 && !is_mount_stage_failed(cxt)) {
rc = mnt_context_call_hooks(cxt, MNT_STAGE_MOUNT_POST);
if (rc)
return rc;
@@ -1052,7 +1069,7 @@ again:
}
}

- if (rc == 0)
+ if (rc == 0 && !is_mount_stage_failed(cxt))
rc = mnt_context_call_hooks(cxt, MNT_STAGE_POST);

mnt_context_deinit_hooksets(cxt);
diff --git a/libmount/src/hook_loopdev.c b/libmount/src/hook_loopdev.c
index 4df1915..31d9861 100644
--- a/libmount/src/hook_loopdev.c
+++ b/libmount/src/hook_loopdev.c
@@ -23,6 +23,8 @@ struct hook_data {
int loopdev_fd;
};

+static int delete_loopdev(struct libmnt_context *cxt, struct hook_data *hd);
+
/* de-initiallize this module */
static int hookset_deinit(struct libmnt_context *cxt, const struct libmnt_hookset *hs)
{
@@ -30,9 +32,16 @@ static int hookset_deinit(struct libmnt_context *cxt, const struct libmnt_hookse

DBG(HOOK, ul_debugobj(hs, "deinit '%s'", hs->name));

- /* remove all our hooks */
+ /* remove all our hooks and free hook data */
while (mnt_context_remove_hook(cxt, hs, 0, &data) == 0) {
- free(data);
+ if (data) {
+ struct hook_data *hd = (struct hook_data *) data;
+
+ /* cleanup after skipped MOUNT_POST hook */
+ if (hd->loopdev_fd > -1)
+ delete_loopdev(cxt, hd);
+ free(hd);
+ }
data = NULL;
}

--
2.45.4

84 changes: 84 additions & 0 deletions SPECS/util-linux/CVE-2026-78408.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
From 5bdc94402e00c398846be67143487dabe0329e9e Mon Sep 17 00:00:00 2001
From: Karel Zak <kzak@redhat.com>
Date: Thu, 3 Sep 2026 12:17:14 +0200
Subject: [PATCH] nsenter: close cgroup.procs fd after join to prevent
authority leak [CVE-2026-78408]

The --join-cgroup option opens the target's cgroup.procs while running
as root and writes nsenter's own PID to migrate itself. The descriptor
was left open across subsequent namespace transitions, credential drops
(setgroups/setgid/setuid) and execve().

The kernel performs cgroup migration permission checks using the
credentials captured at open time (file->f_cred). An open cgroup.procs
descriptor therefore carries the opener's migration authority regardless
of later privilege changes. A program executed inside the target
namespace inherits root's cgroup migration capability even when running
as an unprivileged user with no capabilities.

Fix this by:

- closing the temporary /proc/PID/cgroup fd after reading the path
- adding O_CLOEXEC to the cgroup.procs open as defense in depth
- closing cgroup_procs_fd immediately after the self-migration write
- initializing the temporary cgroup fd to -1 instead of 0 to avoid
accidentally closing stdin via open_target_fd()

The descriptor has no legitimate use after the single migration write.

Introduced-by: b40650b71a74 ("nsenter: add option -c to join the cgroup of target process")
References: b0cf1cf0d255 ("nsenter: close cgroup.procs fd after join to prevent authority leak")
Signed-off-by: Karel Zak <kzak@redhat.com>
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: https://github.com/util-linux/util-linux/commit/afe067c979b9ba2cbe856f7c6411210120ea62aa.patch
---
sys-utils/nsenter.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/sys-utils/nsenter.c b/sys-utils/nsenter.c
index 8f7bac9..c3c92b5 100644
--- a/sys-utils/nsenter.c
+++ b/sys-utils/nsenter.c
@@ -202,7 +202,7 @@ static int get_ns_ino(const char *path, ino_t *ino)
static void open_cgroup_procs(void)
{
char *buf = NULL, *path = NULL, *p;
- int cgroup_fd = 0;
+ int cgroup_fd = -1;
char fdpath[PATH_MAX];

open_target_fd(&cgroup_fd, "cgroup", optarg);
@@ -210,6 +210,8 @@ static void open_cgroup_procs(void)
if (read_all_alloc(cgroup_fd, &buf) < 1)
err(EXIT_FAILURE, _("failed to get cgroup path"));

+ close(cgroup_fd);
+
p = strtok(buf, "\n");
if (p)
path = strrchr(p, ':');
@@ -219,7 +221,7 @@ static void open_cgroup_procs(void)

snprintf(fdpath, sizeof(fdpath), _PATH_SYS_CGROUP "/%s/cgroup.procs", path);

- if ((cgroup_procs_fd = open(fdpath, O_WRONLY | O_APPEND)) < 0)
+ if ((cgroup_procs_fd = open(fdpath, O_WRONLY | O_APPEND | O_CLOEXEC)) < 0)
err(EXIT_FAILURE, _("failed to open cgroup.procs"));

free(buf);
@@ -654,8 +656,11 @@ int main(int argc, char *argv[])
}

// Join into the target cgroup
- if (cgroup_procs_fd >= 0)
+ if (cgroup_procs_fd >= 0) {
join_into_cgroup();
+ close(cgroup_procs_fd);
+ cgroup_procs_fd = -1;
+ }

if (uid_gid_fd >= 0) {
struct stat st;
--
2.45.4

Loading
Loading