[PATCH v5 2/3] staging: rtl8723bs: fix OOB reads in IE loops in issue_assocreq() and join_cmd_hdl()
From: Alexandru Hossu
Date: Mon May 11 2026 - 13:07:19 EST
Seven out-of-bounds read paths in the IE parsing loops of
issue_assocreq() and join_cmd_hdl():
1. Vendor-specific OUI comparison reads 4 bytes past a possibly short
IE payload (issue_assocreq).
For WLAN_EID_VENDOR_SPECIFIC, the code calls memcmp(pIE->data,
OUI, 4) on RTW_WPA_OUI, WMM_OUI, and WPS_OUI without first
verifying that pIE->length is at least 4. Add pIE->length >= 4
guard before the comparisons.
2. WPS truncation path passes vs_ie_length = 14 when pIE->length < 14
(issue_assocreq).
When wifi_spec is 0 and the IE matches WPS_OUI, the code sets
vs_ie_length = 14 and passes pIE->data to rtw_set_ie() regardless
of pIE->length. If pIE->length is between 4 and 13, rtw_set_ie()
reads up to (14 - pIE->length) bytes past the IE payload. Skip the
IE with break when pIE->length < 14.
3. HT Capability IE memcpy reads sizeof(struct HT_caps_element) bytes
from an IE that may be shorter (issue_assocreq).
The WLAN_EID_HT_CAPABILITY handler copies:
memcpy(&pmlmeinfo->HT_caps, pIE->data, sizeof(struct HT_caps_element));
If pIE->length < sizeof(struct HT_caps_element), the memcpy reads
beyond the end of the IE payload. Add a minimum length check and
skip the IE if it is too short.
4. rtw_set_ie called with untrusted pIE->length for HT Capability
(issue_assocreq).
After the memcpy the code passes pIE->length directly to
rtw_set_ie() as the IE body length. If pIE->length exceeds
sizeof(struct HT_caps_element), rtw_set_ie copies that many bytes
from pmlmeinfo->HT_caps, reading past the end of the struct.
Use sizeof(struct HT_caps_element) instead.
5. WMM guard in join_cmd_hdl() insufficient for WMM_param_handler().
The WLAN_EID_VENDOR_SPECIFIC handler in join_cmd_hdl() calls
WMM_param_handler() after a pIE->length >= 4 OUI check.
WMM_param_handler() reads pIE->data + 6 and copies
sizeof(struct WMM_para_element) = 18 bytes, requiring a minimum of
24 bytes total. Strengthen the guard to pIE->length >= WLAN_WMM_LEN.
6. HT Operation IE accessed without minimum length check (join_cmd_hdl).
The WLAN_EID_HT_OPERATION handler casts pIE->data to
struct HT_info_element * and reads pht_info->infos[0] (offset 1)
without verifying pIE->length >= sizeof(struct HT_info_element).
A zero- or one-byte HT Operation IE causes an out-of-bounds read.
Add a minimum length check and break if the IE is too short.
7. Loop advancement uses literal 2 instead of sizeof(*pIE) in both
loops.
i += (pIE->length + 2) is functionally equivalent to
i += sizeof(*pIE) + pIE->length today, but the literal 2 is
inconsistent with the sizeof(*pIE) guards added at the top of each
loop. Use sizeof(*pIE) + pIE->length for consistency.
Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Alexandru Hossu <hossu.alexandru@xxxxxxxxx>
---
Changes in v5:
- In the WPS truncation path of issue_assocreq(), v4 set
vs_ie_length = 14 and called rtw_set_ie() with pIE->data even when
pIE->length < 14, reading (14 - pIE->length) bytes past the IE
payload. Fixed by breaking out of the switch when pIE->length < 14
(sashiko review of v4).
- The WMM guard in join_cmd_hdl() was pIE->length >= 4, sufficient for
the OUI check but not for WMM_param_handler(), which reads
pIE->data + 6 and copies sizeof(struct WMM_para_element) = 18 bytes
(total 24). Strengthened to pIE->length >= WLAN_WMM_LEN
(sashiko review of v4).
Changes in v4:
- Add pIE->length >= 4 guard before the 4-byte OUI memcmps in the
WLAN_EID_VENDOR_SPECIFIC cases of both functions (sashiko review of v3).
- In issue_assocreq() WLAN_EID_HT_CAPABILITY: add minimum length check
(pIE->length < sizeof(struct HT_caps_element)) and use
sizeof(struct HT_caps_element) instead of pIE->length in rtw_set_ie()
to prevent OOB reads past the HT_caps struct (sashiko review of v3).
- In join_cmd_hdl() WLAN_EID_HT_OPERATION: add minimum length check
(pIE->length < sizeof(struct HT_info_element)) before casting pIE->data
to struct HT_info_element * and reading infos[0] (sashiko review of v3).
Changes in v3:
- No code changes from v2.
Changes in v2:
- Add IE loop header and payload bounds checks for issue_assocreq()
and join_cmd_hdl().
drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 26 ++++++++++++++++++--------
1 file changed, 18 insertions(+), 8 deletions(-)
diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
index 68ce422305ed..0c4a73805d39 100644
--- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
+++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
@@ -2943,9 +2943,10 @@ void issue_assocreq(struct adapter *padapter)
switch (pIE->element_id) {
case WLAN_EID_VENDOR_SPECIFIC:
- if ((!memcmp(pIE->data, RTW_WPA_OUI, 4)) ||
+ if (pIE->length >= 4 &&
+ ((!memcmp(pIE->data, RTW_WPA_OUI, 4)) ||
(!memcmp(pIE->data, WMM_OUI, 4)) ||
- (!memcmp(pIE->data, WPS_OUI, 4))) {
+ (!memcmp(pIE->data, WPS_OUI, 4)))) {
vs_ie_length = pIE->length;
if ((!padapter->registrypriv.wifi_spec) && (!memcmp(pIE->data, WPS_OUI, 4))) {
/* Commented by Kurt 20110629
@@ -2953,7 +2954,8 @@ void issue_assocreq(struct adapter *padapter)
* would be fail if we append vendor
* extensions information to AP
*/
-
+ if (pIE->length < 14)
+ break;
vs_ie_length = 14;
}
@@ -2967,8 +2969,10 @@ void issue_assocreq(struct adapter *padapter)
case WLAN_EID_HT_CAPABILITY:
if (padapter->mlmepriv.htpriv.ht_option) {
if (!(is_ap_in_tkip(padapter))) {
+ if (pIE->length < sizeof(struct HT_caps_element))
+ break;
memcpy(&(pmlmeinfo->HT_caps), pIE->data, sizeof(struct HT_caps_element));
- pframe = rtw_set_ie(pframe, WLAN_EID_HT_CAPABILITY, pIE->length, (u8 *)(&(pmlmeinfo->HT_caps)), &(pattrib->pktlen));
+ pframe = rtw_set_ie(pframe, WLAN_EID_HT_CAPABILITY, sizeof(struct HT_caps_element), (u8 *)(&(pmlmeinfo->HT_caps)), &(pattrib->pktlen));
}
}
break;
@@ -2981,7 +2985,7 @@ void issue_assocreq(struct adapter *padapter)
break;
}
- i += (pIE->length + 2);
+ i += sizeof(*pIE) + pIE->length;
}
if (pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_REALTEK)
@@ -5340,7 +5344,8 @@ u8 join_cmd_hdl(struct adapter *padapter, u8 *pbuf)
switch (pIE->element_id) {
case WLAN_EID_VENDOR_SPECIFIC:/* Get WMM IE. */
- if (!memcmp(pIE->data, WMM_OUI, 4))
+ if (pIE->length >= WLAN_WMM_LEN &&
+ !memcmp(pIE->data, WMM_OUI, 4))
WMM_param_handler(padapter, pIE);
break;
@@ -5353,7 +5358,12 @@ u8 join_cmd_hdl(struct adapter *padapter, u8 *pbuf)
/* spec case only for cisco's ap because cisco's ap issue assoc rsp using mcs rate @40MHz or @20MHz */
{
- struct HT_info_element *pht_info = (struct HT_info_element *)(pIE->data);
+ struct HT_info_element *pht_info;
+
+ if (pIE->length < sizeof(struct HT_info_element))
+ break;
+
+ pht_info = (struct HT_info_element *)(pIE->data);
if (pnetwork->configuration.ds_config <= 14) {
if ((pregpriv->bw_mode & 0x0f) > CHANNEL_WIDTH_20)
@@ -5384,7 +5394,7 @@ u8 join_cmd_hdl(struct adapter *padapter, u8 *pbuf)
break;
}
- i += (pIE->length + 2);
+ i += sizeof(*pIE) + pIE->length;
}
/* check channel, bandwidth, offset and switch */
--
2.53.0