mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
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.
Fixes: d96962e6d0 ("svcrdma: Use the new parsed chunk list when pulling Read chunks")
Cc: stable@vger.kernel.org
Acked-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20260526-rpc-kernel-bugs-v1-1-e251306ccca9@oracle.com
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
parent
650d370cfb
commit
3779b7b9e7
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user