[PATCH v2] wifi: ath9k: validate USB RX stream lengths before copying

Pengpeng Hou <[email protected]>
Newsgroups org.kernel.vger.linux-wireless,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
ath9k_hif_usb_rx_stream() parses records from one USB receive buffer
and also completes records split across two buffers. It checks the stream
tag and a packet length ceiling, but not every fixed header, current
payload extent or continuation extent before reading or copying.

Validate the fixed header and complete-record extent, bound a split
record to the capacity of two receive buffers, and require the next
buffer to contain the expected continuation. Clear all saved continuation
state when a malformed record is dropped.

Fixes: fb9987d0f748 ("ath9k_htc: Support for AR9271 chipset.")
Assisted-by: Codex:gpt-5
Signed-off-by: Pengpeng Hou <[email protected]>
---
Changes since v1: https://lore.kernel.org/all/[email protected]/
- validate fixed headers and complete records before copying
- prove the split-record path against two receive-buffer capacities
- clear every saved continuation field after success or rejection

Every RX parser branch was reviewed statically; malformed USB traffic was
not injected on ath9k hardware.

 drivers/net/wireless/ath/ath9k/hif_usb.c | 33 ++++++++++++++++++++++--
 1 file changed, 31 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index 47f904e7e652..8d60779165ab 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -566,6 +566,18 @@ static void ath9k_hif_usb_rx_stream(struct hif_device_usb *hif_dev,
 	if (rx_remain_len != 0) {
 		struct sk_buff *remain_skb = hif_dev->remain_skb;
 
+		if (rx_remain_len < hif_dev->rx_pad_len ||
+		    rx_remain_len > len) {
+			dev_kfree_skb_any(remain_skb);
+			hif_dev->remain_skb = NULL;
+			hif_dev->rx_remain_len = 0;
+			hif_dev->rx_transfer_len = 0;
+			hif_dev->rx_pad_len = 0;
+			RX_STAT_INC(hif_dev, skb_dropped);
+			spin_unlock(&hif_dev->rx_lock);
+			return;
+		}
+
 		if (remain_skb) {
 			ptr = (u8 *) remain_skb->data;
 
@@ -579,11 +591,13 @@ static void ath9k_hif_usb_rx_stream(struct hif_device_usb *hif_dev,
 			skb_put(remain_skb, rx_pkt_len);
 
 			skb_pool[pool_index++] = remain_skb;
-			hif_dev->remain_skb = NULL;
-			hif_dev->rx_remain_len = 0;
 		} else {
 			index = rx_remain_len;
 		}
+		hif_dev->remain_skb = NULL;
+		hif_dev->rx_remain_len = 0;
+		hif_dev->rx_transfer_len = 0;
+		hif_dev->rx_pad_len = 0;
 	}
 
 	spin_unlock(&hif_dev->rx_lock);
@@ -596,6 +610,11 @@ static void ath9k_hif_usb_rx_stream(struct hif_device_usb *hif_dev,
 
 		ptr = (u8 *) skb->data;
 
+		if (len - index < 4) {
+			RX_STAT_INC(hif_dev, skb_dropped);
+			goto invalid_pkt;
+		}
+
 		pkt_len = get_unaligned_le16(ptr + index);
 		pkt_tag = get_unaligned_le16(ptr + index + 2);
 
@@ -624,6 +643,11 @@ static void ath9k_hif_usb_rx_stream(struct hif_device_usb *hif_dev,
 		index = index + 4 + pkt_len + pad_len;
 
 		if (index > MAX_RX_BUF_SIZE) {
+			if (len < MAX_RX_BUF_SIZE || index > 2 * MAX_RX_BUF_SIZE) {
+				RX_STAT_INC(hif_dev, skb_dropped);
+				goto invalid_pkt;
+			}
+
 			spin_lock(&hif_dev->rx_lock);
 			nskb = __dev_alloc_skb(pkt_len + 32, GFP_ATOMIC);
 			if (!nskb) {
@@ -648,6 +672,11 @@ static void ath9k_hif_usb_rx_stream(struct hif_device_usb *hif_dev,
 			hif_dev->remain_skb = nskb;
 			spin_unlock(&hif_dev->rx_lock);
 		} else {
+			if (index > len) {
+				RX_STAT_INC(hif_dev, skb_dropped);
+				goto invalid_pkt;
+			}
+
 			if (pool_index == MAX_PKT_NUM_IN_TRANSFER) {
 				dev_err(&hif_dev->udev->dev,
 					"ath9k_htc: over RX MAX_PKT_NUM\n");
-- 
2.50.1 (Apple Git-155)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.