mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH wireless] wifi: nxpwifi: bound the pairwise-cipher OUI walk to the IE length
@ 2026-11-11 20:59 Paulneja
  0 siblings, 0 replies; only message in thread
From: Paulneja @ 2026-11-11 20:59 UTC (permalink / raw)
  To: Jeff Chen, Johannes Berg
  Cc: Francesco Dolcini, linux-wireless, linux-kernel, stable

nxpwifi_search_oui_in_ie() takes the pairwise cipher suite count from the
RSN or WPA element of a beacon or probe response and compares that many
4-byte suite selectors against the wanted one. The count is not checked
against the length of the element, and the beacon parser records the
element on its ID alone (the RSN case in nxpwifi_update_bss_desc_with_ie()
has no length check at all, the WPA one only requires the OUI and type).

An element which is shorter than its count claims therefore makes the
walk read up to 255 * 4 bytes past the element. The element lives in the
buffer nxpwifi_fill_new_bss_desc() kmemdup()s from the cfg80211 BSS
entry, which is exactly as long as the received elements, so an element
placed last reads past the end of the allocation. The walk runs from
nxpwifi_is_network_compatible() when a station with WPA or WPA2 enabled
connects to an HT-capable BSS, on data that the AP controls.

mwifiex, which this code was derived from, had the same walk and was
fixed by commit e667aee1c192 ("wifi: mwifiex: bound the pairwise-cipher
OUI walk to the IE length"). Do the same here: pass the number of element
bytes available at the suite list and refuse a count that does not fit.
The length stays signed so that an element too short to hold even the
fixed part is rejected rather than wrapped. In nxpwifi the vendor header
keeps the OUI type in a field of its own, so both are subtracted to get
the bytes available in the WPA element.

Fixes: 73b01e57ed3e ("wifi: nxp: add nxpwifi driver for IW61x")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Paulneja <paulnejacontacto@gmail.com>
---

Notes:
    I went through this by hand before sending it: checked the bound against the
    layout of struct ie_body (the suite list starts 6 bytes in) and of struct
    ieee80211_vendor_ie (OUI and OUI type are separate fields here, which is the
    one place this differs from the mwifiex fix), compared it line by line with
    e667aee1c192, and ran the test below myself.
    
    An LLM was used to locate the bug and to draft the patch.
    
    Testing: no IW61x hardware here, so this was not run in a kernel. The three
    functions were copied verbatim into a userspace harness and run under
    AddressSanitizer with an RSN element of length 8 and a pairwise count of
    255 placed last in an exact-size buffer: heap-buffer-overflow read before
    the patch, clean after it, and a well-formed element still matches.
    scan.o builds without warnings with W=1.

 drivers/net/wireless/nxp/nxpwifi/scan.c | 23 ++++++++++++++++++++---
 1 file changed, 20 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/nxp/nxpwifi/scan.c b/drivers/net/wireless/nxp/nxpwifi/scan.c
index 4eeba6700aa7..33779e21336b 100644
--- a/drivers/net/wireless/nxp/nxpwifi/scan.c
+++ b/drivers/net/wireless/nxp/nxpwifi/scan.c
@@ -79,12 +79,20 @@ _dbg_security_flags(int log_level, const char *func, const char *desc,
 
 /* Parse a WPA/RSN element and check whether its PTK list contains the OUI */
 static u8
-nxpwifi_search_oui_in_ie(struct ie_body *iebody, u8 *oui)
+nxpwifi_search_oui_in_ie(struct ie_body *iebody, u8 *oui, int ie_len)
 {
+	const size_t ptk_body_offset = offsetof(struct ie_body, ptk_body);
 	u8 count;
 
+	/* ie_len is the number of element bytes available at iebody */
+	if (ie_len < 0 || (size_t)ie_len < ptk_body_offset)
+		return NXPWIFI_OUI_NOT_PRESENT;
+
 	count = iebody->ptk_cnt[0];
 
+	if (ptk_body_offset + count * sizeof(iebody->ptk_body) > (size_t)ie_len)
+		return NXPWIFI_OUI_NOT_PRESENT;
+
 	/*
 	 * PTK may contain multiple OUIs; iterate through the list and compare
 	 * each one
@@ -113,10 +121,13 @@ nxpwifi_is_rsn_oui_present(struct nxpwifi_bssdescriptor *bss_desc,
 	__be32 oui = cpu_to_be32(cipher);
 
 	if (bss_desc->bcn_rsn_ie) {
+		int ie_len = (int)bss_desc->bcn_rsn_ie->datalen -
+			     RSN_GTK_OUI_OFFSET;
+
 		iebody = (struct ie_body *)
 			 (((u8 *)bss_desc->bcn_rsn_ie->data) +
 			  RSN_GTK_OUI_OFFSET);
-		ret = nxpwifi_search_oui_in_ie(iebody, (u8 *)&oui);
+		ret = nxpwifi_search_oui_in_ie(iebody, (u8 *)&oui, ie_len);
 		if (ret)
 			return ret;
 	}
@@ -132,9 +143,15 @@ nxpwifi_is_wpa_oui_present(struct nxpwifi_bssdescriptor *bss_desc, u32 cipher)
 	__be32 oui = cpu_to_be32(cipher);
 
 	if (bss_desc->bcn_wpa_ie) {
+		/* data[] starts after the OUI and OUI type */
+		int ie_len = (int)bss_desc->bcn_wpa_ie->vend_hdr.len -
+			     (int)sizeof(bss_desc->bcn_wpa_ie->vend_hdr.oui) -
+			     (int)sizeof(bss_desc->bcn_wpa_ie->vend_hdr.oui_type) -
+			     WPA_GTK_OUI_OFFSET;
+
 		iebody = (struct ie_body *)((u8 *)bss_desc->bcn_wpa_ie->data +
 					    WPA_GTK_OUI_OFFSET);
-		ret = nxpwifi_search_oui_in_ie(iebody, (u8 *)&oui);
+		ret = nxpwifi_search_oui_in_ie(iebody, (u8 *)&oui, ie_len);
 		if (ret)
 			return ret;
 	}

base-commit: cb1edd20907e13b058c0d5bd34c9d51b5bedc879
-- 
2.56.0



^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-11-11 20:59 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-11 20:59 [PATCH wireless] wifi: nxpwifi: bound the pairwise-cipher OUI walk to the IE length Paulneja

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

all inboxes | Powered by JetHome®