Re: [PATCH v4 1/9] NFSD: Prevent lock owner use-after-free during client teardown

Thorsten Leemhuis <[email protected]> Wed, 22 Jul 2026 12:24:15 +0200
Newsgroups dev.linux.lists.regressions,org.kernel.vger.linux-nfs
Message-ID <[email protected]>
On 7/9/26 19:40, Chuck Lever wrote:
> __destroy_client() releases a client's open owners, but a lock owner
> whose only reference is a blocked lock (nbl) stays on
> cl_ownerstr_hashtbl.  client_has_state() does not count a bare owner,
> so DESTROY_CLIENTID can reach __destroy_client() with such owners
> present.
>
> [...]
>
> Reported-by: Wolfgang Walter <[email protected]>
> Closes: https://lore.kernel.org/linux-nfs/[email protected]/
> Fixes: 68ef3bc31664 ("nfsd: remove blocked locks on client teardown")

What's the plan to merge this change? From the outside it looks like a
fix for a user reported regression from this merge window, as Wolfgang
afaics blamed commit 516403d4d85607 ("Revert "NFSD: Defer sub-object
cleanup in export put callbacks""), which also made it to various stable
trees.

So shouldn't this change ideally be on it's way to Linus by now? Ideally
with a stable tag to ensure quick backporting? Or is it not a good idea
to have this in without the follow-up patches from this series?

Or did I get something totally wrong (which is easily possible from my
external point)?

Ciao, Thorsten


> Reviewed-by: NeilBrown <[email protected]>
> Reviewed-by: Jeff Layton <[email protected]>
> Signed-off-by: Chuck Lever <[email protected]>
> ---
>  fs/nfsd/nfs4state.c | 16 +++++++++++++---
>  1 file changed, 13 insertions(+), 3 deletions(-)
> 
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index a4398dc861a5..e000ed3e96e9 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -2758,14 +2758,24 @@ __destroy_client(struct nfs4_client *clp)
>  		release_openowner(oo);
>  	}
>  	for (i = 0; i < OWNER_HASH_SIZE; i++) {
> -		struct nfs4_stateowner *so, *tmp;
> +		struct nfs4_stateowner *so;
>  
> -		list_for_each_entry_safe(so, tmp, &clp->cl_ownerstr_hashtbl[i],
> -					 so_strhash) {
> +		spin_lock(&clp->cl_lock);
> +		while (!list_empty(&clp->cl_ownerstr_hashtbl[i])) {
> +			so = list_first_entry(&clp->cl_ownerstr_hashtbl[i],
> +					      struct nfs4_stateowner, so_strhash);
>  			/* Should be no openowners at this point */
>  			WARN_ON_ONCE(so->so_is_open_owner);
> +			nfs4_get_stateowner(so);
> +			unhash_lockowner_locked(lockowner(so));
> +			spin_unlock(&clp->cl_lock);
> +
>  			remove_blocked_locks(lockowner(so));
> +			nfs4_put_stateowner(so);
> +
> +			spin_lock(&clp->cl_lock);
>  		}
> +		spin_unlock(&clp->cl_lock);
>  	}
>  	nfsd4_return_all_client_layouts(clp);
>  	nfsd4_shutdown_copy(clp);
>