[PATCH 3/3] wifi: ipw2x00: bound management frame length to the receive buffer

Shmulik Cohen <[email protected]>
Newsgroups org.kernel.vger.linux-wireless,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Both management receive paths establish a lower bound on the frame
length and no upper bound, even though the length originates from the
device.

ipw2100_corruption_check() returns 0 without inspecting frame_size for
management frames, and __ipw2100_rx_process() only rejects a frame
smaller than the three-address header, so any reported size up to the
u32 limit reaches libipw_rx_mgt() against a receive allocation of
IPW_RX_NIC_BUFFER_LENGTH bytes.  Check frame_size itself rather than
stats.len, which is a u16: a size of 65566 truncates to 30 on
assignment and would pass a check made afterwards.

ipw_rx() likewise only rejects a frame shorter than the header length.
Bound it against the DMA mapped receive buffer.  The size passed to
alloc_skb() is rounded up by the allocator, so skb_tailroom() can
exceed IPW_RX_BUF_SIZE and is not a usable bound here; the existing
uses of that idiom in the data paths are too permissive for the same
reason.

libipw then hands the remainder to libipw_parse_info_param(), which
walks information elements for as long as the length allows, so an
over-long reported length reads past the receive buffer without any
wraparound being involved.

The length is device-reported, so per
Documentation/process/threat-model.rst this is a robustness fix rather
than a vulnerability.

Found by an AI-assisted review of length arithmetic in management frame
parsers.  Compile-tested only for these two hunks; I do not have the
hardware, so they are not tested on a real device.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Shmulik Cohen <[email protected]>
---
 drivers/net/wireless/intel/ipw2x00/ipw2100.c | 4 +++-
 drivers/net/wireless/intel/ipw2x00/ipw2200.c | 9 +++++++++
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c
index 2b8a23865bfb..43b4e432956b 100644
--- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c
@@ -2712,7 +2712,9 @@ static void __ipw2100_rx_process(struct ipw2100_priv *priv)
 				break;
 			}
 #endif
-			if (stats.len < sizeof(struct libipw_hdr_3addr))
+			if (sq->drv[i].frame_size <
+				    sizeof(struct libipw_hdr_3addr) ||
+			    sq->drv[i].frame_size > IPW_RX_NIC_BUFFER_LENGTH)
 				break;
 			switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) {
 			case IEEE80211_FTYPE_MGMT:
diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c
index 4bc9bb406e8e..8249d493ee22 100644
--- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c
+++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c
@@ -8322,6 +8322,15 @@ static void ipw_rx(struct ipw_priv *priv)
 					break;
 				}
 
+				if (unlikely(le16_to_cpu(pkt->u.frame.length) >
+					     IPW_RX_BUF_SIZE -
+					     IPW_RX_FRAME_SIZE)) {
+					IPW_DEBUG_DROP("Received oversized packet. Dropping.\n");
+					priv->net_dev->stats.rx_errors++;
+					priv->wstats.discard.misc++;
+					break;
+				}
+
 				switch (WLAN_FC_GET_TYPE
 					(le16_to_cpu(header->frame_ctl))) {
 
-- 
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.