nfsd: hold cl_lock in client_has_openowners()

client_has_openowners() walks clp->cl_openowners and reads so_stateids
without clp->cl_lock. nfs4_put_stateowner() unhashes that openowner under
cl_lock and then frees it, so a concurrent EXCHANGE_ID with mismatched
creds can use-after-free the nfs4_openowner.

BUG: KASAN: slab-use-after-free in client_has_state+0x10a/0x140
  fs/nfsd/nfs4state.c:3718 client_has_openowners()
  nfsd4_exchange_id
  nfsd4_proc_compound
  nfsd_dispatch
  svc_process

Take clp->cl_lock while client_has_openowners() walks the openowner
list.

Fixes: 4eaea13425 ("nfsd: improve client_has_state to check for unused openowners")
Cc: stable@vger.kernel.org
Reported-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
Cc: AutonomousCodeSecurity@microsoft.com
Signed-off-by: Cen Zhang (Microsoft Security FORGE Labs) <blbllhy@gmail.com>
Link: https://patch.msgid.link/20260828041925.36758-1-blbllhy@gmail.com
Signed-off-by: Chuck Lever <cel@kernel.org>
This commit is contained in:
Cen Zhang (Microsoft Security FORGE Labs) 2026-08-28 00:19:25 -04:00 committed by Chuck Lever
parent 2288839975
commit 5336899b54

View File

@ -4251,12 +4251,18 @@ nfsd4_set_ex_flags(struct nfs4_client *new, struct nfsd4_exchange_id *clid)
static bool client_has_openowners(struct nfs4_client *clp)
{
struct nfs4_openowner *oo;
bool found = false;
spin_lock(&clp->cl_lock);
list_for_each_entry(oo, &clp->cl_openowners, oo_perclient) {
if (!list_empty(&oo->oo_owner.so_stateids))
return true;
if (!list_empty(&oo->oo_owner.so_stateids)) {
found = true;
break;
}
}
return false;
spin_unlock(&clp->cl_lock);
return found;
}
static bool client_has_state(struct nfs4_client *clp)