From: Sasha Levin <sashal@kernel.org>
To: stable@vger.kernel.org
Cc: Chuck Lever <cel@kernel.org>, NeilBrown <neil@brown.name>,
Jeff Layton <jlayton@kernel.org>, Sasha Levin <sashal@kernel.org>
Subject: [PATCH 5.15.y 2/3] NFSD: Consolidate the revocation-path client unpin
Date: Mon, 7 Sep 2026 21:08:12 -0400 [thread overview]
Message-ID: <20260908010813.1681295-2-sashal@kernel.org> (raw)
In-Reply-To: <20260908010813.1681295-1-sashal@kernel.org>
From: Chuck Lever <cel@kernel.org>
[ Upstream commit 3308cf3f11ed23c79f9f3f90b34bbbad3e3a6ea9 ]
The client use-after-free fixes in the state-revocation paths left
four open-coded copies of one idiom: drop a cl_rpc_users pin without
renewing the client's lease, waking force_expire_client() when the
last pin drops on a client it is tearing down. The accompanying "do
not renew" rationale was documented at only one of the four sites.
put_client_renew_locked() and put_client_renew() already carry the
same pin-drop logic, but they renew a non-expired client's lease and
so would resurrect the client whose state is being revoked. Factor
the common pin-drop into __put_client_locked(), parameterized by
whether to renew. The renew helpers pass true; the new
put_client_no_renew_locked() and put_client_no_renew() pass false and
carry the revocation paths, which must not revive the client they are
tearing down. No change in behavior.
Reviewed-by: NeilBrown <neil@brown.name>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20260709-cel-v4-6-1d519d9be0cb@kernel.org
Signed-off-by: Chuck Lever <cel@kernel.org>
Stable-dep-of: 2330b788d732 ("NFSD: Prevent client use-after-free during close_lru reaping")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/nfsd/nfs4state.c | 57 ++++++++++++++++++++++++++++++---------------
1 file changed, 38 insertions(+), 19 deletions(-)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index b7b4c90f9fd8f..2026725344a12 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -205,18 +205,28 @@ renew_client_locked(struct nfs4_client *clp)
clp->cl_state = NFSD4_ACTIVE;
}
+/*
+ * Finish a cl_rpc_users unpin with the client_lock held. A
+ * revocation walk clears @renew so the client whose state it is
+ * revoking is not revived; every other caller renews the lease of
+ * a still-active client.
+ */
+static void __put_client_locked(struct nfs4_client *clp, bool renew)
+{
+ if (is_client_expired(clp))
+ wake_up_all(&expiry_wq);
+ else if (renew)
+ renew_client_locked(clp);
+}
+
static void put_client_renew_locked(struct nfs4_client *clp)
{
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
lockdep_assert_held(&nn->client_lock);
- if (!atomic_dec_and_test(&clp->cl_rpc_users))
- return;
- if (!is_client_expired(clp))
- renew_client_locked(clp);
- else
- wake_up_all(&expiry_wq);
+ if (atomic_dec_and_test(&clp->cl_rpc_users))
+ __put_client_locked(clp, true);
}
static void put_client_renew(struct nfs4_client *clp)
@@ -225,10 +235,27 @@ static void put_client_renew(struct nfs4_client *clp)
if (!atomic_dec_and_lock(&clp->cl_rpc_users, &nn->client_lock))
return;
- if (!is_client_expired(clp))
- renew_client_locked(clp);
- else
- wake_up_all(&expiry_wq);
+ __put_client_locked(clp, true);
+ spin_unlock(&nn->client_lock);
+}
+
+static void __maybe_unused put_client_no_renew_locked(struct nfs4_client *clp)
+{
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+ lockdep_assert_held(&nn->client_lock);
+
+ if (atomic_dec_and_test(&clp->cl_rpc_users))
+ __put_client_locked(clp, false);
+}
+
+static void put_client_no_renew(struct nfs4_client *clp)
+{
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+ if (!atomic_dec_and_lock(&clp->cl_rpc_users, &nn->client_lock))
+ return;
+ __put_client_locked(clp, false);
spin_unlock(&nn->client_lock);
}
@@ -6232,15 +6259,7 @@ nfs4_laundromat(struct nfsd_net *nn)
clp = dp->dl_stid.sc_client;
list_del_init(&dp->dl_recall_lru);
revoke_delegation(dp);
- /*
- * Unpin without renewing: put_client_renew() would
- * renew the reaped client's lease.
- */
- if (atomic_dec_and_lock(&clp->cl_rpc_users, &nn->client_lock)) {
- if (is_client_expired(clp))
- wake_up_all(&expiry_wq);
- spin_unlock(&nn->client_lock);
- }
+ put_client_no_renew(clp);
}
spin_lock(&nn->client_lock);
--
2.53.0
next prev parent reply other threads:[~2026-09-08 1:08 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-03 13:34 FAILED: patch "[PATCH] NFSD: Prevent client use-after-free during close_lru reaping" failed to apply to 5.15-stable tree gregkh
2026-09-08 1:08 ` [PATCH 5.15.y 1/3] NFSD: Prevent client use-after-free during delegation revoke Sasha Levin
2026-09-08 1:08 ` Sasha Levin [this message]
2026-09-08 1:08 ` [PATCH 5.15.y 3/3] NFSD: Prevent client use-after-free during close_lru reaping Sasha Levin
-- strict thread matches above, loose matches on Subject: below --
2026-09-03 13:32 FAILED: patch "[PATCH] NFSD: Prevent client use-after-free during blocked-lock" failed to apply to 5.15-stable tree gregkh
2026-09-08 1:13 ` [PATCH 5.15.y 1/3] NFSD: Prevent client use-after-free during delegation revoke Sasha Levin
2026-09-08 1:13 ` [PATCH 5.15.y 2/3] NFSD: Consolidate the revocation-path client unpin Sasha Levin
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=20260908010813.1681295-2-sashal@kernel.org \
--to=sashal@kernel.org \
--cc=cel@kernel.org \
--cc=jlayton@kernel.org \
--cc=neil@brown.name \
--cc=stable@vger.kernel.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox