* FAILED: patch "[PATCH] NFSD: Prevent client use-after-free during close_lru reaping" failed to apply to 5.15-stable tree
@ 2026-09-03 13:34 gregkh
2026-09-08 1:08 ` [PATCH 5.15.y 1/3] NFSD: Prevent client use-after-free during delegation revoke Sasha Levin
0 siblings, 1 reply; 5+ messages in thread
From: gregkh @ 2026-09-03 13:34 UTC (permalink / raw)
To: cel, jlayton; +Cc: stable
The patch below does not apply to the 5.15-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.15.y
git checkout FETCH_HEAD
git cherry-pick -x 2330b788d732f43668b965b3105b37ceb276dfea
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '2026090306-dazzler-deputy-dc16@gregkh' --subject-prefix 'PATCH 5.15.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 2330b788d732f43668b965b3105b37ceb276dfea Mon Sep 17 00:00:00 2001
From: Chuck Lever <cel@kernel.org>
Date: Thu, 9 Jul 2026 13:40:31 -0400
Subject: [PATCH] NFSD: Prevent client use-after-free during close_lru reaping
An nfs4_openowner left on nn->close_lru after its final CLOSE keeps
its last closed stateid in oo_last_closed_stid, holding only a raw
pointer to its nfs4_client. The laundromat reaps timed-out entries,
drops nn->client_lock, and calls nfs4_put_stid(), which dereferences
the client through cl_lock. Nothing pins the client across that
window, so a concurrent force_expire_client() can free it and
nfs4_put_stid() reads freed memory. __destroy_client() hits the same
race, walking clp->cl_openowners without cl_lock.
Pin the client with cl_rpc_users before dropping client_lock, and
skip clients already expiring. __destroy_client() then cleans up its
own close_lru entries through release_last_closed_stateid(), so
teardown no longer races the laundromat.
Fixes: 217526e7ecc9 ("nfsd: protect the close_lru list and oo_last_closed_stid with client_lock")
Cc: stable@vger.kernel.org
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20260709-cel-v4-8-1d519d9be0cb@kernel.org
Signed-off-by: Chuck Lever <cel@kernel.org>
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 4acd02f1642c..20556b8f186a 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -7581,11 +7581,16 @@ nfs4_laundromat(struct nfsd_net *nn)
if (!state_expired(<, oo->oo_time))
break;
list_del_init(&oo->oo_close_lru);
+ clp = oo->oo_owner.so_client;
+ if (is_client_expired(clp))
+ continue;
stp = oo->oo_last_closed_stid;
oo->oo_last_closed_stid = NULL;
+ atomic_inc(&clp->cl_rpc_users);
spin_unlock(&nn->client_lock);
nfs4_put_stid(&stp->st_stid);
spin_lock(&nn->client_lock);
+ put_client_no_renew_locked(clp);
}
spin_unlock(&nn->client_lock);
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 5.15.y 1/3] NFSD: Prevent client use-after-free during delegation revoke
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 ` Sasha Levin
2026-09-08 1:08 ` [PATCH 5.15.y 2/3] NFSD: Consolidate the revocation-path client unpin Sasha Levin
2026-09-08 1:08 ` [PATCH 5.15.y 3/3] NFSD: Prevent client use-after-free during close_lru reaping Sasha Levin
0 siblings, 2 replies; 5+ messages in thread
From: Sasha Levin @ 2026-09-08 1:08 UTC (permalink / raw)
To: stable; +Cc: Chuck Lever, NeilBrown, Jeff Layton, Sasha Levin
From: Chuck Lever <cel@kernel.org>
[ Upstream commit 4683ca76b3b7e5808338491c6eb3c20e6b4894d5 ]
A delegation stateid holds only a bare pointer to its owning
nfs4_client and does not keep it alive. The client survives its
stateids only because __destroy_client() drains cl_delegations and
cl_revoked before free_client() runs.
nfs4_laundromat() breaks that invariant: it unhashes an
expired delegation from cl_delegations, drops deleg_lock, then
revoke_delegation() relinks it onto cl_revoked under cl_lock. In that
window the delegation is on neither list, so client_has_state() can
report no remaining state.
Every teardown path first requires cl_rpc_users to be zero, but
the laundromat holds no such reference. A client whose recalled
delegation has just timed out can therefore reach free_client()
while revoke_delegation() is still about to dereference cl_lock,
a use-after-free.
Pin the client with cl_rpc_users across the revoke so teardown blocks
until it completes, then reap the delegation from cl_revoked. A client
already expiring reaps its own, so skip it and leave the delegation on
del_recall_lru.
Fixes: 3bd64a5ba171 ("nfsd4: implement SEQ4_STATUS_RECALLABLE_STATE_REVOKED")
Cc: stable@vger.kernel.org
Reviewed-by: NeilBrown <neil@brown.name>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20260709-cel-v4-2-1d519d9be0cb@kernel.org
Signed-off-by: Chuck Lever <cel@kernel.org>
[ sashal: Reduced backport -- upstream 4683ca76b3b7e touches 2 file(s), this
backport carries 1. Not backported here:
fs/nfsd/netns.h
This note is generated from the file lists only; see the resolution record
for the reasoning. ]
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 | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index e6c0b1520973e..b7b4c90f9fd8f 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -93,7 +93,7 @@ static void nfsd4_file_hash_remove(struct nfs4_file *fi);
/*
* Currently used for the del_recall_lru and file hash table. In an
* effort to decrease the scope of the client_mutex, this spinlock may
- * eventually cover more:
+ * eventually cover more. It nests outside client_lock:
*/
static DEFINE_SPINLOCK(state_lock);
@@ -6184,6 +6184,7 @@ nfs4_laundromat(struct nfsd_net *nn)
.new_timeo = nn->nfsd4_lease
};
struct nfs4_cpntf_state *cps;
+ struct nfs4_client *clp;
copy_stateid_t *cps_t;
int i;
@@ -6209,6 +6210,18 @@ nfs4_laundromat(struct nfsd_net *nn)
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
if (!state_expired(<, dp->dl_time))
break;
+ clp = dp->dl_stid.sc_client;
+ spin_lock(&nn->client_lock);
+ if (is_client_expired(clp)) {
+ spin_unlock(&nn->client_lock);
+ continue;
+ }
+ /*
+ * Pin without reviving: get_client_locked() would
+ * flip a courtesy client back to NFSD4_ACTIVE.
+ */
+ atomic_inc(&clp->cl_rpc_users);
+ spin_unlock(&nn->client_lock);
WARN_ON(!unhash_delegation_locked(dp));
list_add(&dp->dl_recall_lru, &reaplist);
}
@@ -6216,8 +6229,18 @@ nfs4_laundromat(struct nfsd_net *nn)
while (!list_empty(&reaplist)) {
dp = list_first_entry(&reaplist, struct nfs4_delegation,
dl_recall_lru);
+ 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);
+ }
}
spin_lock(&nn->client_lock);
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 5.15.y 2/3] NFSD: Consolidate the revocation-path client unpin
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
2026-09-08 1:08 ` [PATCH 5.15.y 3/3] NFSD: Prevent client use-after-free during close_lru reaping Sasha Levin
1 sibling, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2026-09-08 1:08 UTC (permalink / raw)
To: stable; +Cc: Chuck Lever, NeilBrown, Jeff Layton, Sasha Levin
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
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 5.15.y 3/3] NFSD: Prevent client use-after-free during close_lru reaping
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 ` [PATCH 5.15.y 2/3] NFSD: Consolidate the revocation-path client unpin Sasha Levin
@ 2026-09-08 1:08 ` Sasha Levin
1 sibling, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2026-09-08 1:08 UTC (permalink / raw)
To: stable; +Cc: Chuck Lever, Jeff Layton, Sasha Levin
From: Chuck Lever <cel@kernel.org>
[ Upstream commit 2330b788d732f43668b965b3105b37ceb276dfea ]
An nfs4_openowner left on nn->close_lru after its final CLOSE keeps
its last closed stateid in oo_last_closed_stid, holding only a raw
pointer to its nfs4_client. The laundromat reaps timed-out entries,
drops nn->client_lock, and calls nfs4_put_stid(), which dereferences
the client through cl_lock. Nothing pins the client across that
window, so a concurrent force_expire_client() can free it and
nfs4_put_stid() reads freed memory. __destroy_client() hits the same
race, walking clp->cl_openowners without cl_lock.
Pin the client with cl_rpc_users before dropping client_lock, and
skip clients already expiring. __destroy_client() then cleans up its
own close_lru entries through release_last_closed_stateid(), so
teardown no longer races the laundromat.
Fixes: 217526e7ecc9 ("nfsd: protect the close_lru list and oo_last_closed_stid with client_lock")
Cc: stable@vger.kernel.org
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20260709-cel-v4-8-1d519d9be0cb@kernel.org
Signed-off-by: Chuck Lever <cel@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/nfsd/nfs4state.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 2026725344a12..af3e5e8822564 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -6269,11 +6269,16 @@ nfs4_laundromat(struct nfsd_net *nn)
if (!state_expired(<, oo->oo_time))
break;
list_del_init(&oo->oo_close_lru);
+ clp = oo->oo_owner.so_client;
+ if (is_client_expired(clp))
+ continue;
stp = oo->oo_last_closed_stid;
oo->oo_last_closed_stid = NULL;
+ atomic_inc(&clp->cl_rpc_users);
spin_unlock(&nn->client_lock);
nfs4_put_stid(&stp->st_stid);
spin_lock(&nn->client_lock);
+ put_client_no_renew_locked(clp);
}
spin_unlock(&nn->client_lock);
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* FAILED: patch "[PATCH] NFSD: Prevent client use-after-free during blocked-lock" failed to apply to 5.15-stable tree
@ 2026-09-03 13:32 gregkh
2026-09-08 1:13 ` [PATCH 5.15.y 1/3] NFSD: Prevent client use-after-free during delegation revoke Sasha Levin
0 siblings, 1 reply; 5+ messages in thread
From: gregkh @ 2026-09-03 13:32 UTC (permalink / raw)
To: cel, jlayton; +Cc: stable
The patch below does not apply to the 5.15-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.15.y
git checkout FETCH_HEAD
git cherry-pick -x 9026932ac8be4d0ae01db47f23619a98cc57b671
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '2026090333-gutter-request-d440@gregkh' --subject-prefix 'PATCH 5.15.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 9026932ac8be4d0ae01db47f23619a98cc57b671 Mon Sep 17 00:00:00 2001
From: Chuck Lever <cel@kernel.org>
Date: Thu, 9 Jul 2026 13:40:30 -0400
Subject: [PATCH] NFSD: Prevent client use-after-free during blocked-lock
reaping
A bare lock owner -- its only remaining reference a blocked lock on
nn->blocked_locks_lru -- holds a raw pointer to its nfs4_client but
no reference keeping the client alive. When the per-net laundromat
reaps such a lock, freeing the nbl drops the owner reference
held through flc_owner, and the final nfs4_put_stateowner()
takes the client's cl_lock. Because the laundromat detaches the
nbl first, __destroy_client() no longer finds it, so a concurrent
force_expire_client() can free the client before nfs4_put_stateowner()
runs, dereferencing cl_lock in freed memory.
Pin the client with cl_rpc_users before dropping
nn->blocked_locks_lock, and skip clients already expiring, whose
blocked locks __destroy_client() frees while holding an owner
reference. Take nn->client_lock outside nn->blocked_locks_lock.
Every other site holds nn->blocked_locks_lock as a leaf, acquiring
no further lock, so placing nn->client_lock outside it cannot form
a lock-order cycle.
Fixes: 7919d0a27f1e ("nfsd: add a LRU list for blocked locks")
Cc: stable@vger.kernel.org
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20260709-cel-v4-7-1d519d9be0cb@kernel.org
Signed-off-by: Chuck Lever <cel@kernel.org>
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 142ba7d80539..4acd02f1642c 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -357,6 +357,16 @@ free_blocked_lock(struct nfsd4_blocked_lock *nbl)
kref_put(&nbl->nbl_kref, free_nbl);
}
+/* A blocked lock's flc_owner is its nfs4_lockowner. */
+static struct nfs4_client *
+nbl_client(struct nfsd4_blocked_lock *nbl)
+{
+ struct nfs4_lockowner *lo;
+
+ lo = (struct nfs4_lockowner *)nbl->nbl_lock.c.flc_owner;
+ return lo->lo_owner.so_client;
+}
+
static void
remove_blocked_locks(struct nfs4_lockowner *lo)
{
@@ -7591,22 +7601,29 @@ nfs4_laundromat(struct nfsd_net *nn)
* indefinitely once the lock does become free.
*/
BUG_ON(!list_empty(&reaplist));
+ spin_lock(&nn->client_lock);
spin_lock(&nn->blocked_locks_lock);
- while (!list_empty(&nn->blocked_locks_lru)) {
- nbl = list_first_entry(&nn->blocked_locks_lru,
- struct nfsd4_blocked_lock, nbl_lru);
+ list_for_each_safe(pos, next, &nn->blocked_locks_lru) {
+ nbl = list_entry(pos, struct nfsd4_blocked_lock, nbl_lru);
if (!state_expired(<, nbl->nbl_time))
break;
+ clp = nbl_client(nbl);
+ if (is_client_expired(clp))
+ continue;
+ atomic_inc(&clp->cl_rpc_users);
list_move(&nbl->nbl_lru, &reaplist);
list_del_init(&nbl->nbl_list);
}
spin_unlock(&nn->blocked_locks_lock);
+ spin_unlock(&nn->client_lock);
while (!list_empty(&reaplist)) {
nbl = list_first_entry(&reaplist,
struct nfsd4_blocked_lock, nbl_lru);
+ clp = nbl_client(nbl);
list_del_init(&nbl->nbl_lru);
free_blocked_lock(nbl);
+ put_client_no_renew(clp);
}
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
/* service the server-to-server copy delayed unmount list */
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 5.15.y 1/3] NFSD: Prevent client use-after-free during delegation revoke
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 ` Sasha Levin
2026-09-08 1:13 ` [PATCH 5.15.y 2/3] NFSD: Consolidate the revocation-path client unpin Sasha Levin
0 siblings, 1 reply; 5+ messages in thread
From: Sasha Levin @ 2026-09-08 1:13 UTC (permalink / raw)
To: stable; +Cc: Chuck Lever, NeilBrown, Jeff Layton, Sasha Levin
From: Chuck Lever <cel@kernel.org>
[ Upstream commit 4683ca76b3b7e5808338491c6eb3c20e6b4894d5 ]
A delegation stateid holds only a bare pointer to its owning
nfs4_client and does not keep it alive. The client survives its
stateids only because __destroy_client() drains cl_delegations and
cl_revoked before free_client() runs.
nfs4_laundromat() breaks that invariant: it unhashes an
expired delegation from cl_delegations, drops deleg_lock, then
revoke_delegation() relinks it onto cl_revoked under cl_lock. In that
window the delegation is on neither list, so client_has_state() can
report no remaining state.
Every teardown path first requires cl_rpc_users to be zero, but
the laundromat holds no such reference. A client whose recalled
delegation has just timed out can therefore reach free_client()
while revoke_delegation() is still about to dereference cl_lock,
a use-after-free.
Pin the client with cl_rpc_users across the revoke so teardown blocks
until it completes, then reap the delegation from cl_revoked. A client
already expiring reaps its own, so skip it and leave the delegation on
del_recall_lru.
Fixes: 3bd64a5ba171 ("nfsd4: implement SEQ4_STATUS_RECALLABLE_STATE_REVOKED")
Cc: stable@vger.kernel.org
Reviewed-by: NeilBrown <neil@brown.name>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20260709-cel-v4-2-1d519d9be0cb@kernel.org
Signed-off-by: Chuck Lever <cel@kernel.org>
Stable-dep-of: 9026932ac8be ("NFSD: Prevent client use-after-free during blocked-lock reaping")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/nfsd/netns.h | 3 ++-
fs/nfsd/nfs4state.c | 29 ++++++++++++++++++++++++++---
2 files changed, 28 insertions(+), 4 deletions(-)
diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index be0c8f1ce4e3e..ad457a1b48157 100644
--- a/fs/nfsd/netns.h
+++ b/fs/nfsd/netns.h
@@ -105,7 +105,8 @@ struct nfsd_net {
struct delayed_work laundromat_work;
- /* client_lock protects the client lru list and session hash table */
+ /* client_lock protects the client lru list and session hash
+ * table; nests inside state_lock */
spinlock_t client_lock;
/* protects blocked_locks_lru */
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index e6c0b1520973e..a29b7e317b16a 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -91,9 +91,9 @@ static void nfsd4_file_hash_remove(struct nfs4_file *fi);
/* Locking: */
/*
- * Currently used for the del_recall_lru and file hash table. In an
- * effort to decrease the scope of the client_mutex, this spinlock may
- * eventually cover more:
+ * Currently used for the del_recall_lru and file hash table. It nests
+ * outside client_lock. In an effort to decrease the scope of the
+ * client_mutex, this spinlock may eventually cover more:
*/
static DEFINE_SPINLOCK(state_lock);
@@ -6184,6 +6184,7 @@ nfs4_laundromat(struct nfsd_net *nn)
.new_timeo = nn->nfsd4_lease
};
struct nfs4_cpntf_state *cps;
+ struct nfs4_client *clp;
copy_stateid_t *cps_t;
int i;
@@ -6209,6 +6210,18 @@ nfs4_laundromat(struct nfsd_net *nn)
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
if (!state_expired(<, dp->dl_time))
break;
+ clp = dp->dl_stid.sc_client;
+ spin_lock(&nn->client_lock);
+ if (is_client_expired(clp)) {
+ spin_unlock(&nn->client_lock);
+ continue;
+ }
+ /*
+ * Pin without reviving: get_client_locked() would
+ * flip a courtesy client back to NFSD4_ACTIVE.
+ */
+ atomic_inc(&clp->cl_rpc_users);
+ spin_unlock(&nn->client_lock);
WARN_ON(!unhash_delegation_locked(dp));
list_add(&dp->dl_recall_lru, &reaplist);
}
@@ -6216,8 +6229,18 @@ nfs4_laundromat(struct nfsd_net *nn)
while (!list_empty(&reaplist)) {
dp = list_first_entry(&reaplist, struct nfs4_delegation,
dl_recall_lru);
+ 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);
+ }
}
spin_lock(&nn->client_lock);
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 5.15.y 2/3] NFSD: Consolidate the revocation-path client unpin
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 ` Sasha Levin
0 siblings, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2026-09-08 1:13 UTC (permalink / raw)
To: stable; +Cc: Chuck Lever, NeilBrown, Jeff Layton, Sasha Levin
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: 9026932ac8be ("NFSD: Prevent client use-after-free during blocked-lock reaping")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/nfsd/nfs4state.c | 47 +++++++++++++++++++++++++++------------------
1 file changed, 28 insertions(+), 19 deletions(-)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index a29b7e317b16a..4468c24145687 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,17 @@ 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 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 +6249,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
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-08 1:13 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH 5.15.y 2/3] NFSD: Consolidate the revocation-path client unpin Sasha Levin
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
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.