summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChuck Lever <chuck.lever@oracle.com>2026-03-24 18:18:12 +0300
committerChuck Lever <chuck.lever@oracle.com>2026-06-01 18:08:18 +0300
commit625981c8f3da0cc2d236d7b46c39dd75554b8276 (patch)
tree7ae7e47f8ae4fdbf8ceb5b4135bb8ff14a9d5882
parente7f558158edda53b89b456cc5795807459914f2e (diff)
downloadlinux-625981c8f3da0cc2d236d7b46c39dd75554b8276.tar.xz
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>
-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 6837b63d9864..3c2eb03f78c6 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);