* [PATCH] nfsd: fix use-after-free of DRC slot in nfsd4_sequence()
@ 2026-08-19 5:43 Tien Phan
2026-08-19 5:52 ` Greg KH
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Tien Phan @ 2026-08-19 5:43 UTC (permalink / raw)
To: security; +Cc: Chuck Lever, NeilBrown, Jeff Layton, Tien Phan, stable
nfsd4_sequence() loads the slot addressed by the client-supplied
seq->slotid, bounded only against se_fchannel.maxreqs:
if (seq->slotid >= session->se_fchannel.maxreqs)
goto out_put_session;
slot = xa_load(&session->se_slots, seq->slotid);
When the client acknowledges a slot-count reduction, the handler frees
every slot in [se_target_maxslots, maxreqs):
if (session->se_target_maxslots < session->se_fchannel.maxreqs &&
slot->sl_generation == session->se_slot_gen &&
seq->maxslots <= session->se_target_maxslots)
free_session_slots(session, session->se_target_maxslots);
but never checks that the slot it just loaded lies outside that range.
A malicious NFSv4.1 client can pick seq->slotid >= se_target_maxslots
(still < maxreqs, so the bound above passes) and, once the DRC slot
shrinker has lowered se_target_maxslots below maxreqs, issue two SEQUENCE
calls on that high slot: the first aligns slot->sl_generation with
se_slot_gen, the second satisfies the reduce-ack condition and calls
free_session_slots(), which kfree()s the very slot still referenced by
the handler. The slot is then written (slot->sl_seqid = seq->seqid,
slot->sl_flags, cstate->slot = slot) and used for DRC caching -- a
use-after-free write.
Reject a slotid that falls within the range about to be freed before
freeing it, so the handler never operates on a freed slot.
Found with an in-tree KASAN harness driving the real free_session_slots()
on a live slot xarray (KASAN: slab-use-after-free, write of size 4).
Fixes: fc8738c68d0b ("nfsd: add support for freeing unused session-DRC slots")
Cc: stable@vger.kernel.org # 6.14+
Signed-off-by: Tien Phan <tienpp@gmail.com>
---
fs/nfsd/nfs4state.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index a42f34842d77..f737b841b5a2 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4575,9 +4575,19 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (session->se_target_maxslots < session->se_fchannel.maxreqs &&
slot->sl_generation == session->se_slot_gen &&
- seq->maxslots <= session->se_target_maxslots)
+ seq->maxslots <= session->se_target_maxslots) {
/* Client acknowledged our reduce maxreqs */
+ if (seq->slotid >= session->se_target_maxslots) {
+ /*
+ * ... but is using a slot from the range we are
+ * about to free. Proceeding would leave the code
+ * below dereferencing a freed slot (use-after-free).
+ */
+ status = nfserr_badslot;
+ goto out_put_session;
+ }
free_session_slots(session, session->se_target_maxslots);
+ }
buflen = (seq->cachethis) ?
session->se_fchannel.maxresp_cached :
--
2.34.1
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH] nfsd: fix use-after-free of DRC slot in nfsd4_sequence()
2026-08-19 5:43 [PATCH] nfsd: fix use-after-free of DRC slot in nfsd4_sequence() Tien Phan
@ 2026-08-19 5:52 ` Greg KH
[not found] ` <CALjHmwND53Gdy0xMaPNMtvBP5=9KQ7M9-E2S+W1wweQw30tEmw@mail.gmail.com>
2026-08-19 7:21 ` [PATCH v2] " Tien Phan
2026-08-19 7:47 ` [PATCH v3] " Tien Phan
2 siblings, 1 reply; 8+ messages in thread
From: Greg KH @ 2026-08-19 5:52 UTC (permalink / raw)
To: Tien Phan; +Cc: security, Chuck Lever, NeilBrown, Jeff Layton, stable
On Wed, Aug 19, 2026 at 02:43:26PM +0900, Tien Phan wrote:
> nfsd4_sequence() loads the slot addressed by the client-supplied
> seq->slotid, bounded only against se_fchannel.maxreqs:
>
> if (seq->slotid >= session->se_fchannel.maxreqs)
> goto out_put_session;
> slot = xa_load(&session->se_slots, seq->slotid);
>
> When the client acknowledges a slot-count reduction, the handler frees
> every slot in [se_target_maxslots, maxreqs):
>
> if (session->se_target_maxslots < session->se_fchannel.maxreqs &&
> slot->sl_generation == session->se_slot_gen &&
> seq->maxslots <= session->se_target_maxslots)
> free_session_slots(session, session->se_target_maxslots);
>
> but never checks that the slot it just loaded lies outside that range.
> A malicious NFSv4.1 client can pick seq->slotid >= se_target_maxslots
> (still < maxreqs, so the bound above passes) and, once the DRC slot
> shrinker has lowered se_target_maxslots below maxreqs, issue two SEQUENCE
> calls on that high slot: the first aligns slot->sl_generation with
> se_slot_gen, the second satisfies the reduce-ack condition and calls
> free_session_slots(), which kfree()s the very slot still referenced by
> the handler. The slot is then written (slot->sl_seqid = seq->seqid,
> slot->sl_flags, cstate->slot = slot) and used for DRC caching -- a
> use-after-free write.
>
> Reject a slotid that falls within the range about to be freed before
> freeing it, so the handler never operates on a freed slot.
>
> Found with an in-tree KASAN harness driving the real free_session_slots()
> on a live slot xarray (KASAN: slab-use-after-free, write of size 4).
>
> Fixes: fc8738c68d0b ("nfsd: add support for freeing unused session-DRC slots")
> Cc: stable@vger.kernel.org # 6.14+
> Signed-off-by: Tien Phan <tienpp@gmail.com>
Did you forget an Assisted-by: tag here?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH v2] nfsd: fix use-after-free of DRC slot in nfsd4_sequence()
2026-08-19 5:43 [PATCH] nfsd: fix use-after-free of DRC slot in nfsd4_sequence() Tien Phan
2026-08-19 5:52 ` Greg KH
@ 2026-08-19 7:21 ` Tien Phan
2026-08-19 7:47 ` [PATCH v3] " Tien Phan
2 siblings, 0 replies; 8+ messages in thread
From: Tien Phan @ 2026-08-19 7:21 UTC (permalink / raw)
To: security; +Cc: Greg KH, Chuck Lever, NeilBrown, Jeff Layton, stable, Tien Phan
nfsd4_sequence() loads the slot addressed by the client-supplied
seq->slotid, bounded only against se_fchannel.maxreqs:
if (seq->slotid >= session->se_fchannel.maxreqs)
goto out_put_session;
slot = xa_load(&session->se_slots, seq->slotid);
When the client acknowledges a slot-count reduction, the handler frees
every slot in [se_target_maxslots, maxreqs):
if (session->se_target_maxslots < session->se_fchannel.maxreqs &&
slot->sl_generation == session->se_slot_gen &&
seq->maxslots <= session->se_target_maxslots)
free_session_slots(session, session->se_target_maxslots);
but never checks that the slot it just loaded lies outside that range.
A malicious NFSv4.1 client can pick seq->slotid >= se_target_maxslots
(still < maxreqs, so the bound above passes) and, once the DRC slot
shrinker has lowered se_target_maxslots below maxreqs, issue two SEQUENCE
calls on that high slot: the first aligns slot->sl_generation with
se_slot_gen, the second satisfies the reduce-ack condition and calls
free_session_slots(), which kfree()s the very slot still referenced by
the handler. The slot is then written (slot->sl_seqid = seq->seqid,
slot->sl_flags, cstate->slot = slot) and used for DRC caching -- a
use-after-free write.
Reject a slotid that falls within the range about to be freed before
freeing it, so the handler never operates on a freed slot.
This bug was found by Sentinel, an automated vulnerability-analysis
agent, and reproduced with an in-tree KASAN harness driving the real
free_session_slots() on a live slot xarray (KASAN: slab-use-after-free,
write of size 4). The analysis and fix were prepared with AI assistance.
Fixes: fc8738c68d0b ("nfsd: add support for freeing unused session-DRC slots")
Cc: stable@vger.kernel.org # 6.14+
Reported-by: Sentinel (automated vulnerability-analysis agent)
Assisted-by: Claude (Anthropic)
Signed-off-by: Tien Phan <tienpp@gmail.com>
---
v2:
- Add Reported-by: (Sentinel, the automated analysis agent that found this)
and Assisted-by: (AI assistance) trailers, per Greg KH's request.
- Correct NeilBrown's address to neil@brown.name (v1 bounced at suse.de).
- No code changes since v1.
v1: https://lore.kernel.org/all/20260819054326.1265334-1-tienpp@gmail.com/
fs/nfsd/nfs4state.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index a42f34842d77..f737b841b5a2 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4575,9 +4575,19 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (session->se_target_maxslots < session->se_fchannel.maxreqs &&
slot->sl_generation == session->se_slot_gen &&
- seq->maxslots <= session->se_target_maxslots)
+ seq->maxslots <= session->se_target_maxslots) {
/* Client acknowledged our reduce maxreqs */
+ if (seq->slotid >= session->se_target_maxslots) {
+ /*
+ * ... but is using a slot from the range we are
+ * about to free. Proceeding would leave the code
+ * below dereferencing a freed slot (use-after-free).
+ */
+ status = nfserr_badslot;
+ goto out_put_session;
+ }
free_session_slots(session, session->se_target_maxslots);
+ }
buflen = (seq->cachethis) ?
session->se_fchannel.maxresp_cached :
--
2.34.1
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v3] nfsd: fix use-after-free of DRC slot in nfsd4_sequence()
2026-08-19 5:43 [PATCH] nfsd: fix use-after-free of DRC slot in nfsd4_sequence() Tien Phan
2026-08-19 5:52 ` Greg KH
2026-08-19 7:21 ` [PATCH v2] " Tien Phan
@ 2026-08-19 7:47 ` Tien Phan
2026-08-19 11:23 ` Jeff Layton
2 siblings, 1 reply; 8+ messages in thread
From: Tien Phan @ 2026-08-19 7:47 UTC (permalink / raw)
To: security; +Cc: Greg KH, Chuck Lever, NeilBrown, Jeff Layton, stable, Tien Phan
nfsd4_sequence() loads the slot for the client-supplied seq->slotid
(bounded only by se_fchannel.maxreqs), then on the reduce-ack path frees
every slot in [se_target_maxslots, maxreqs) with free_session_slots()
without checking that the loaded slot is outside that range.
Once the DRC shrinker has lowered se_target_maxslots below maxreqs, a
client using seq->slotid >= se_target_maxslots has its own slot freed and
then dereferenced and written (slot->sl_seqid, cstate->slot): a
use-after-free.
Reject such a slotid before freeing the slots.
Fixes: fc8738c68d0b ("nfsd: add support for freeing unused session-DRC slots")
Cc: stable@vger.kernel.org # 6.14+
Reported-by: Sentinel (automated analysis agent)
Assisted-by: Claude (Anthropic)
Signed-off-by: Tien Phan <tienpp@gmail.com>
---
v3: trim commit message; drop the added in-code comment (Greg KH).
v2: add Reported-by:/Assisted-by: trailers (Greg KH); fix NeilBrown address.
v1: https://lore.kernel.org/all/20260819054326.1265334-1-tienpp@gmail.com/
fs/nfsd/nfs4state.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index a42f34842d77..def1ea534e18 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4575,9 +4575,14 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (session->se_target_maxslots < session->se_fchannel.maxreqs &&
slot->sl_generation == session->se_slot_gen &&
- seq->maxslots <= session->se_target_maxslots)
+ seq->maxslots <= session->se_target_maxslots) {
/* Client acknowledged our reduce maxreqs */
+ if (seq->slotid >= session->se_target_maxslots) {
+ status = nfserr_badslot;
+ goto out_put_session;
+ }
free_session_slots(session, session->se_target_maxslots);
+ }
buflen = (seq->cachethis) ?
session->se_fchannel.maxresp_cached :
--
2.34.1
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH v3] nfsd: fix use-after-free of DRC slot in nfsd4_sequence()
2026-08-19 7:47 ` [PATCH v3] " Tien Phan
@ 2026-08-19 11:23 ` Jeff Layton
2026-08-23 9:26 ` Ingo Molnar
0 siblings, 1 reply; 8+ messages in thread
From: Jeff Layton @ 2026-08-19 11:23 UTC (permalink / raw)
To: Tien Phan, security; +Cc: Greg KH, Chuck Lever, NeilBrown, stable
On Wed, 2026-08-19 at 16:47 +0900, Tien Phan wrote:
> nfsd4_sequence() loads the slot for the client-supplied seq->slotid
> (bounded only by se_fchannel.maxreqs), then on the reduce-ack path frees
> every slot in [se_target_maxslots, maxreqs) with free_session_slots()
> without checking that the loaded slot is outside that range.
>
> Once the DRC shrinker has lowered se_target_maxslots below maxreqs, a
> client using seq->slotid >= se_target_maxslots has its own slot freed and
> then dereferenced and written (slot->sl_seqid, cstate->slot): a
> use-after-free.
>
> Reject such a slotid before freeing the slots.
>
> Fixes: fc8738c68d0b ("nfsd: add support for freeing unused session-DRC slots")
> Cc: stable@vger.kernel.org # 6.14+
> Reported-by: Sentinel (automated analysis agent)
> Assisted-by: Claude (Anthropic)
> Signed-off-by: Tien Phan <tienpp@gmail.com>
> ---
> v3: trim commit message; drop the added in-code comment (Greg KH).
> v2: add Reported-by:/Assisted-by: trailers (Greg KH); fix NeilBrown address.
> v1: https://lore.kernel.org/all/20260819054326.1265334-1-tienpp@gmail.com/
> fs/nfsd/nfs4state.c | 7 ++++++-
> 1 file changed, 6 insertions(+), 1 deletion(-)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index a42f34842d77..def1ea534e18 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -4575,9 +4575,14 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
>
> if (session->se_target_maxslots < session->se_fchannel.maxreqs &&
> slot->sl_generation == session->se_slot_gen &&
> - seq->maxslots <= session->se_target_maxslots)
> + seq->maxslots <= session->se_target_maxslots) {
> /* Client acknowledged our reduce maxreqs */
> + if (seq->slotid >= session->se_target_maxslots) {
> + status = nfserr_badslot;
> + goto out_put_session;
> + }
> free_session_slots(session, session->se_target_maxslots);
> + }
>
> buflen = (seq->cachethis) ?
> session->se_fchannel.maxresp_cached :
I think this problem is already fixed (and more comprehensively) by
this patch in Chuck's nfsd-testing branch:
f5d22e372f4a nfsd: don't free session slots that are still in use
If you're scanning for bugs in sunrpc/nfsd/nlm, then please do check
whether they've already been fixed in the development trees before you
waste your time.
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH v3] nfsd: fix use-after-free of DRC slot in nfsd4_sequence()
2026-08-19 11:23 ` Jeff Layton
@ 2026-08-23 9:26 ` Ingo Molnar
0 siblings, 0 replies; 8+ messages in thread
From: Ingo Molnar @ 2026-08-23 9:26 UTC (permalink / raw)
To: Jeff Layton; +Cc: Tien Phan, security, Greg KH, Chuck Lever, NeilBrown, stable
* Jeff Layton <jlayton@kernel.org> wrote:
> I think this problem is already fixed (and more comprehensively) by
> this patch in Chuck's nfsd-testing branch:
>
> f5d22e372f4a nfsd: don't free session slots that are still in use
Commit/patch attached below for convenience - and it's now upstream
as well already.
Thanks,
Ingo
=========================>
# From f5d22e372f4ac3eb287037a488c29691d52a6330 Mon Sep 17 00:00:00 2001
From: Jeff Layton <jlayton@kernel.org>
Date: Tue, 26 May 2026 12:24:48 -0400
Subject: [PATCH] nfsd: don't free session slots that are still in use
nfsd4_sequence() can free the very slot it is currently processing.
When the session shrinker has reduced se_target_maxslots below
se_fchannel.maxreqs, the shrink path checks three conditions before
calling free_session_slots():
1. se_target_maxslots < maxreqs (shrink was advertised)
2. slot->sl_generation == se_slot_gen (slot is up-to-date)
3. seq->maxslots <= se_target_maxslots (client acknowledges)
However, seq->slotid is never checked against se_target_maxslots.
A client using a slot in the range [se_target_maxslots, maxreqs) can
satisfy all three conditions: its slot has the current generation
(set by a prior SEQUENCE), and it sends sa_highest_slotid <=
se_target_maxslots to acknowledge the reduction.
free_session_slots() then kfrees every slot at index >=
se_target_maxslots, including the caller's own slot. The function
continues to write sl_seqid, sl_flags, sl_generation, and stores the
dangling pointer in cstate->slot. Later, nfsd4_store_cache_entry()
copies up to maxresp_cached bytes of the compound reply into the freed
sl_data[] array, corrupting whatever slab object now occupies that
address.
Additionally, a concurrent thread processing SEQUENCE on a different
high-numbered slot can have its slot freed out from under it.
NFSD4_SLOT_INUSE is set under nn->client_lock before the lock is
released, so any concurrent thread past SEQUENCE will have its slot
marked. However, free_session_slots() does not check NFSD4_SLOT_INUSE
before freeing.
Fix both problems by:
1. Checking that the current request's slotid is below the shrink
boundary.
2. Scanning slots in the to-be-freed range for NFSD4_SLOT_INUSE and
deferring the shrink if any are active.
Fixes: fc8738c68d0b ("nfsd: add support for freeing unused session-DRC slots")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20260526-nfsd4_sequence_shrink_uaf_on_loaded_slot-v2-1-74a89db0639e@kernel.org
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfsd/nfs4state.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 8f90fba53357..0d6a7440a4e2 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4496,6 +4496,19 @@ static void nfsd4_construct_sequence_response(struct nfsd4_session *session,
seq->status_flags |= SEQ4_STATUS_ADMIN_STATE_REVOKED;
}
+static bool nfsd4_slots_inuse(struct nfsd4_session *ses, int from)
+{
+ int i;
+
+ for (i = from; i < ses->se_fchannel.maxreqs; i++) {
+ struct nfsd4_slot *slot = xa_load(&ses->se_slots, i);
+
+ if (slot->sl_flags & NFSD4_SLOT_INUSE)
+ return true;
+ }
+ return false;
+}
+
__be32
nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
union nfsd4_op_u *u)
@@ -4575,7 +4588,9 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (session->se_target_maxslots < session->se_fchannel.maxreqs &&
slot->sl_generation == session->se_slot_gen &&
- seq->maxslots <= session->se_target_maxslots)
+ seq->maxslots <= session->se_target_maxslots &&
+ seq->slotid < session->se_target_maxslots &&
+ !nfsd4_slots_inuse(session, session->se_target_maxslots))
/* Client acknowledged our reduce maxreqs */
free_session_slots(session, session->se_target_maxslots);
^ permalink raw reply related [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-08-23 9:26 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-19 5:43 [PATCH] nfsd: fix use-after-free of DRC slot in nfsd4_sequence() Tien Phan
2026-08-19 5:52 ` Greg KH
[not found] ` <CALjHmwND53Gdy0xMaPNMtvBP5=9KQ7M9-E2S+W1wweQw30tEmw@mail.gmail.com>
2026-08-19 7:29 ` Greg KH
2026-08-19 11:00 ` Jeff Layton
2026-08-19 7:21 ` [PATCH v2] " Tien Phan
2026-08-19 7:47 ` [PATCH v3] " Tien Phan
2026-08-19 11:23 ` Jeff Layton
2026-08-23 9:26 ` Ingo Molnar
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox