diff options
| author | Chuck Lever <chuck.lever@oracle.com> | 2026-03-24 11:18:12 -0400 |
|---|---|---|
| committer | Chuck Lever <chuck.lever@oracle.com> | 2026-05-28 11:31:26 -0400 |
| commit | dda027301d634b6f0da021467c9e42c28f22de40 (patch) | |
| tree | 5ee9a98f25b39eee74fada794466b2bdb01ad300 /fs | |
| parent | c9f3f22ab7a9549dfa76f173f3c75b684e103a08 (diff) | |
| download | linux-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.c | 9 |
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); |
