From: CryoSlayer Date: Sun, 25 Oct 2026 12:00:00 +0500 Subject: [PATCH] wifi: rsi: fix missing offset bounds check in rsi_read_pkt() for SDIO path The bounds check on the 'offset' field of an incoming frame descriptor was only active for the USB code path (rcv_pkt_len == 0). In the SDIO path rsi_read_pkt() is called with a non-zero rcv_pkt_len equal to num_blks * 256, so the existing guard: if (!rcv_pkt_len && offset > RSI_MAX_RX_USB_PKT_SIZE - FRAME_DESC_SZ) goto fail; was silently skipped, leaving 'offset' -- a u16 read verbatim from device-controlled receive data -- unvalidated before it was used to index into pktbuffer[8192]. Any rogue or compromised SDIO device could set bytes [2..3] of a frame descriptor to an arbitrary value (0-65535). The subsequent calls to rsi_get_queueno() and rsi_get_length() would then perform an out-of-bounds read of up to 57 KB past the end of pktbuffer, leading to a kernel oops or exposing heap memory to the driver. Fix this by replacing the transport-specific guard with a single check that always validates 'offset' against the actual number of bytes that were read into the buffer for the current transport: - SDIO: rcv_pkt_len (= num_blks * 256, max 7936) - USB: RSI_MAX_RX_USB_PKT_SIZE (= 3000) Also add a check that 'actual_length' is non-zero and does not exceed the remaining buffer length, preventing an infinite loop and a subsequent out-of-bounds 'index' on a crafted packet. Fixes: dad0d04fa0f6 ("rsi: Add RS9113 wireless driver") Signed-off-by: CryoSlayer --- drivers/net/wireless/rsi/rsi_91x_main.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_main.c b/drivers/net/wireless/rsi/rsi_91x_main.c index xxxxxxx..xxxxxxx 100644 --- a/drivers/net/wireless/rsi/rsi_91x_main.c +++ b/drivers/net/wireless/rsi/rsi_91x_main.c @@ -157,6 +157,7 @@ int rsi_read_pkt(struct rsi_common *common, u8 *rx_pkt, s32 rcv_pkt_len) u8 *frame_desc = NULL, extended_desc = 0; u32 index, length = 0, queueno = 0; u16 actual_length = 0, offset; + u32 buf_sz; struct sk_buff *skb = NULL; #ifdef CONFIG_RSI_COEX u8 bt_pkt_type; @@ -164,15 +164,22 @@ int rsi_read_pkt(struct rsi_common *common, u8 *rx_pkt, s32 rcv_pkt_len) + buf_sz = rcv_pkt_len ? (u32)rcv_pkt_len : RSI_MAX_RX_USB_PKT_SIZE; + index = 0; do { frame_desc = &rx_pkt[index]; actual_length = *(u16 *)&frame_desc[0]; offset = *(u16 *)&frame_desc[2]; - if (!rcv_pkt_len && offset > - RSI_MAX_RX_USB_PKT_SIZE - FRAME_DESC_SZ) + + /* Validate actual_length to avoid infinite loop and + * out-of-bounds index on subsequent iterations. + */ + if (actual_length == 0 || actual_length > buf_sz - index) + goto fail; + + /* Validate offset for both USB and SDIO paths. */ + if (offset > buf_sz - index - FRAME_DESC_SZ) goto fail; queueno = rsi_get_queueno(frame_desc, offset);