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>
This commit is contained in:
Chuck Lever
2026-06-13 18:16:34 -04:00
parent 292d915d3b
commit 5f367f0548
2 changed files with 21 additions and 1 deletions

View File

@@ -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];

View File

@@ -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);