All of lore.kernel.org
 help / color / mirror / Atom feed
From: Justin Suess <utilityemal77@gmail.com>
To: "Christian Brauner" <brauner@kernel.org>,
	"Alexander Viro" <viro@zeniv.linux.org.uk>,
	"Jan Kara" <jack@suse.cz>, NeilBrown <neil@brown.name>,
	"Mickaël Salaün" <mic@digikod.net>,
	"Alexei Starovoitov" <ast@kernel.org>,
	"Daniel Borkmann" <daniel@iogearbox.net>,
	"Andrii Nakryiko" <andrii@kernel.org>,
	"Song Liu" <song@kernel.org>
Cc: linux-fsdevel@vger.kernel.org, bpf@vger.kernel.org,
	linux-security-module@vger.kernel.org,
	linux-kernel@vger.kernel.org, "Günther Noack" <gnoack@google.com>,
	"Paul Moore" <paul@paul-moore.com>,
	"James Morris" <jmorris@namei.org>,
	"Serge E . Hallyn" <serge@hallyn.com>,
	"Martin KaFai Lau" <martin.lau@linux.dev>,
	"Eduard Zingerman" <eddyz87@gmail.com>,
	"Yonghong Song" <yonghong.song@linux.dev>,
	"John Fastabend" <john.fastabend@gmail.com>,
	"Kumar Kartikeya Dwivedi" <memxor@gmail.com>,
	"Jiri Olsa" <jolsa@kernel.org>,
	"Jeff Layton" <jlayton@kernel.org>,
	"Amir Goldstein" <amir73il@gmail.com>,
	"Mateusz Guzik" <mjguzik@gmail.com>,
	"Shuah Khan" <shuah@kernel.org>, "Tingmao Wang" <m@maowtm.org>,
	"Justin Suess" <utilityemal77@gmail.com>
Subject: [RFC PATCH bpf-next 08/12] fs: add mnt_undo_legitimize()
Date: Mon,  5 Oct 2026 20:20:15 -0400	[thread overview]
Message-ID: <20261006002020.2890858-9-utilityemal77@gmail.com> (raw)
In-Reply-To: <20261006002020.2890858-1-utilityemal77@gmail.com>

A failed __legitimize_mnt() can oblige its caller to mntput() the
mount, which may be the final put and so may sleep: legitimize_mnt()
leaves the RCU read-side critical section it ran under to do it.  A
caller that cannot leave its critical section - a BPF program driving
a lockless walk, which only the verifier ends - needs that put to be
callable from inside it.

Add mnt_undo_legitimize(), which drops the count and, when the count
turns out to be the mount's last, keeps it and hands the mount to
delayed_mntput() for the sleepable final put.  delayed_mntput() then
tells the two queue populations apart by MNT_DOOMED rather than by an
unlocked mnt_get_count() read, which a concurrent failed
__legitimize_mnt() could transiently inflate.  legitimize_mnt()
switches to it as well, retiring its
rcu_read_unlock/mntput/rcu_read_lock dance.

Unlike mntput(), mnt_expiry_mark is left alone: a walker that failed
to legitimize a mount never used it.

Signed-off-by: Justin Suess <utilityemal77@gmail.com>
---
 fs/mount.h     |  1 +
 fs/namespace.c | 63 ++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 57 insertions(+), 7 deletions(-)

diff --git a/fs/mount.h b/fs/mount.h
index 94fcc306d21e..e714040f8e8f 100644
--- a/fs/mount.h
+++ b/fs/mount.h
@@ -140,6 +140,7 @@ static inline int is_mounted(struct vfsmount *mnt)
 extern struct mount *__lookup_mnt(struct vfsmount *, struct dentry *);
 
 extern int __legitimize_mnt(struct vfsmount *, unsigned);
+void mnt_undo_legitimize(struct mount *mnt);
 
 static inline bool __path_is_mountpoint(const struct path *path)
 {
diff --git a/fs/namespace.c b/fs/namespace.c
index 580877e46b1a..920b8a85f9fa 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -767,11 +767,8 @@ static bool legitimize_mnt(struct vfsmount *bastard, unsigned seq)
 	int res = __legitimize_mnt(bastard, seq);
 	if (likely(!res))
 		return true;
-	if (unlikely(res < 0)) {
-		rcu_read_unlock();
-		mntput(bastard);
-		rcu_read_lock();
-	}
+	if (unlikely(res < 0))
+		mnt_undo_legitimize(real_mount(bastard));
 	return false;
 }
 
@@ -1330,8 +1327,19 @@ static void delayed_mntput(struct work_struct *unused)
 	struct llist_node *node = llist_del_all(&delayed_mntput_list);
 	struct mount *m, *t;
 
-	llist_for_each_entry_safe(m, t, node, mnt_llist)
-		cleanup_mnt(m);
+	llist_for_each_entry_safe(m, t, node, mnt_llist) {
+		/*
+		 * MNT_DOOMED is only ever set on the mounts
+		 * mntput_no_expire_slowpath() queues here, never on the
+		 * kept-count ones mnt_undo_legitimize() queues.  Route on it
+		 * rather than on mnt_get_count(), which a concurrent failed
+		 * __legitimize_mnt() can transiently inflate.
+		 */
+		if (m->mnt.mnt_flags & MNT_DOOMED)
+			cleanup_mnt(m);
+		else
+			mntput(&m->mnt);	/* kept by mnt_undo_legitimize() */
+	}
 }
 static DECLARE_DELAYED_WORK(delayed_mntput_work, delayed_mntput);
 
@@ -1423,6 +1431,47 @@ void mntput(struct vfsmount *mnt)
 }
 EXPORT_SYMBOL(mntput);
 
