svcrdma: Reject Read lists that exceed the page budget

Individual Read segment lengths are validated at decode time, but
nothing prevents a requester from sending multiple segments whose
cumulative length exceeds the rq_pages array budget. When one
segment fills the page array exactly, the runtime guard in
svc_rdma_build_read_segment() is bypassed because len reaches zero.
A subsequent segment then accesses the NULL sentinel slot at
rq_pages[rq_maxpages], resulting in a NULL pointer dereference during
DMA mapping.

Accumulate pages across all Read segments and reject the message at
decode time when the total would overflow the page budget.

Fixes: 026d958b38 ("svcrdma: Add recvfrom helpers to svc_rdma_rw.c")
Cc: stable@vger.kernel.org
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
Chuck Lever
2026-05-26 13:34:54 -04:00
committed by Chuck Lever
parent b036727d33
commit 0ca487abb3

View File

@@ -440,11 +440,14 @@ static void svc_rdma_build_arg_xdr(struct svc_rqst *rqstp,
* to the first byte past the Read list. rc_read_pcl and
* rc_call_pcl cl_count fields are set to the number of
* Read segments in the list.
* %false: Read list is corrupt. @rctxt's xdr_stream is left in an
* unknown state.
* %false: Read list is corrupt or exceeds the page budget. @rctxt's
* xdr_stream is left in an unknown state.
*/
static bool xdr_count_read_segments(struct svc_rdma_recv_ctxt *rctxt, __be32 *p)
{
unsigned int maxlen = rctxt->rc_maxpages << PAGE_SHIFT;
unsigned int total_len = 0;
rctxt->rc_call_pcl.cl_count = 0;
rctxt->rc_read_pcl.cl_count = 0;
while (xdr_item_is_present(p)) {
@@ -458,7 +461,10 @@ static bool xdr_count_read_segments(struct svc_rdma_recv_ctxt *rctxt, __be32 *p)
xdr_decode_read_segment(p, &position, &handle,
&length, &offset);
if (length > rctxt->rc_maxpages << PAGE_SHIFT)
if (length > maxlen)
return false;
total_len += length;
if (PAGE_ALIGN(total_len) > maxlen)
return false;
if (position) {
if (position & 3)