mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-15 21:21:49 -04:00
smb: client: avoid integer overflow in SMB2 READ length check
SMB2 READ response validation in cifs_readv_receive() and handle_read_data() checks data_offset + data_len against the received buffer length. Both values are attacker-controlled fields from the server response and are stored as unsigned int, so the addition can wrap before the bounds check: fs/smb/client/transport.c:1259 if (!use_rdma_mr && (data_offset + data_len > buflen)) fs/smb/client/smb2ops.c:4839 else if (buf_len >= data_offset + data_len) A malicious SMB server can use this to bypass validation. In the non-encrypted receive path the client attempts an oversized socket read and stalls for the SMB response timeout (180 seconds) before reconnecting. In the SMB3 encrypted path, runtime testing shows the malformed length can reach copy_to_iter() in handle_read_data() with attacker-controlled size, where usercopy hardening stops the oversized copy before bytes reach userspace. Guard both call sites with check_add_overflow(), which is already used elsewhere in this subsystem (smb2pdu.c). On overflow, treat the response as malformed and reject with -EIO. Signed-off-by: Jeremy Erazo <mendozayt13@gmail.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
committed by
Steve French
parent
ab26dfeba2
commit
81a874233c
@@ -4721,6 +4721,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
|
||||
{
|
||||
unsigned int data_offset;
|
||||
unsigned int data_len;
|
||||
unsigned int end_off;
|
||||
unsigned int cur_off;
|
||||
unsigned int cur_page_idx;
|
||||
unsigned int pad_len;
|
||||
@@ -4836,7 +4837,8 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
|
||||
}
|
||||
rdata->got_bytes = buffer_len;
|
||||
|
||||
} else if (buf_len >= data_offset + data_len) {
|
||||
} else if (!check_add_overflow(data_offset, data_len, &end_off) &&
|
||||
buf_len >= end_off) {
|
||||
/* read response payload is in buf */
|
||||
WARN_ONCE(buffer, "read data can be either in buf or in buffer");
|
||||
copied = copy_to_iter(buf + data_offset, data_len, &rdata->subreq.io_iter);
|
||||
|
||||
@@ -1158,7 +1158,7 @@ int
|
||||
cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||
{
|
||||
int length, len;
|
||||
unsigned int data_offset, data_len;
|
||||
unsigned int data_offset, data_len, end_off;
|
||||
struct cifs_io_subrequest *rdata = mid->callback_data;
|
||||
char *buf = server->smallbuf;
|
||||
unsigned int buflen = server->pdu_size;
|
||||
@@ -1256,11 +1256,14 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||
use_rdma_mr = rdata->mr;
|
||||
#endif
|
||||
data_len = server->ops->read_data_length(buf, use_rdma_mr);
|
||||
if (!use_rdma_mr && (data_offset + data_len > buflen)) {
|
||||
/* data_len is corrupt -- discard frame */
|
||||
rdata->result = smb_EIO2(smb_eio_trace_read_rsp_malformed,
|
||||
data_offset + data_len, buflen);
|
||||
return cifs_readv_discard(server, mid);
|
||||
if (!use_rdma_mr) {
|
||||
if (check_add_overflow(data_offset, data_len, &end_off) ||
|
||||
end_off > buflen) {
|
||||
/* data_len is corrupt -- discard frame */
|
||||
rdata->result = smb_EIO2(smb_eio_trace_read_rsp_malformed,
|
||||
end_off, buflen);
|
||||
return cifs_readv_discard(server, mid);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_SMB_DIRECT
|
||||
|
||||
Reference in New Issue
Block a user