[PATCH] staging: rtl8723bs: fix buffer overflow and OOB accesses in rtw_check_beacon_data()
From: Park Tae-sun
Date: Mon Sep 21 2026 - 03:39:59 EST
In rtw_check_beacon_data(), the Information Element (IE) parsing logic
has boundary validation issues and search window calculations that can
lead to out-of-bounds accesses:
First, IEEE 802.11 beacon frames contain 12 bytes of fixed parameters
(Timestamp, Beacon Interval, Capability Info) before variable IEs start
at _BEACON_IE_OFFSET_ (12). The function checks len < 0, but if len is
smaller than _BEACON_IE_OFFSET_, (pbss_network->ie_length -
_BEACON_IE_OFFSET_) underflows on the u32 field, and reading the beacon
interval at offset 8 via rtw_get_beacon_interval_from_ie() accesses
out-of-bounds data if len < 10.
Second, the manual vendor IE loops for WPA and WMM advance p by
(ie_len + 2) on non-matching elements, but calculate the limit passed
to rtw_get_ie() as:
(pbss_network->ie_length - _BEACON_IE_OFFSET_ - (ie_len + 2))
This only subtracts the previous element's length rather than the
accumulated offset (p - start), causing p + limit to extend past the
end of the buffer on subsequent iterations. In addition, calling
memcmp() without checking ie_len can read past short vendor elements.
Third, in the WMM loop, once the OUI matches, bytes up to *(p + 22)
are modified without checking whether the IE contains the full
24-byte WMM parameter payload (WLAN_WMM_LEN). A truncated element
leads to out-of-bounds writes.
Address these by:
1. Checking len < _BEACON_IE_OFFSET_ at function entry.
2. Using the existing rtw_get_ie_ex() helper which validates
element boundaries and OUI lengths before calling memcmp().
3. Passing ie_len directly to rtw_parse_wpa_ie() without +2,
because rtw_get_ie_ex() already includes the 2-byte header.
4. Verifying that the WMM element has at least WLAN_WMM_LEN + 2
bytes before modifying the parameter records.
Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Park Tae-sun <ts930@xxxxxxxxx>
---
drivers/staging/rtl8723bs/core/rtw_ap.c | 81 ++++++++++---------------
1 file changed, 33 insertions(+), 48 deletions(-)
diff --git a/drivers/staging/rtl8723bs/core/rtw_ap.c b/drivers/staging/rtl8723bs/core/rtw_ap.c
index 4728f62a1557..70e95b07ca2a 100644
--- a/drivers/staging/rtl8723bs/core/rtw_ap.c
+++ b/drivers/staging/rtl8723bs/core/rtw_ap.c
@@ -771,7 +771,7 @@ int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len)
if (!check_fwstate(pmlmepriv, WIFI_AP_STATE))
return _FAIL;
- if (len < 0 || len > MAX_IE_SZ)
+ if (len < _BEACON_IE_OFFSET_ || len > MAX_IE_SZ)
return _FAIL;
pbss_network->ie_length = len;
@@ -885,61 +885,46 @@ int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len)
group_cipher = 0; pairwise_cipher = 0;
psecuritypriv->wpa_group_cipher = _NO_PRIVACY_;
psecuritypriv->wpa_pairwise_cipher = _NO_PRIVACY_;
- for (p = ie + _BEACON_IE_OFFSET_; ; p += (ie_len + 2)) {
- p = rtw_get_ie(p,
- WLAN_EID_VENDOR_SPECIFIC,
- &ie_len,
- (pbss_network->ie_length - _BEACON_IE_OFFSET_ - (ie_len + 2)));
- if ((p) && (!memcmp(p + 2, OUI1, 4))) {
- if (rtw_parse_wpa_ie(p,
- ie_len + 2,
- &group_cipher,
- &pairwise_cipher,
- NULL) == _SUCCESS) {
- psecuritypriv->dot11_auth_algrthm = dot11_auth_algrthm_8021x;
-
- psecuritypriv->dot8021xalg = 1;/* psk, todo:802.1x */
-
- psecuritypriv->wpa_psk |= BIT(0);
-
- psecuritypriv->wpa_group_cipher = group_cipher;
- psecuritypriv->wpa_pairwise_cipher = pairwise_cipher;
- }
+ p = rtw_get_ie_ex(ie + _BEACON_IE_OFFSET_,
+ pbss_network->ie_length - _BEACON_IE_OFFSET_,
+ WLAN_EID_VENDOR_SPECIFIC,
+ OUI1, sizeof(OUI1), NULL, &ie_len);
+ if (p) {
+ if (rtw_parse_wpa_ie(p,
+ ie_len,
+ &group_cipher,
+ &pairwise_cipher,
+ NULL) == _SUCCESS) {
+ psecuritypriv->dot11_auth_algrthm = dot11_auth_algrthm_8021x;
- break;
- }
+ psecuritypriv->dot8021xalg = 1;/* psk, todo:802.1x */
- if (!p || ie_len == 0)
- break;
+ psecuritypriv->wpa_psk |= BIT(0);
+
+ psecuritypriv->wpa_group_cipher = group_cipher;
+ psecuritypriv->wpa_pairwise_cipher = pairwise_cipher;
+ }
}
/* wmm */
ie_len = 0;
pmlmepriv->qospriv.qos_option = 0;
if (pregistrypriv->wmm_enable) {
- for (p = ie + _BEACON_IE_OFFSET_; ; p += (ie_len + 2)) {
- p = rtw_get_ie(p,
- WLAN_EID_VENDOR_SPECIFIC,
- &ie_len,
- (pbss_network->ie_length -
- _BEACON_IE_OFFSET_ - (ie_len + 2)));
- if ((p) && !memcmp(p + 2, WMM_PARA_IE, 6)) {
- pmlmepriv->qospriv.qos_option = 1;
-
- *(p + 8) |= BIT(7);/* QoS Info, support U-APSD */
-
- /* disable all ACM bits since the WMM admission */
- /* control is not supported */
- *(p + 10) &= ~BIT(4); /* BE */
- *(p + 14) &= ~BIT(4); /* BK */
- *(p + 18) &= ~BIT(4); /* VI */
- *(p + 22) &= ~BIT(4); /* VO */
-
- break;
- }
-
- if (!p || ie_len == 0)
- break;
+ p = rtw_get_ie_ex(ie + _BEACON_IE_OFFSET_,
+ pbss_network->ie_length - _BEACON_IE_OFFSET_,
+ WLAN_EID_VENDOR_SPECIFIC,
+ WMM_PARA_IE, sizeof(WMM_PARA_IE), NULL, &ie_len);
+ if (p && ie_len >= WLAN_WMM_LEN + 2) {
+ pmlmepriv->qospriv.qos_option = 1;
+
+ *(p + 8) |= BIT(7);/* QoS Info, support U-APSD */
+
+ /* disable all ACM bits since the WMM admission */
+ /* control is not supported */
+ *(p + 10) &= ~BIT(4); /* BE */
+ *(p + 14) &= ~BIT(4); /* BK */
+ *(p + 18) &= ~BIT(4); /* VI */
+ *(p + 22) &= ~BIT(4); /* VO */
}
}
--
2.43.0