From: Chuck Lever <cel@kernel.org>
To: Jeff Layton <jlayton@kernel.org>, NeilBrown <neil@brown.name>,
Olga Kornievskaia <okorniev@redhat.com>,
Dai Ngo <Dai.Ngo@oracle.com>, Tom Talpey <tom@talpey.com>
Cc: linux-nfs@vger.kernel.org, Chuck Lever <cel@kernel.org>
Subject: [PATCH v2 3/6] NFSD: Prevent client use-after-free during admin state revocation
Date: Sun, 05 Jul 2026 21:25:39 -0400 [thread overview]
Message-ID: <20260705-cel-v2-3-d88c3b68e8bc@kernel.org> (raw)
In-Reply-To: <20260705-cel-v2-0-d88c3b68e8bc@kernel.org>
A stateid stores only a bare pointer to its owning nfs4_client; a
reference on the stateid does not keep the client alive. The client
outlives its stateids only because __destroy_client() drains them
before free_client() runs.
nfsd4_revoke_states() drops nn->client_lock across revoke_one_stid(),
which revokes the delegation, open, lock, or layout stateid and
dereferences its client -- revoke_delegation() and revoke_ol_stid()
both reacquire clp->cl_lock. The stateid reference it holds does not
pin the client, so a teardown racing the dropped lock can run
__destroy_client() and free the client first. The subsequent read of
clp->cl_minorversion is exposed the same way.
A DESTROY_CLIENTID or superseding EXCHANGE_ID reaches teardown through
mark_client_expired_locked(), which refuses while cl_rpc_users is
non-zero, so pinning the client under client_lock holds those paths
off. force_expire_client() does not consult cl_rpc_users: it zeroes
cl_time under client_lock, waits once for cl_rpc_users to reach zero,
then unhashes and frees. A pin taken after that wait goes unnoticed,
so the walk must also skip a client that is already expiring.
Under client_lock, skip a client whose cl_time is zero and otherwise
pin it with cl_rpc_users before dropping the lock. cl_time is set
under the same lock, so the walk either sees the expiry and skips, or
pins early enough that force_expire_client() waits for the revoke and
the cl_minorversion update to finish.
Fixes: 1c13bf9f2e3c ("nfsd: allow lock state ids to be revoked and then freed")
Signed-off-by: Chuck Lever <cel@kernel.org>
---
fs/nfsd/nfs4state.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index efeb2a2e9c8f..cdb62b3bf718 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1965,9 +1965,19 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
struct nfs4_client *clp;
retry:
list_for_each_entry(clp, head, cl_idhash) {
- struct nfs4_stid *stid = find_one_sb_stid(clp, sb,
- sc_types);
+ struct nfs4_stid *stid;
+
+ /*
+ * force_expire_client() ignores cl_rpc_users once
+ * its wait_event() has passed, so pinning cannot
+ * keep an already-expiring client alive; the
+ * expiry path revokes its states instead.
+ */
+ if (is_client_expired(clp))
+ continue;
+ stid = find_one_sb_stid(clp, sb, sc_types);
if (stid) {
+ atomic_inc(&clp->cl_rpc_users);
spin_unlock(&nn->client_lock);
revoke_one_stid(nn, clp, stid);
nfs4_put_stid(stid);
@@ -1980,6 +1990,9 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
*/
nn->nfs40_last_revoke =
ktime_get_boottime_seconds();
+ if (atomic_dec_and_test(&clp->cl_rpc_users) &&
+ is_client_expired(clp))
+ wake_up_all(&expiry_wq);
goto retry;
}
}
--
2.54.0
next prev parent reply other threads:[~2026-07-06 1:26 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-06 1:25 [PATCH v2 0/6] NFSD: Fix UAFs in client teardown and state revocation Chuck Lever
2026-07-06 1:25 ` [PATCH v2 1/6] NFSD: Prevent lock owner use-after-free during client teardown Chuck Lever
2026-07-06 1:25 ` [PATCH v2 2/6] NFSD: Prevent client use-after-free during delegation revoke Chuck Lever
2026-07-06 17:29 ` Jeff Layton
2026-07-06 1:25 ` Chuck Lever [this message]
2026-07-06 1:25 ` [PATCH v2 4/6] NFSD: Prevent client use-after-free during export state revocation Chuck Lever
2026-07-06 1:25 ` [PATCH v2 5/6] NFSD: Prevent client use-after-free during NFSv4.0 revoked-state cleanup Chuck Lever
2026-07-06 1:25 ` [PATCH v2 6/6] NFSD: Consolidate the revocation-path client unpin Chuck Lever
2026-07-06 3:53 ` [PATCH v2 0/6] NFSD: Fix UAFs in client teardown and state revocation NeilBrown
2026-07-06 16:42 ` Jeff Layton
2026-07-06 17:14 ` Chuck Lever
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260705-cel-v2-3-d88c3b68e8bc@kernel.org \
--to=cel@kernel.org \
--cc=Dai.Ngo@oracle.com \
--cc=jlayton@kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=neil@brown.name \
--cc=okorniev@redhat.com \
--cc=tom@talpey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.