FAILED: patch "[PATCH] svcrdma: Validate Read chunk positions before reconstruction" failed to apply to 6.6-stable tree
Subsystems (4)
[email protected]
maintainerThe patch below does not apply to the 6.6-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 <[email protected]>.
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-6.6.y
git checkout FETCH_HEAD
git cherry-pick -x 3779b7b9e7d1c8ba4738f9d327de3b0288cefe9b
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090309-wooing-reassign-9ecf@gregkh' --subject-prefix 'PATCH 6.6.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 3779b7b9e7d1c8ba4738f9d327de3b0288cefe9b Mon Sep 17 00:00:00 2001
From: Chuck Lever <[email protected]>
Date: Tue, 26 May 2026 09:35:55 -0400
Subject: [PATCH] svcrdma: Validate Read chunk positions before reconstruction
The RPC/RDMA Read chunk position field is supplied by the remote client and stored verbatim in the parsed chunk list. xdr_count_read_segments() checks only 4-byte alignment; it never compares the position against the received inline body length.
In the single-chunk path, svc_rdma_read_complete_one() splits the head and tail kvecs at ch_position. A position past the inline body underflows the tail length, exposing adjacent slab memory to the upper XDR decoder.
In the multi-chunk path, svc_rdma_read_multiple_chunks() computes gap lengths between chunks as unsigned subtractions from ch_position. Overlapping Read chunks cause these subtractions to underflow. A final position past the inline body likewise underflows the trailing gap length. svc_rdma_copy_inline_range() then copies past the receive buffer into request pages that are returned to the client through the Reply channel.
Bound inline-range copies in svc_rdma_copy_inline_range() against the decoded inline RPC body saved in rc_saved_arg. Reject a single Read chunk positioned beyond that body, and reject multi-chunk lists where accumulated read bytes exceed the next chunk's position. Apply the same position and overlap checks in the call-chunk interleaving path.
net/sunrpc/xprtrdma/svc_rdma_rw.c +30 -8
@@ -847,7 +847,7 @@ static int svc_rdma_build_read_chunk(struct svc_rqst *rqstp,* svc_rdma_copy_inline_range - Copy part of the inline content into pages * @rqstp: RPC transaction context * @head: context for ongoing I/O -* @offset: offset into the Receive buffer of region to copy +* @offset: offset into the inline content of region to copy * @remaining: length of region to copy * * Take a page at a time from rqstp->rq_pages and copy the inline
@@ -864,9 +864,13 @@ static int svc_rdma_copy_inline_range(struct svc_rqst *rqstp,unsigned int offset, unsigned int remaining) { - unsigned char *dst, *src = head->rc_recv_buf; + unsigned char *dst, *src = head->rc_saved_arg.head[0].iov_base; +unsigned int inline_len = head->rc_saved_arg.head[0 ].iov_len;unsigned int page_no, numpages; +if (offset > inline_len || remaining > inline_len - offset) +return -EINVAL ; + numpages =PAGE_ALIGN (head->rc_pageoff + remaining) >>PAGE_SHIFT ;for (page_no =0 ; page_no < numpages; page_no++) {unsigned int page_len;
@@ -917,9 +921,10 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp, {const struct svc_rdma_pcl *pcl = &head->rc_read_pcl;struct svc_rdma_chunk *chunk, *next; -unsigned int start, length; +unsigned int inline_len, start, length;int ret; + inline_len = head->rc_saved_arg.head[0 ].iov_len; start =0 ; chunk =pcl_first_chunk (pcl); length = chunk->ch_position;
@@ -937,6 +942,8 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp,break ; start += length; +if (head->rc_readbytes > next->ch_position) +return -EINVAL ; length = next->ch_position - head->rc_readbytes; ret =svc_rdma_copy_inline_range (rqstp, head, start, length);if (ret <0 )
@@ -944,7 +951,9 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp, } start += length; - length = head->rc_byte_len - start; +if (start > inline_len) +return -EINVAL ; + length = inline_len - start;return svc_rdma_copy_inline_range (rqstp, head, start, length); }
@@ -969,8 +978,12 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp,static int svc_rdma_read_data_item (struct svc_rqst *rqstp,struct svc_rdma_recv_ctxt *head) { -return svc_rdma_build_read_chunk (rqstp, head, -pcl_first_chunk (&head->rc_read_pcl)); +struct svc_rdma_chunk *chunk =pcl_first_chunk (&head->rc_read_pcl); + +if (chunk->ch_position > head->rc_saved_arg.head[0 ].iov_len) +return -EINVAL ; + +return svc_rdma_build_read_chunk (rqstp, head, chunk); }/**
@@ -1039,14 +1052,17 @@ static int svc_rdma_read_call_chunk(struct svc_rqst *rqstp,pcl_first_chunk (&head->rc_call_pcl);const struct svc_rdma_pcl *pcl = &head->rc_read_pcl;struct svc_rdma_chunk *chunk, *next; -unsigned int start, length; +unsigned int call_len, start, length;int ret;if (pcl_is_empty (pcl))return svc_rdma_build_read_chunk (rqstp, head, call_chunk); + call_len = call_chunk->ch_length; start =0 ; chunk =pcl_first_chunk (pcl); +if (chunk->ch_position > call_len) +return -EINVAL ; length = chunk->ch_position; ret =svc_rdma_read_chunk_range (rqstp, head, call_chunk, start, length);
@@ -1063,6 +1079,10 @@ static int svc_rdma_read_call_chunk(struct svc_rqst *rqstp,break ; start += length; +if (next->ch_position > call_len) +return -EINVAL ; +if (head->rc_readbytes > next->ch_position) +return -EINVAL ; length = next->ch_position - head->rc_readbytes; ret =svc_rdma_read_chunk_range (rqstp, head, call_chunk, start, length);
@@ -1071,7 +1091,9 @@ static int svc_rdma_read_call_chunk(struct svc_rqst *rqstp, } start += length; - length = call_chunk->ch_length - start; +if (start > call_len) +return -EINVAL ; + length = call_len - start;return svc_rdma_read_chunk_range (rqstp, head, call_chunk, start, length); }
Sasha Levin
maintainerPATCH 1/4 svcrdma: Update synopsis of svc_rdma_read_multiple_chunks()
From: Chuck Lever <[email protected]>
[ Upstream commit 740a3c895d94fc72494f2a4abf1ab31b6be482e8 ]
Since the RDMA Read I/O state is now contained in the recv_ctxt, svc_rdma_read_multiple_chunks() can use that recv_ctxt to derive the read_info rather than the other way around. This removes another usage of the ri_readctxt field, enabling its removal in a subsequent patch.
Stable dependency adaptation for 3779b7b9e7d1 (6.6):
The original synopsis change assumes earlier Read-state and helper API refactors that are absent here. Move the page cursor, byte offset and accumulated byte count into the receive context, and give the existing Read helpers the request/receive-context interfaces used by the target. Keep the allocated read_info and synchronous completion path, using a receive-context pointer that is cleared before read_info is freed.
Save the decoded rq_arg before reconstruction so rc_saved_arg identifies the inline RPC body for the target's bounds checks. Move the existing single- and multi-chunk rq_arg reconstruction into process_read_list(), after successful WR construction, without adding any functions or the upstream asynchronous completion machinery. Use the current page byte offset for inline memcpy, as in the upstream helper.
This supplies the target's unchanged patch context and required state; the position/overlap validation remains in the target commit.
Stable-dep-of: 3779b7b9e7d1 ("svcrdma: Validate Read chunk positions before reconstruction")
--- include/linux/sunrpc/svc_rdma.h | 7 + net/sunrpc/xprtrdma/svc_rdma_rw.c | 242 ++++++++++++++---------------- 2 files changed, 116 insertions(+), 133 deletions(-)
include/linux/sunrpc/svc_rdma.h +7 -0
@@ -140,6 +140,13 @@ struct svc_rdma_recv_ctxt {u32 rc_inv_rkey;__be32 rc_msgtype; +/* Read state; rc_read_info lives until synchronous I/O completes. */ +struct svc_rdma_read_info *rc_read_info; +struct xdr_buf rc_saved_arg; +unsigned int rc_curpage; +unsigned int rc_pageoff; +unsigned int rc_readbytes; +struct svc_rdma_pcl rc_call_pcl;struct svc_rdma_pcl rc_read_pcl;
net/sunrpc/xprtrdma/svc_rdma_rw.c +109 -133
487 lines not on this page · raw message
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/4 svcrdma: Add back svc_rdma_recv_ctxt::rc_pages
From: Chuck Lever <[email protected]>
[ Upstream commit 4d9d69db898d05bd063548eee65d16a020676fec ]
Having an nfsd thread waiting for an RDMA Read completion is problematic if the Read responder (the client) stops responding. We need to go back to handling RDMA Reads by allowing the nfsd thread to return to the svc scheduler, then waking a second thread finish the RPC message once the Read completion fires.
To start with, restore the rc_pages field so that RDMA Read pages can be managed across calls to svc_rdma_recvfrom().
Stable-dep-of: 3779b7b9e7d1 ("svcrdma: Validate Read chunk positions before reconstruction")
--- include/linux/sunrpc/svc_rdma.h | 4 +++- net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 5 +++++ net/sunrpc/xprtrdma/svc_rdma_rw.c | 4 +++- 3 files changed, 11 insertions(+), 2 deletions(-)
include/linux/sunrpc/svc_rdma.h +3 -1
@@ -136,7 +136,6 @@ struct svc_rdma_recv_ctxt {void *rc_recv_buf;struct xdr_stream rc_stream;u32 rc_byte_len; -unsigned int rc_page_count;u32 rc_inv_rkey;__be32 rc_msgtype;
@@ -153,6 +152,9 @@ struct svc_rdma_recv_ctxt {struct svc_rdma_chunk *rc_cur_result_payload;struct svc_rdma_pcl rc_write_pcl;struct svc_rdma_pcl rc_reply_pcl; + +unsigned int rc_page_count; +struct page *rc_pages[RPCSVC_MAXPAGES ]; };struct svc_rdma_send_ctxt {
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +5 -0
@@ -227,6 +227,11 @@ struct svc_rdma_recv_ctxt *svc_rdma_recv_ctxt_get(struct svcxprt_rdma *rdma)void svc_rdma_recv_ctxt_put (struct svcxprt_rdma *rdma,struct svc_rdma_recv_ctxt *ctxt) { +/* @rc_page_count is normally zero here, but error flows +* can leave pages in @rc_pages. +*/ +release_pages (ctxt->rc_pages, ctxt->rc_page_count); +pcl_free (&ctxt->rc_call_pcl);pcl_free (&ctxt->rc_read_pcl);pcl_free (&ctxt->rc_write_pcl);
net/sunrpc/xprtrdma/svc_rdma_rw.c +3 -1
@@ -1138,7 +1138,9 @@ int svc_rdma_process_read_list(struct svcxprt_rdma *rdma, rqstp->rq_respages = &rqstp->rq_pages[head->rc_page_count]; rqstp->rq_next_page = rqstp->rq_respages +1 ; -/* Ensure svc_rdma_recv_ctxt_put() does not try to release pages */ +/* Ensure svc_rdma_recv_ctxt_put() does not release pages +* left in @rc_pages while I/O proceeds. +*/ head->rc_page_count =0 ; out_err:
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/4 svcrdma: Add back svcxprt_rdma::sc_read_complete_q
In reply to Sasha LevinFrom: Chuck Lever <[email protected]>
[ Upstream commit a937693a82fd2211c5e52b638959d1486a77d16a ]
Having an nfsd thread waiting for an RDMA Read completion is problematic if the Read responder (ie, the client) stops responding. We need to go back to handling RDMA Reads by allowing the nfsd thread to return to the svc scheduler, then waking a second thread finish the RPC message once the Read completion fires.
As a next step, add a list_head upon which completed Reads are queued. A subsequent patch will make use of this queue.
Stable-dep-of: 3779b7b9e7d1 ("svcrdma: Validate Read chunk positions before reconstruction")
--- include/linux/sunrpc/svc_rdma.h | 1 + net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 37 +++++++++++++++++++++++- net/sunrpc/xprtrdma/svc_rdma_transport.c | 1 + 3 files changed, 38 insertions(+), 1 deletion(-)
include/linux/sunrpc/svc_rdma.h +1 -0
@@ -97,6 +97,7 @@ struct svcxprt_rdma {u32 sc_pending_recvs;u32 sc_recv_batch;struct list_head sc_rq_dto_q; +struct list_head sc_read_complete_q;spinlock_t sc_rq_dto_lock;struct ib_qp *sc_qp;struct ib_cq *sc_rq_cq;
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +36 -1
@@ -378,6 +378,10 @@ void svc_rdma_flush_recv_queues(struct svcxprt_rdma *rdma) {struct svc_rdma_recv_ctxt *ctxt; +while ((ctxt =svc_rdma_next_recv_ctxt (&rdma->sc_read_complete_q))) { +list_del (&ctxt->rc_list); +svc_rdma_recv_ctxt_put (rdma, ctxt); + }while ((ctxt =svc_rdma_next_recv_ctxt (&rdma->sc_rq_dto_q))) {list_del (&ctxt->rc_list);svc_rdma_recv_ctxt_put (rdma, ctxt);
@@ -765,6 +769,30 @@ static bool svc_rdma_is_reverse_direction_reply(struct svc_xprt *xprt,return true; } +static noinline void svc_rdma_read_complete(struct svc_rqst *rqstp, +struct svc_rdma_recv_ctxt *ctxt) +{ +int i; + +/* Transfer the Read chunk pages into @rqstp.rq_pages, replacing +* the rq_pages that were already allocated for this rqstp. +*/ +release_pages (rqstp->rq_respages, ctxt->rc_page_count); +for (i =0 ; i < ctxt->rc_page_count; i++) + rqstp->rq_pages[i] = ctxt->rc_pages[i]; + +/* Update @rqstp's result send buffer to start after the +* last page in the RDMA Read payload. +*/ + rqstp->rq_respages = &rqstp->rq_pages[ctxt->rc_page_count]; + rqstp->rq_next_page = rqstp->rq_respages +1 ; + +/* Prevent svc_rdma_recv_ctxt_put() from releasing the +* pages in ctxt::rc_pages a second time. +*/ + ctxt->rc_page_count =0 ; +} +/** * svc_rdma_recvfrom - Receive an RPC call * @rqstp: request structure into which to receive an RPC Call
@@ -809,8 +837,14 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) rqstp->rq_xprt_ctxt =NULL ; - ctxt =NULL ;spin_lock (&rdma_xprt->sc_rq_dto_lock); + ctxt =svc_rdma_next_recv_ctxt (&rdma_xprt->sc_read_complete_q); +if (ctxt) { +list_del (&ctxt->rc_list); +spin_unlock_bh (&rdma_xprt->sc_rq_dto_lock); +svc_rdma_read_complete (rqstp, ctxt); + goto complete; + } ctxt =svc_rdma_next_recv_ctxt (&rdma_xprt->sc_rq_dto_q);if (ctxt)list_del (&ctxt->rc_list);
@@ -848,6 +882,7 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) goto out_readfail; } +complete: rqstp->rq_xprt_ctxt = ctxt; rqstp->rq_prot =IPPROTO_MAX ;svc_xprt_copy_addrs (rqstp, xprt);
net/sunrpc/xprtrdma/svc_rdma_transport.c +1 -0
@@ -134,6 +134,7 @@ static struct svcxprt_rdma *svc_rdma_create_xprt(struct svc_serv *serv,svc_xprt_init (net, &svc_rdma_class, &cma_xprt->sc_xprt, serv);INIT_LIST_HEAD (&cma_xprt->sc_accept_q);INIT_LIST_HEAD (&cma_xprt->sc_rq_dto_q); +INIT_LIST_HEAD (&cma_xprt->sc_read_complete_q);init_llist_head (&cma_xprt->sc_send_ctxts);init_llist_head (&cma_xprt->sc_recv_ctxts);init_llist_head (&cma_xprt->sc_rw_ctxts);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/4 svcrdma: Validate Read chunk positions before reconstruction
In reply to Sasha LevinFrom: Chuck Lever <[email protected]>
[ Upstream commit 3779b7b9e7d1c8ba4738f9d327de3b0288cefe9b ]
The RPC/RDMA Read chunk position field is supplied by the remote client and stored verbatim in the parsed chunk list. xdr_count_read_segments() checks only 4-byte alignment; it never compares the position against the received inline body length.
In the single-chunk path, svc_rdma_read_complete_one() splits the head and tail kvecs at ch_position. A position past the inline body underflows the tail length, exposing adjacent slab memory to the upper XDR decoder.
In the multi-chunk path, svc_rdma_read_multiple_chunks() computes gap lengths between chunks as unsigned subtractions from ch_position. Overlapping Read chunks cause these subtractions to underflow. A final position past the inline body likewise underflows the trailing gap length. svc_rdma_copy_inline_range() then copies past the receive buffer into request pages that are returned to the client through the Reply channel.
Bound inline-range copies in svc_rdma_copy_inline_range() against the decoded inline RPC body saved in rc_saved_arg. Reject a single Read chunk positioned beyond that body, and reject multi-chunk lists where accumulated read bytes exceed the next chunk's position. Apply the same position and overlap checks in the call-chunk interleaving path.
--- net/sunrpc/xprtrdma/svc_rdma_rw.c | 38 ++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 8 deletions(-)
net/sunrpc/xprtrdma/svc_rdma_rw.c +30 -8
@@ -792,7 +792,7 @@ static int svc_rdma_build_read_chunk(struct svc_rqst *rqstp,* svc_rdma_copy_inline_range - Copy part of the inline content into pages * @rqstp: RPC transaction context * @head: context for ongoing I/O -* @offset: offset into the Receive buffer of region to copy +* @offset: offset into the inline content of region to copy * @remaining: length of region to copy * * Take a page at a time from rqstp->rq_pages and copy the inline
@@ -809,9 +809,13 @@ static int svc_rdma_copy_inline_range(struct svc_rqst *rqstp,unsigned int offset, unsigned int remaining) { - unsigned char *dst, *src = head->rc_recv_buf; + unsigned char *dst, *src = head->rc_saved_arg.head[0].iov_base; +unsigned int inline_len = head->rc_saved_arg.head[0 ].iov_len;unsigned int page_no, numpages; +if (offset > inline_len || remaining > inline_len - offset) +return -EINVAL ; + numpages =PAGE_ALIGN (head->rc_pageoff + remaining) >>PAGE_SHIFT ;for (page_no =0 ; page_no < numpages; page_no++) {unsigned int page_len;
@@ -862,9 +866,10 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp, {const struct svc_rdma_pcl *pcl = &head->rc_read_pcl;struct svc_rdma_chunk *chunk, *next; -unsigned int start, length; +unsigned int inline_len, start, length;int ret; + inline_len = head->rc_saved_arg.head[0 ].iov_len; start =0 ; chunk =pcl_first_chunk (pcl); length = chunk->ch_position;
@@ -882,6 +887,8 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp,break ; start += length; +if (head->rc_readbytes > next->ch_position) +return -EINVAL ; length = next->ch_position - head->rc_readbytes; ret =svc_rdma_copy_inline_range (rqstp, head, start, length);if (ret <0 )
@@ -889,7 +896,9 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp, } start += length; - length = head->rc_byte_len - start; +if (start > inline_len) +return -EINVAL ; + length = inline_len - start;return svc_rdma_copy_inline_range (rqstp, head, start, length); }
@@ -914,8 +923,12 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp,static int svc_rdma_read_data_item (struct svc_rqst *rqstp,struct svc_rdma_recv_ctxt *head) { -return svc_rdma_build_read_chunk (rqstp, head, -pcl_first_chunk (&head->rc_read_pcl)); +struct svc_rdma_chunk *chunk =pcl_first_chunk (&head->rc_read_pcl); + +if (chunk->ch_position > head->rc_saved_arg.head[0 ].iov_len) +return -EINVAL ; + +return svc_rdma_build_read_chunk (rqstp, head, chunk); }/**
@@ -984,14 +997,17 @@ static int svc_rdma_read_call_chunk(struct svc_rqst *rqstp,pcl_first_chunk (&head->rc_call_pcl);const struct svc_rdma_pcl *pcl = &head->rc_read_pcl;struct svc_rdma_chunk *chunk, *next; -unsigned int start, length; +unsigned int call_len, start, length;int ret;if (pcl_is_empty (pcl))return svc_rdma_build_read_chunk (rqstp, head, call_chunk); + call_len = call_chunk->ch_length; start =0 ; chunk =pcl_first_chunk (pcl); +if (chunk->ch_position > call_len) +return -EINVAL ; length = chunk->ch_position; ret =svc_rdma_read_chunk_range (rqstp, head, call_chunk, start, length);
@@ -1008,6 +1024,10 @@ static int svc_rdma_read_call_chunk(struct svc_rqst *rqstp,break ; start += length; +if (next->ch_position > call_len) +return -EINVAL ; +if (head->rc_readbytes > next->ch_position) +return -EINVAL ; length = next->ch_position - head->rc_readbytes; ret =svc_rdma_read_chunk_range (rqstp, head, call_chunk, start, length);
@@ -1016,7 +1036,9 @@ static int svc_rdma_read_call_chunk(struct svc_rqst *rqstp, } start += length; - length = call_chunk->ch_length - start; +if (start > call_len) +return -EINVAL ; + length = call_len - start;return svc_rdma_read_chunk_range (rqstp, head, call_chunk, start, length); }
Signature · 1 line
2.53.0