Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH] wifi: wilc1000: validate packet bounds in RX buffer parsing
@ 2026-09-06 23:37 Aamir Ahmed
  0 siblings, 0 replies; 2+ messages in thread
From: Aamir Ahmed @ 2026-09-06 23:37 UTC (permalink / raw)
  To: Kalle Valo, Ajay Singh; +Cc: linux-wireless, netdev, linux-kernel, stable

wilc_wlan_handle_rx_buff() parses a firmware DMA receive buffer
containing multiple packets.  The header, packet length, packet
offset, and total-length fields are all read from firmware-provided
data without validating them against the remaining buffer size.

A malformed or corrupted receive buffer can cause out-of-bounds reads
in three ways:

1. The 4-byte packet header is read with get_unaligned_le32() when
   fewer than 4 bytes remain in the buffer.

2. For management frames, HOST_HDR_OFFSET + pkt_len bytes are accessed
   past the current offset without checking they fit within tp_len.

3. For data and config frames, pkt_offset + pkt_len bytes are accessed
   without similar bounds checks.

The do-while loop condition (offset < size) is insufficient because it
does not guarantee that a full header or packet body fits in the
remaining bytes.

Fix by:
 - Converting the do-while to a while loop that ensures at least
   sizeof(u32) bytes remain before reading the header.
 - Checking tp_len against the remaining buffer size.
 - Validating pkt_len against tp_len for management frames
   (accounting for HOST_HDR_OFFSET).
 - Validating pkt_offset and pkt_len against tp_len for data
   and config frames.

Fixes: c5c77ba18ea6 ("staging: wilc1000: Add SDIO/SPI 802.11 driver")
Cc: stable@vger.kernel.org
Signed-off-by: Aamir Ahmed <elb12345@hotmail.co.uk>
---
 drivers/net/wireless/microchip/wilc1000/wlan.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/microchip/wilc1000/wlan.c b/drivers/net/wireless/microchip/wilc1000/wlan.c
index 4b116fe6f9ea..3ebcd688f2dd 100644
--- a/drivers/net/wireless/microchip/wilc1000/wlan.c
+++ b/drivers/net/wireless/microchip/wilc1000/wlan.c
@@ -1111,7 +1111,7 @@ static void wilc_wlan_handle_rx_buff(struct wilc *wilc, u8 *buffer, int size)
 	int is_cfg_packet;
 	u8 *buff_ptr;
 
-	do {
+	while (offset + sizeof(u32) <= size) {
 		buff_ptr = buffer + offset;
 		header = get_unaligned_le32(buff_ptr);
 
@@ -1123,11 +1123,19 @@ static void wilc_wlan_handle_rx_buff(struct wilc *wilc, u8 *buffer, int size)
 		if (pkt_len == 0 || tp_len == 0)
 			break;
 
+		if (tp_len > size - offset)
+			break;
+
 		if (pkt_offset & IS_MANAGMEMENT) {
+			if (tp_len < HOST_HDR_OFFSET || pkt_len > tp_len - HOST_HDR_OFFSET)
+				break;
 			buff_ptr += HOST_HDR_OFFSET;
 			wilc_wfi_mgmt_rx(wilc, buff_ptr, pkt_len,
 					 pkt_offset & IS_MGMT_AUTH_PKT);
 		} else {
+			if (pkt_offset > tp_len ||
+			    pkt_len > tp_len - pkt_offset)
+				break;
 			if (!is_cfg_packet) {
 				wilc_frmw_to_host(wilc, buff_ptr, pkt_len,
 						  pkt_offset);
@@ -1148,7 +1156,7 @@ static void wilc_wlan_handle_rx_buff(struct wilc *wilc, u8 *buffer, int size)
 			}
 		}
 		offset += tp_len;
-	} while (offset < size);
+	}
 }
 
 static void wilc_wlan_handle_rxq(struct wilc *wilc)
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH] wifi: wilc1000: validate packet bounds in RX buffer parsing
@ 2026-09-10  6:38 netdev-bot+sashiko
  0 siblings, 0 replies; 2+ messages in thread
From: netdev-bot+sashiko @ 2026-09-10  6:38 UTC (permalink / raw)
  To: elb12345
  Cc: kvalo, ajay.kathat, linux-wireless, netdev, linux-kernel, stable,
	kuba

Thank you for your contribution! Sashiko AI review found 3 potential
issue(s) to consider:

Critical: 1 · High: 1 (1) · Medium: 1 · Low: 0

- [Critical] The `size` that the patched parser now treats as its
  authoritative bound is a raw device-supplied length that is never…
- [High] The new checks bound only the upper end of a record
  (pkt_len/pkt_offset against tp_len, tp_len against size - offset) and…
- [Medium] The new validation still accepts transport records that cannot
  contain their own 4-byte header, and payload offsets that point inside…

