Skip to contentSkip to conversation

FAILED: patch "[PATCH] namespace: never expire a locked mount" failed to apply to 6.12-stable tree

PATCH [email protected] 1 participant 1 message Oct 12

Subsystems (2)
lore
Share & tools
lore

[email protected]

author maintainer
#0 lore raw

The patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.

To reproduce the conflict and resubmit, you may use the following commands:

git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x f78ddcc872248c7aa1d7e904928441a0be4c6d5d
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026101225-upon-doorknob-a385@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'

Possible dependencies:

thanks,

greg k-h

------------------ original commit in Linus's tree ------------------

From f78ddcc872248c7aa1d7e904928441a0be4c6d5d Mon Sep 17 00:00:00 2001
From: Christian Brauner <[email protected]>
Date: Fri, 2 Oct 2026 15:52:39 +0200
Subject: [PATCH] namespace: never expire a locked mount

Locked mounts are special. They protect the underlying files and directories from being revealed. do_umount() refuses to unmount locked mounts but shrink_submounts() doesn't.

A shrinkable mount can become locked once the owner of a user namespace puts it beneath a locked mount with MOVE_MOUNT_BENEATH. The locked property now moves to the mount at the bottom making it possible to unmount the top mount.

So umount() of an unlocked ancestor now expires the bottom mount first and the covered directory is revealed:

move_mount(c -> x/hidden, MOVE_MOUNT_BENEATH) = 0

  the cover after the lock moved:  umount2(x/hidden) = 0
  the holder of the lock:          umount2(x/hidden) = EINVAL
  the root of the copy, busy:      umount2(x) = EBUSY

reads x/hidden/secret: "covered-by-root"

So leave a locked mount alone as it dies together with its parent. A plain umount() of an unlocked mount with a locked one below it is EBUSY from now on. It's the same for any other locked child. A lazy umount still takes the whole tree.

mark_mounts_for_expiry() never sees a locked mount. lock_mnt_tree() leaves a mount on an expiry list alone and nothing else puts a locked one on such a list. Add an assert for this.

Fixes 5ff9d8a65ce8 ("vfs: Lock in place mounts from more privileged users")
Fixes c62a4766937e ("move_mount: transfer MNT_LOCKED")
fs/namespace.c +4 -1
index 27bf8665ed58..e74e63466c24 100644
@@ -4032,6 +4032,8 @@ void mark_mounts_for_expiry(struct list_head *mounts)
 	list_for_each_entry_safe(mnt, next, mounts, mnt_expire) {
 		if (!is_mounted(&mnt->mnt))
 			continue;
+		/* lock_mnt_tree() leaves expirable mounts alone */
+		VFS_WARN_ON_ONCE(IS_MNT_LOCKED(mnt));
 		if (!xchg(&mnt->mnt_expiry_mark, 1) ||
 			propagate_mount_busy(mnt, 1))
 			continue;
@@ -4058,7 +4060,8 @@ EXPORT_SYMBOL_GPL(mark_mounts_for_expiry);
  */
 static bool shrink_submount(struct mount *mnt)
 {
-	if (propagate_mount_busy(mnt, 1))
+	/* not the kernel's to remove either */
+	if (IS_MNT_LOCKED(mnt) || propagate_mount_busy(mnt, 1))
 		return false;
 	touch_mnt_namespace(mnt->mnt_ns);
 	umount_tree(mnt, UMOUNT_PROPAGATE|UMOUNT_SYNC);