[PATCH] can: kvaser_pciefd: validate DMA packet extents
From: Pengpeng Hou
Date: Sun Aug 30 2026 - 08:43:39 EST
The RX parser reads a packet header and payload before checking the packet
size. It also compares its word index against the DMA buffer size in bytes,
allowing the loop index to pass the end of the 4 KiB buffer.
Bound the word index, require the fixed packet header, validate the
declared packet span, and prove a CAN payload fits before passing it to the
packet handler.
Fixes: 26ad340e582d ("can: kvaser_pciefd: Add driver for Kvaser PCIEcan devices")
Signed-off-by: Pengpeng Hou <pengpeng@xxxxxxxxxxx>
---
drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c b/drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c
index d8c9bfb202304..354a9e29a056e 100644
--- a/drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c
+++ b/drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c
@@ -1552,6 +1552,7 @@ static int kvaser_pciefd_read_packet(struct kvaser_pciefd *pcie, int *start_pos,
int dma_buf)
{
__le32 *buffer = pcie->dma_data[dma_buf];
+ const int buffer_words = KVASER_PCIEFD_DMA_SIZE / sizeof(*buffer);
__le64 timestamp;
struct kvaser_pciefd_rx_packet packet;
struct kvaser_pciefd_rx_packet *p = &packet;
@@ -1560,11 +1561,16 @@ static int kvaser_pciefd_read_packet(struct kvaser_pciefd *pcie, int *start_pos,
int size;
int ret = 0;
+ if (*start_pos < 0 || *start_pos >= buffer_words)
+ return -EIO;
+
size = le32_to_cpu(buffer[pos++]);
if (!size) {
*start_pos = 0;
return 0;
}
+ if (size < 5 || size > buffer_words - *start_pos)
+ return -EIO;
p->header[0] = le32_to_cpu(buffer[pos++]);
p->header[1] = le32_to_cpu(buffer[pos++]);
@@ -1577,13 +1583,22 @@ static int kvaser_pciefd_read_packet(struct kvaser_pciefd *pcie, int *start_pos,
type = FIELD_GET(KVASER_PCIEFD_PACKET_TYPE_MASK, p->header[1]);
switch (type) {
case KVASER_PCIEFD_PACK_TYPE_DATA:
+ if (!(p->header[0] & KVASER_PCIEFD_RPACKET_RTR)) {
+ u8 data_len, data_words;
+
+ data_len = can_fd_dlc2len(FIELD_GET(KVASER_PCIEFD_RPACKET_DLC_MASK,
+ p->header[1]));
+ data_words = DIV_ROUND_UP(data_len, sizeof(*buffer));
+ if (data_words > *start_pos + size - pos)
+ return -EIO;
+ }
ret = kvaser_pciefd_handle_data_packet(pcie, p, &buffer[pos]);
if (!(p->header[0] & KVASER_PCIEFD_RPACKET_RTR)) {
u8 data_len;
data_len = can_fd_dlc2len(FIELD_GET(KVASER_PCIEFD_RPACKET_DLC_MASK,
p->header[1]));
- pos += DIV_ROUND_UP(data_len, 4);
+ pos += DIV_ROUND_UP(data_len, sizeof(*buffer));
}
break;
@@ -1640,7 +1655,8 @@ static int kvaser_pciefd_read_buffer(struct kvaser_pciefd *pcie, int dma_buf)
do {
res = kvaser_pciefd_read_packet(pcie, &pos, dma_buf);
- } while (!res && pos > 0 && pos < KVASER_PCIEFD_DMA_SIZE);
+ } while (!res && pos > 0 &&
+ pos < KVASER_PCIEFD_DMA_SIZE / sizeof(__le32));
/* Report ACKs in this buffer to BQL en masse for correct periods */
for (i = 0; i < pcie->nr_channels; ++i) {
base-commit: 08dbfad3f5040f5bdb6c529da20d6d4e81fefd72
--
2.50.1