+/**
+ * mnt_undo_legitimize - drop a count __legitimize_mnt() asked us to put
+ * @mnt: the mount __legitimize_mnt() failed on
+ *
+ * Like the mntput() that a failed __legitimize_mnt() normally obliges,
+ * but callable from the RCU read-side critical section the legitimization
+ * ran under: when the count turns out to be the mount's last, it is kept
+ * and the mount handed to delayed_mntput() for the sleepable final put.
+ * Unlike mntput(), mnt_expiry_mark is left alone: a walker that failed
+ * to legitimize the mount never used it.
+ */
+void mnt_undo_legitimize(struct mount *mnt)
+{
+	if (likely(READ_ONCE(mnt->mnt_ns))) {
+		/* Not the final count, as in mntput_no_expire(). */
+		mnt_add_count(mnt, -1);
+		return;
+	}
+	lock_mount_hash();
+	/*
+	 * As in mntput_no_expire(): make sure that if a concurrent
+	 * __legitimize_mnt() has not seen us grab mount_lock, we'll see
+	 * its refcount increment here.
+	 */
+	smp_mb();
+	if (likely(mnt_get_count(mnt) > 1)) {
+		mnt_add_count(mnt, -1);
+		unlock_mount_hash();
+		return;
+	}
+	unlock_mount_hash();
+	/*
+	 * Ours is the last count: nothing else can reach the mount any
+	 * more, which makes its mnt_llist ours to use.  delayed_mntput()
+	 * tells our kept-count mount from the doomed ones by MNT_DOOMED.
+	 */
+	WARN_ON_ONCE(mnt->mnt.mnt_flags & MNT_DOOMED);
+	if (llist_add(&mnt->mnt_llist, &delayed_mntput_list))
+		schedule_delayed_work(&delayed_mntput_work, 1);
+}
+
 struct vfsmount *mntget(struct vfsmount *mnt)
 {
 	if (mnt)
-- 
2.55.0


  parent reply	other threads:[~2026-10-06  0:20 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-06  0:20 [RFC PATCH bpf-next 00/12] fs: unified VFS ancestor walk for Landlock and BPF Justin Suess
2026-10-06  0:20 ` [RFC PATCH bpf-next 01/12] namei: introduce __path_walk_parent() Justin Suess
2026-10-06  0:27   ` sashiko-bot
2026-10-06  0:20 ` [RFC PATCH bpf-next 02/12] namei: add vfs_walk_ancestors() Justin Suess
2026-10-06  0:30   ` sashiko-bot
2026-10-06  0:20 ` [RFC PATCH bpf-next 03/12] landlock: convert ancestor walk to vfs_walk_ancestors() Justin Suess
2026-10-06  0:27   ` sashiko-bot
2026-10-06  1:10   ` bot+bpf-ci
2026-10-06  0:20 ` [RFC PATCH bpf-next 04/12] bpf: mark struct path trusted Justin Suess
2026-10-06  0:34   ` sashiko-bot
2026-10-06  1:10   ` bot+bpf-ci
2026-10-06  0:20 ` [RFC PATCH bpf-next 05/12] namei: make vfs_walk_ancestors() stepwise Justin Suess
2026-10-06  0:30   ` sashiko-bot
2026-10-06  0:20 ` [RFC PATCH bpf-next 06/12] bpf: add a path ancestor iterator Justin Suess
2026-10-06  0:30   ` sashiko-bot
2026-10-06  1:11   ` bot+bpf-ci
2026-10-06  0:20 ` [RFC PATCH bpf-next 07/12] selftests/bpf: exercise the " Justin Suess
2026-10-06  0:28   ` sashiko-bot
2026-10-06  1:10   ` bot+bpf-ci
2026-10-06  0:20 ` Justin Suess [this message]
2026-10-06  0:28   ` [RFC PATCH bpf-next 08/12] fs: add mnt_undo_legitimize() sashiko-bot
2026-10-06  0:20 ` [RFC PATCH bpf-next 09/12] namei: add an rcu-walk mode to the ancestor walk Justin Suess
2026-10-06  0:33   ` sashiko-bot
2026-10-06  1:10   ` bot+bpf-ci
2026-10-06  0:20 ` [RFC PATCH bpf-next 10/12] bpf: support "__uninit" iterator arguments in generic kfuncs Justin Suess
2026-10-06  0:37   ` sashiko-bot
2026-10-06 14:58     ` Justin Suess
2026-10-06  0:20 ` [RFC PATCH bpf-next 11/12] bpf: add a lockless path ancestor iterator Justin Suess
2026-10-06  0:31   ` sashiko-bot
2026-10-06  1:10   ` bot+bpf-ci
2026-10-06 14:44     ` Justin Suess
2026-10-06  0:20 ` [RFC PATCH bpf-next 12/12] selftests/bpf: exercise the " Justin Suess
2026-10-06  0:25   ` sashiko-bot
2026-10-06  1:10   ` bot+bpf-ci

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261006002020.2890858-9-utilityemal77@gmail.com \
    --to=utilityemal77@gmail.com \
    --cc=amir73il@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=brauner@kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=gnoack@google.com \
    --cc=jack@suse.cz \
    --cc=jlayton@kernel.org \
    --cc=jmorris@namei.org \
    --cc=john.fastabend@gmail.com \
    --cc=jolsa@kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=m@maowtm.org \
    --cc=martin.lau@linux.dev \
    --cc=memxor@gmail.com \
    --cc=mic@digikod.net \
    --cc=mjguzik@gmail.com \
    --cc=neil@brown.name \
    --cc=paul@paul-moore.com \
    --cc=serge@hallyn.com \
    --cc=shuah@kernel.org \
    --cc=song@kernel.org \
    --cc=viro@zeniv.linux.org.uk \
    --cc=yonghong.song@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.