aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
authorChuck Lever <chuck.lever@oracle.com>2026-03-24 11:18:12 -0400
committerChuck Lever <chuck.lever@oracle.com>2026-05-28 11:31:26 -0400
commitdda027301d634b6f0da021467c9e42c28f22de40 (patch)
tree5ee9a98f25b39eee74fada794466b2bdb01ad300 /fs
parentc9f3f22ab7a9549dfa76f173f3c75b684e103a08 (diff)
downloadlinux-next-history-dda027301d634b6f0da021467c9e42c28f22de40.tar.gz
NFSD: Fix delegation reference leak in nfsd4_revoke_states
When revoking delegation state, nfsd4_revoke_states() takes an extra reference on the stid before calling unhash_delegation_locked(). If unhash_delegation_locked() returns false (the delegation was already unhashed by a concurrent path), dp is set to NULL and revoke_delegation() is skipped, but the extra reference is never released. Each occurrence permanently pins the stid in memory. The leaked reference also prevents nfs4_put_stid() from decrementing cl_admin_revoked, leaving the counter permanently inflated. Drop the extra reference in the failure path. Fixes: 8dd91e8d31fe ("nfsd: fix race between laundromat and free_stateid") Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/nfsd/nfs4state.c9
1 files changed, 8 insertions, 1 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6837b63d98645..3c2eb03f78c6d 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1376,7 +1376,8 @@ static void destroy_delegation(struct nfs4_delegation *dp)
* stateid or it's called from a laundromat thread (nfsd4_landromat()) that
* determined that this specific state has expired and needs to be revoked
* (both mark state with the appropriate stid sc_status mode). It is also
- * assumed that a reference was taken on the @dp state.
+ * assumed that a reference was taken on the @dp state. This function
+ * consumes that reference.
*
* If this function finds that the @dp state is SC_STATUS_FREED it means
* that a FREE_STATEID operation for this stateid has been processed and
@@ -1839,6 +1840,10 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
mutex_unlock(&stp->st_mutex);
break;
case SC_TYPE_DELEG:
+ /* Extra reference guards against concurrent
+ * FREE_STATEID; revoke_delegation() consumes
+ * it, otherwise release it directly.
+ */
refcount_inc(&stid->sc_count);
dp = delegstateid(stid);
spin_lock(&nn->deleg_lock);
@@ -1848,6 +1853,8 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
spin_unlock(&nn->deleg_lock);
if (dp)
revoke_delegation(dp);
+ else
+ nfs4_put_stid(stid);
break;
case SC_TYPE_LAYOUT:
ls = layoutstateid(stid);