Pre-existing issues:
- [High] Short public-action management frames underflow the vendor-IE
  length in wilc_wfi_p2p_rx() (cfg80211.c).

--

wifi: wilc1000: validate packet bounds in RX buffer parsing

The patch converts the record loop in wilc_wlan_handle_rx_buff() from a
do-while into a while loop that requires four header bytes to remain, and
adds checks of tp_len against the remaining buffer plus pkt_len/pkt_offset
against tp_len.  A few questions about how complete the new bounds are.

> diff --git a/drivers/net/wireless/microchip/wilc1000/wlan.c b/drivers/net/wireless/microchip/wilc1000/wlan.c
> index 4b116fe6f9ea9..3ebcd688f2dd5 100644
> --- a/drivers/net/wireless/microchip/wilc1000/wlan.c
> +++ b/drivers/net/wireless/microchip/wilc1000/wlan.c
> @@ -1111,7 +1111,7 @@ static void wilc_wlan_handle_rx_buff(struct wilc *wilc, u8 *buffer, int size)
>  	int is_cfg_packet;
>  	u8 *buff_ptr;
>  
> -	do {
> +	while (offset + sizeof(u32) <= size) {
>  		buff_ptr = buffer + offset;
>  		header = get_unaligned_le32(buff_ptr);
>  
> @@ -1123,11 +1123,19 @@ static void wilc_wlan_handle_rx_buff(struct wilc *wilc, u8 *buffer, int size)
>  		if (pkt_len == 0 || tp_len == 0)
>  			break;
>  
> +		if (tp_len > size - offset)
> +			break;

[Severity: Critical]
Is "size" itself known to describe memory inside the RX allocation?  The
only producer is wilc_wlan_handle_isr_ext():

drivers/net/wireless/microchip/wilc1000/wlan.c:wilc_wlan_handle_isr_ext() {
	size = FIELD_GET(WILC_INTERRUPT_DATA_SIZE, int_status) << 2;
	...
	if (size <= 0)
		return;

	if (WILC_RX_BUFF_SIZE - offset < size)
		offset = 0;

	buffer = &wilc->rx_buffer[offset];
	...
	ret = wilc->hif_func->hif_block_rx_ext(wilc, 0, buffer, size);
	...
	offset += size;
	wilc->rx_buffer_offset = offset;
	...
	rqe->buffer = buffer;
	rqe->buffer_size = size;
}

WILC_INTERRUPT_DATA_SIZE is GENMASK(14, 0) and the field is word scaled, so
the chip can advertise 32767 << 2 = 131068 bytes, while wilc->rx_buffer is a
single WILC_RX_BUFF_SIZE (96 * 1024 = 98304) byte allocation.  Since size is
u32 the "size <= 0" test never fires, and for size > 98304 the code only
rewinds offset to 0 and still issues hif_block_rx_ext() for the full size;
wilc_sdio_read() and wilc_spi_read() only reject size <= 4, so nothing clamps
the transfer.  Can this write past the end of wilc->rx_buffer?

There also seems to be a second-order effect: rx_buffer_offset is then set to
offset + size (up to 131068), and on the next interrupt
"WILC_RX_BUFF_SIZE - offset" is evaluated in unsigned arithmetic, so
98304 - 131068 wraps to a huge value that is never < size and the rewind
never triggers again.  Would that leave buffer pointing entirely outside the
allocation for every subsequent block read?

Because rqe->buffer_size carries that same unvalidated length, every new
check added here ("tp_len > size - offset" and friends) can pass while the
parser walks memory outside the allocation.  Would clamping size to
WILC_RX_BUFF_SIZE in wilc_wlan_handle_isr_ext() be needed for the new bounds
in this function to mean anything?

>  		if (pkt_offset & IS_MANAGMEMENT) {
> +			if (tp_len < HOST_HDR_OFFSET || pkt_len > tp_len - HOST_HDR_OFFSET)
> +				break;
>  			buff_ptr += HOST_HDR_OFFSET;
>  			wilc_wfi_mgmt_rx(wilc, buff_ptr, pkt_len,
>  					 pkt_offset & IS_MGMT_AUTH_PKT);
>  		} else {

[Severity: High]
This isn't a bug introduced by this patch, but since the management check
here only caps pkt_len from above, short management frames still reach
wilc_wfi_p2p_rx() in cfg80211.c, where the vendor IE length underflows:

drivers/net/wireless/microchip/wilc1000/cfg80211.c:wilc_wfi_p2p_rx() {
	...
	if (!ieee80211_is_action(mgmt->frame_control) ||
	    !ieee80211_is_public_action((struct ieee80211_hdr *)buff, size))
	...
	d = (struct wilc_p2p_pub_act_frame *)(&mgmt->u.action);
	if (d->oui_subtype != GO_NEG_REQ && d->oui_subtype != GO_NEG_RSP &&
	...
	vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
					    buff + ie_offset, size - ie_offset);
}

ie_offset is offsetof(struct ieee80211_mgmt, u) + sizeof(*d) = 24 + 8 = 32,
but ieee80211_is_public_action() only guarantees
IEEE80211_MIN_ACTION_SIZE(category), i.e. 25 bytes.  For a 31 byte frame
d->oui_subtype (frame offset 30) is still in bounds, and "size - ie_offset"
with u32 size wraps to roughly 4 GiB.  Does that let cfg80211_find_vendor_ie()
scan far past the RX buffer?

A "size >= ie_offset" guard in wilc_wfi_p2p_rx(), and/or a minimum pkt_len
check for management records here, would close it.

> +			if (pkt_offset > tp_len ||
> +			    pkt_len > tp_len - pkt_offset)
> +				break;

[Severity: High]
The new checks bound the upper end of a record but never require a minimum
length, since only zero pkt_len/tp_len is rejected.  Do the consumers still
read past what has been validated?

For the data path, tp_len = pkt_len = 1 with pkt_offset = 0 passes both
checks, and then:

drivers/net/wireless/microchip/wilc1000/netdev.c:wilc_frmw_to_host() {
	...
	wilc_netdev = get_if_handler(wilc, buff);
	if (!wilc_netdev)
		goto out;

	buff += pkt_offset;
}

get_if_handler() is called with the record start, before pkt_offset is added,
and does:

drivers/net/wireless/microchip/wilc1000/netdev.c:get_if_handler() {
	struct ieee80211_hdr *h = (struct ieee80211_hdr *)mac_header;
	...
			if (ether_addr_equal_unaligned(h->addr2, vif->bssid)) {
}

That touches bytes 4..15 of the record.  With the loop guard only requiring
offset + 4 <= size, a record at offset == size - 4 would read up to 12 bytes
beyond the region described by size.

For the config path, pkt_len == 1 is accepted, but:

drivers/net/wireless/microchip/wilc1000/wlan_cfg.c:wilc_wlan_cfg_indicate_rx() {
	msg_type = frame[0];
	msg_id = frame[1];	/* seq no */
	frame += 4;
	size -= 4;
}

size goes negative, and the info handlers then do:

drivers/net/wireless/microchip/wilc1000/hif.c:wilc_gnrl_async_info_received() {
	id = get_unaligned_le32(&buffer[length - 4]);
}

with a u32 length, so length < 4 indexes at 0xFFFFFFFD.  The same pattern
appears in wilc_network_info_received(), which additionally does
kmemdup(&buffer[9], get_unaligned_le16(&buffer[6]) - 1) and can copy up to
65534 bytes out of a record validated as 1..8 bytes.

On the management side, pkt_len == 1 with tp_len == 5 passes, yet
wilc_wfi_mgmt_rx() reads two bytes:

drivers/net/wireless/microchip/wilc1000/netdev.c:wilc_wfi_mgmt_rx() {
		struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buff;
		u16 type = le16_to_cpup((__le16 *)buff);
}

Would per-branch minimum lengths be needed here, at least
sizeof(struct ieee80211_hdr) before the data classifier, 4 plus the subtype
requirements for config frames, and the parsed header size for management
frames?

[Severity: Medium]
Can a record smaller than its own header still be accepted?  tp_len is only
rejected when zero or larger than the remaining bytes, so tp_len in 1..3
passes even though four header bytes were already consumed, and this branch
accepts pkt_offset in 0..3.

With tp_len = 1, pkt_offset = 0, pkt_len = 1, one byte of the framing header
itself is handed to wilc_frmw_to_host() as packet data, and

		offset += tp_len;

advances by 1, so the next iteration parses a header overlapping the record
just consumed.  Even with a large tp_len, pkt_offset = 0 makes the four byte
receive header the first bytes of the Ethernet or configuration frame, which
wilc_wlan_cfg_indicate_rx() then reads as msg_type/msg_id.

Should the checks require tp_len >= sizeof(u32), and pkt_offset >=
HOST_HDR_OFFSET for non-management records?

>  			if (!is_cfg_packet) {
>  				wilc_frmw_to_host(wilc, buff_ptr, pkt_len,
>  						  pkt_offset);

[ ... ]

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/AS8P251MB0001BF71A8397CE77A82E7E2C8B32%40AS8P251MB0001.EURP251.PROD.OUTLOOK.COM

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-09-10  6:38 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-10  6:38 [PATCH] wifi: wilc1000: validate packet bounds in RX buffer parsing netdev-bot+sashiko
  -- strict thread matches above, loose matches on Subject: below --
2026-09-06 23:37 Aamir Ahmed

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox