summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChuck Lever <cel@kernel.org>2026-06-13 18:16:34 -0400
committerChuck Lever <cel@kernel.org>2026-08-10 09:54:35 -0400
commit5f367f05481d56be90f8c13eee4cb421cd7af2d8 (patch)
tree8019ae5bb775d950a96375956d7d25436dcedd00
parent292d915d3ba6fd15eeb88351fa10581683073109 (diff)
NFSD: Annotate caller preconditions for the state-table walkers
The state-table walkers now assert nfsd_mutex with lockdep_assert_held() and document the nfsd_mutex / nn->nfsd_serv precondition in a Context: kdoc section, so the next caller added to this path cannot silently reintroduce the same use-after-free. Reviewed-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260613-unlock-filesystem-uaf-v1-3-462b9bec8c84@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
-rw-r--r--fs/nfsd/nfs4proc.c6
-rw-r--r--fs/nfsd/nfs4state.c16
2 files changed, 21 insertions, 1 deletions
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index eb8a2a16839f..3e4de45aa360 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1587,6 +1587,11 @@ static bool nfsd4_copy_on_sb(const struct nfsd4_copy *copy,
* nfsd4_cancel_copy_by_sb - cancel async copy operations on @sb
* @net: net namespace containing the copy operations
* @sb: targeted superblock
+ *
+ * Context: Caller must hold nfsd_mutex with nn->nfsd_serv confirmed
+ * non-NULL. nfs4_state_destroy_net() frees conf_id_hashtbl
+ * at server shutdown without clearing the pointer, so a
+ * walk without these guarantees iterates freed slab memory.
*/
void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb)
{
@@ -1596,6 +1601,7 @@ void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb)
unsigned int idhashval;
LIST_HEAD(to_cancel);
+ lockdep_assert_held(&nfsd_mutex);
spin_lock(&nn->client_lock);
for (idhashval = 0; idhashval < CLIENT_HASH_SIZE; idhashval++) {
struct list_head *head = &nn->conf_id_hashtbl[idhashval];
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index bef0ec9be459..2a6a0c9ef65f 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1873,14 +1873,21 @@ static void revoke_one_stid(struct nfsd_net *nn, struct nfs4_client *clp,
* being released. Thus nfsd will no longer prevent the filesystem from being
* unmounted.
*
- * The clients which own the states will subsequently being notified that the
+ * The clients which own the states will subsequently be notified that the
* states have been "admin-revoked".
+ *
+ * Context: Caller must hold nfsd_mutex with nn->nfsd_serv confirmed
+ * non-NULL. nfs4_state_destroy_net() frees conf_id_hashtbl
+ * at server shutdown without clearing the pointer, so a
+ * walk without these guarantees iterates freed slab memory.
*/
void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
{
unsigned int idhashval;
unsigned int sc_types;
+ lockdep_assert_held(&nfsd_mutex);
+
sc_types = SC_TYPE_OPEN | SC_TYPE_LOCK | SC_TYPE_DELEG | SC_TYPE_LAYOUT;
spin_lock(&nn->client_lock);
@@ -1946,12 +1953,19 @@ static struct nfs4_stid *find_one_export_stid(struct nfs4_client *clp,
*
* Userspace (exportfs -u) sends this after removing the last client
* for a path, enabling the underlying filesystem to be unmounted.
+ *
+ * Context: Caller must hold nfsd_mutex with nn->nfsd_serv confirmed
+ * non-NULL. nfs4_state_destroy_net() frees conf_id_hashtbl
+ * at server shutdown without clearing the pointer, so a
+ * walk without these guarantees iterates freed slab memory.
*/
void nfsd4_revoke_export_states(struct nfsd_net *nn, const struct path *path)
{
unsigned int idhashval;
unsigned int sc_types;
+ lockdep_assert_held(&nfsd_mutex);
+
sc_types = SC_TYPE_OPEN | SC_TYPE_LOCK | SC_TYPE_DELEG | SC_TYPE_LAYOUT;
spin_lock(&nn->client_lock);