* [PATCH v1] SUNRPC: Copy the deferred RPC Call from the head buffer
@ 2026-09-02 13:31 Chuck Lever
0 siblings, 0 replies; only message in thread
From: Chuck Lever @ 2026-09-02 13:31 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
Cc: linux-nfs, Kimi Security Team, Yilin Zhang
svc_defer() snapshots the RPC Call for replay once a pending cache
upcall resolves. It copies the Call starting (rq_arg.len -
head[0].iov_len) bytes before head[0].iov_base, a back-up that once
covered a transport header. No transport has had one since commit
983084b2672c ("SUNRPC: Remove svc_rqst::rq_xprt_hlen"), so the two
lengths are meant to be equal.
An RPCSEC_GSS Call breaks that assumption. Unwrapping the integrity
service truncates rq_arg.len by the length of the trailing checksum,
and unwrapping the privacy service leaves rq_arg.len short by an
amount the client sets with trailing bytes after the wrap token.
Neither adjusts head[0].iov_len. Both lengths are size_t, so the
subtraction underflows and the copy starts past head[0].iov_base.
Every deferred integrity Call replays a shifted snapshot.
A deferred replay skips GSS verification, so a shifted snapshot runs
as an authenticated Call. With the privacy service the offset is
client-chosen. A forged Call placed in that window, naming another
user's GSS context handle, runs under that identity.
Copy the Call from head[0].iov_base. Refuse to defer a Call whose
length runs past the head buffer, so the copy cannot read out of
bounds.
Fixes: 260c1d1298f6 ("svc: Add transport hdr size for defer/revisit")
Reported-by: Kimi Security Team <bug-report@moonshot.ai>
Reported-by: Yilin Zhang <yilinzhang@moonshot.ai>
Signed-off-by: Chuck Lever <cel@kernel.org>
---
net/sunrpc/svc_xprt.c | 22 +++++++---------------
1 file changed, 7 insertions(+), 15 deletions(-)
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index 77e28dcc4d2a..7f1c6bdd8425 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -1278,13 +1278,8 @@ static void svc_revisit(struct cache_deferred_req *dreq, int too_many)
}
/*
- * Save the request off for later processing. The request buffer looks
- * like this:
- *
- * <xprt-header><rpc-header><rpc-pagelist><rpc-tail>
- *
- * This code can only handle requests that consist of an xprt-header
- * and rpc-header.
+ * Save the request off for later processing. Only a Call that fits
+ * entirely in rq_arg.head[0] can be deferred.
*/
static struct cache_deferred_req *svc_defer(struct cache_req *req)
{
@@ -1297,8 +1292,11 @@ static struct cache_deferred_req *svc_defer(struct cache_req *req)
dr = rqstp->rq_deferred;
rqstp->rq_deferred = NULL;
} else {
- size_t skip;
size_t size;
+
+ if (rqstp->rq_arg.len > rqstp->rq_arg.head[0].iov_len)
+ return NULL;
+
/* FIXME maybe discard if size too large */
size = sizeof(struct svc_deferred_req) + rqstp->rq_arg.len;
dr = kmalloc(size, GFP_KERNEL);
@@ -1312,9 +1310,7 @@ static struct cache_deferred_req *svc_defer(struct cache_req *req)
dr->daddr = rqstp->rq_daddr;
dr->argslen = rqstp->rq_arg.len >> 2;
- /* back up head to the start of the buffer and copy */
- skip = rqstp->rq_arg.len - rqstp->rq_arg.head[0].iov_len;
- memcpy(dr->args, rqstp->rq_arg.head[0].iov_base - skip,
+ memcpy(dr->args, rqstp->rq_arg.head[0].iov_base,
dr->argslen << 2);
}
dr->xprt_ctxt = rqstp->rq_xprt_ctxt;
@@ -1337,17 +1333,13 @@ static noinline int svc_deferred_recv(struct svc_rqst *rqstp)
trace_svc_defer_recv(dr);
- /* setup iov_base past transport header */
rqstp->rq_arg.head[0].iov_base = dr->args;
- /* The iov_len does not include the transport header bytes */
rqstp->rq_arg.head[0].iov_len = dr->argslen << 2;
rqstp->rq_arg.page_len = 0;
- /* The rq_arg.len includes the transport header bytes */
rqstp->rq_arg.len = dr->argslen << 2;
rqstp->rq_prot = dr->prot;
memcpy(&rqstp->rq_addr, &dr->addr, dr->addrlen);
rqstp->rq_addrlen = dr->addrlen;
- /* Save off transport header len in case we get deferred again */
rqstp->rq_daddr = dr->daddr;
rqstp->rq_xprt_ctxt = dr->xprt_ctxt;
--
2.55.0
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-09-02 13:31 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-02 13:31 [PATCH v1] SUNRPC: Copy the deferred RPC Call from the head buffer Chuck Lever
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.