HT: More robust 20/40 coex Action frame parsing

Commit 587d60d2b7 ('Add AP mode support
for HT 20/40 co-ex Action frame') added processing of co-ex report, but
did not include proper bounds checking or IE type checking for the
payload. Furthermore, this was not ready for the possible extensibility
of the 20/40 BSS Coexistence element.

Fix these by checking IE ids for both elements and doing more
apprioriate bounds checking for the element lengths to avoid potentially
reading beyond the frame buffer. Though, the event receive buffer in
both libnl and driver_nl80211_monitor.c is sufficiently large to make it
very unlikely that the maximum read of about 260 bytes beyond the end of
the Action frame would really have any chances of hitting the end of the
memory buffer, so the practical effect of missing bounds checking would
have been possibly accepting an invalid report frame and moving to 20
MHz channel unnecessarily.

Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2014-12-22 21:54:11 +02:00
parent 2a32ad66da
commit 0acc2c809d

View file

@ -211,7 +211,8 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
struct ieee80211_2040_intol_chan_report *ic_report; struct ieee80211_2040_intol_chan_report *ic_report;
int is_ht_allowed = 1; int is_ht_allowed = 1;
int i; int i;
const u8 *data = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1; const u8 *start = (const u8 *) mgmt;
const u8 *data = start + IEEE80211_HDRLEN + 2;
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d", HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d",
@ -220,14 +221,22 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
return; return;
if (len < IEEE80211_HDRLEN + 1) if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie))
return; return;
data++;
bc_ie = (struct ieee80211_2040_bss_coex_ie *) &data[0]; bc_ie = (struct ieee80211_2040_bss_coex_ie *) data;
ic_report = (struct ieee80211_2040_intol_chan_report *) if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE ||
(&data[0] + sizeof(*bc_ie)); bc_ie->length < 1) {
wpa_printf(MSG_DEBUG, "Unexpected IE (%u,%u) in coex report",
bc_ie->element_id, bc_ie->length);
return;
}
if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length)
return;
data += 2 + bc_ie->length;
wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x",
bc_ie->coex_param);
if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) { if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) {
hostapd_logger(hapd, mgmt->sa, hostapd_logger(hapd, mgmt->sa,
HOSTAPD_MODULE_IEEE80211, HOSTAPD_MODULE_IEEE80211,
@ -244,22 +253,34 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
is_ht_allowed = 0; is_ht_allowed = 0;
} }
if (ic_report && if (start + len - data >= 3 &&
(ic_report->element_id == WLAN_EID_20_40_BSS_INTOLERANT)) { data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
u8 ielen = data[1];
if (ielen > start + len - data - 2)
return;
ic_report = (struct ieee80211_2040_intol_chan_report *) data;
wpa_printf(MSG_DEBUG,
"20/40 BSS Intolerant Channel Report: Operating Class %u",
ic_report->op_class);
/* Go through the channel report to find any BSS there in the /* Go through the channel report to find any BSS there in the
* affected channel range */ * affected channel range */
for (i = 0; i < ic_report->length - 1; i++) { for (i = 0; i < ielen - 1; i++) {
if (is_40_allowed(iface, ic_report->variable[i])) u8 chan = ic_report->variable[i];
if (is_40_allowed(iface, chan))
continue; continue;
hostapd_logger(hapd, mgmt->sa, hostapd_logger(hapd, mgmt->sa,
HOSTAPD_MODULE_IEEE80211, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, HOSTAPD_LEVEL_DEBUG,
"20_40_INTOLERANT channel %d reported", "20_40_INTOLERANT channel %d reported",
ic_report->variable[i]); chan);
is_ht_allowed = 0; is_ht_allowed = 0;
break;
} }
} }
wpa_printf(MSG_DEBUG, "is_ht_allowed=%d num_sta_ht40_intolerant=%d",
is_ht_allowed, iface->num_sta_ht40_intolerant);
if (!is_ht_allowed && if (!is_ht_allowed &&
(iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
@ -279,6 +300,9 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
NULL); NULL);
eloop_register_timeout(delay_time, 0, ap_ht2040_timeout, eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
hapd->iface, NULL); hapd->iface, NULL);
wpa_printf(MSG_DEBUG,
"Reschedule HT 20/40 timeout to occur in %u seconds",
delay_time);
} }
} }
} }