wifi: mwifiex: bound the pairwise-cipher OUI walk to the IE length

mwifiex_search_oui_in_ie() reads a pairwise-cipher (PTK) count from a
beacon/probe-response RSN or WPA information element and then walks that
many 4-byte OUIs, comparing each with memcmp(). The count comes straight
from the (attacker-supplied) IE and is never checked against the
element's own length, and the callers admit the element on element_id
alone (has_ieee_hdr() / has_vendor_hdr(), no length check). A crafted
RSN/WPA IE with a large pairwise count therefore makes the walk read up
to 255 * 4 bytes past the element -- an out-of-bounds read of the
kmemdup()'d beacon buffer, reachable from any AP whose beacon/probe
response is processed during scan-result parsing.

Pass the number of IE bytes available at the OUI list and bound the walk
to the element. Keep the length signed and reject a negative value
before any unsigned arithmetic, so a small or zero IE length cannot
underflow to a large size_t and defeat the bound.

Found by 0sec automated security-research tooling (https://0sec.ai).

Fixes: 5e6e3a92b9 ("wireless: mwifiex: initial commit for Marvell mwifiex driver")
Cc: stable@vger.kernel.org
Assisted-by: 0sec:multi-model
Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
Link: https://patch.msgid.link/20260814134704.85902-1-doruk@0sec.ai
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Doruk Tan Ozturk 2026-08-14 15:47:04 +02:00 committed by Johannes Berg
parent e2de8d5eb2
commit e667aee1c1

View File

@ -104,12 +104,24 @@ has_vendor_hdr(struct ieee_types_vendor_specific *ie, u8 key)
* a given oui in PTK.
*/
static u8
mwifiex_search_oui_in_ie(struct ie_body *iebody, u8 *oui)
mwifiex_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 bytes available at iebody. Keep it signed
* and reject a negative (underflowed) length before the unsigned
* comparisons below, so a small or zero IE length cannot wrap.
*/
if (ie_len < 0 || (size_t)ie_len < ptk_body_offset)
return MWIFIEX_OUI_NOT_PRESENT;
count = iebody->ptk_cnt[0];
/* Reject an OUI count whose list would run past the element. */
if (ptk_body_offset + count * sizeof(iebody->ptk_body) > (size_t)ie_len)
return MWIFIEX_OUI_NOT_PRESENT;
/* There could be multiple OUIs for PTK hence
1) Take the length.
2) Check all the OUIs for AES.
@ -143,11 +155,14 @@ mwifiex_is_rsn_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher)
u8 ret = MWIFIEX_OUI_NOT_PRESENT;
if (has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN)) {
int ie_len = (int)bss_desc->bcn_rsn_ie->ieee_hdr.len -
RSN_GTK_OUI_OFFSET;
iebody = (struct ie_body *)
(((u8 *) bss_desc->bcn_rsn_ie->data) +
RSN_GTK_OUI_OFFSET);
oui = &mwifiex_rsn_oui[cipher][0];
ret = mwifiex_search_oui_in_ie(iebody, oui);
ret = mwifiex_search_oui_in_ie(iebody, oui, ie_len);
if (ret)
return ret;
}
@ -169,10 +184,14 @@ mwifiex_is_wpa_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher)
u8 ret = MWIFIEX_OUI_NOT_PRESENT;
if (has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC)) {
int ie_len = (int)bss_desc->bcn_wpa_ie->vend_hdr.len -
(int)sizeof(bss_desc->bcn_wpa_ie->vend_hdr.oui) -
WPA_GTK_OUI_OFFSET;
iebody = (struct ie_body *)((u8 *)bss_desc->bcn_wpa_ie->data +
WPA_GTK_OUI_OFFSET);
oui = &mwifiex_wpa_oui[cipher][0];
ret = mwifiex_search_oui_in_ie(iebody, oui);
ret = mwifiex_search_oui_in_ie(iebody, oui, ie_len);
if (ret)
return ret;
}