From 3b2abe930d267fc09e9e4c9dceee56784d0f8532 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Fri, 15 Dec 2017 09:03:47 -0800 Subject: [PATCH] qcacld-3.0: Validate Scan Default IEs Currently the Scan Default IEs configured via the attribute QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES are not validated. As a result a buffer overread can occur when the Scan Default IEs are later referenced. To address this issue validate the Scan Default IEs before storing them. Change-Id: Ifd8739c96a9990f01ff159eb59a7e904f7b8c592 CRs-Fixed: 2154346 --- core/hdd/src/wlan_hdd_cfg80211.c | 103 +++++++++++++++++++++++-------- core/hdd/src/wlan_hdd_cfg80211.h | 15 +++++ 2 files changed, 92 insertions(+), 26 deletions(-) diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c index dee992d3e994..cb087a1d068d 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.c +++ b/core/hdd/src/wlan_hdd_cfg80211.c @@ -660,6 +660,27 @@ static const struct wiphy_wowlan_support wowlan_support_cfg80211_init = { }; #endif +bool hdd_is_ie_valid(const uint8_t *ie, size_t ie_len) +{ + uint8_t elen; + + while (ie_len) { + if (ie_len < 2) + return false; + + elen = ie[1]; + ie_len -= 2; + ie += 2; + if (elen > ie_len) + return false; + + ie_len -= elen; + ie += elen; + } + + return true; +} + /** * hdd_add_channel_switch_support()- Adds Channel Switch flag if supported * @flags: Pointer to the flags to Add channel switch flag. @@ -6142,6 +6163,56 @@ static int wlan_hdd_cfg80211_wifi_set_rx_blocksize(struct hdd_context *hdd_ctx, return ret_val; } +static int hdd_config_scan_default_ies(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t *scan_ie; + uint16_t scan_ie_len; + QDF_STATUS status; + + if (!attr) + return 0; + + scan_ie_len = nla_len(attr); + hdd_debug("IE len %d session %d device mode %d", + scan_ie_len, adapter->session_id, adapter->device_mode); + + if (!scan_ie_len) { + hdd_err("zero-length IE prohibited"); + return -EINVAL; + } + + if (scan_ie_len > MAX_DEFAULT_SCAN_IE_LEN) { + hdd_err("IE length %d exceeds max of %d", + scan_ie_len, MAX_DEFAULT_SCAN_IE_LEN); + return -EINVAL; + } + + scan_ie = nla_data(attr); + if (!hdd_is_ie_valid(scan_ie, scan_ie_len)) { + hdd_err("Invalid default scan IEs"); + return -EINVAL; + } + + if (wlan_hdd_save_default_scan_ies(hdd_ctx, adapter, + scan_ie, scan_ie_len)) + hdd_err("Failed to save default scan IEs"); + + if (adapter->device_mode == QDF_STA_MODE) { + status = sme_set_default_scan_ie(hdd_ctx->hHal, + adapter->session_id, scan_ie, + scan_ie_len); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("failed to set default scan IEs in sme: %d", + status); + return -EPERM; + } + } + + return 0; +} + /** * __wlan_hdd_cfg80211_wifi_configuration_set() - Wifi configuration * vendor command @@ -6165,6 +6236,8 @@ __wlan_hdd_cfg80211_wifi_configuration_set(struct wiphy *wiphy, struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); struct hdd_context *hdd_ctx = wiphy_priv(wiphy); struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + 1]; + const struct nlattr *attr; + int ret; int ret_val = 0; u32 modulated_dtim, override_li; u16 stats_avg_factor; @@ -6177,8 +6250,6 @@ __wlan_hdd_cfg80211_wifi_configuration_set(struct wiphy *wiphy, int access_policy = 0; char vendor_ie[SIR_MAC_MAX_IE_LENGTH + 2]; bool vendor_ie_present = false, access_policy_present = false; - uint16_t scan_ie_len = 0; - uint8_t *scan_ie; struct sir_set_tx_rx_aggregation_size request; QDF_STATUS qdf_status; uint8_t retry, delay, enable_flag; @@ -6421,30 +6492,10 @@ __wlan_hdd_cfg80211_wifi_configuration_set(struct wiphy *wiphy, ret_val = hdd_enable_disable_ca_event(hdd_ctx, set_value); } - if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES]) { - scan_ie_len = nla_len( - tb[QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES]); - hdd_debug("Received default scan IE of len %d session %d device mode %d", - scan_ie_len, adapter->session_id, - adapter->device_mode); - if (scan_ie_len && (scan_ie_len <= MAX_DEFAULT_SCAN_IE_LEN)) { - scan_ie = (uint8_t *) nla_data(tb - [QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES]); - - if (wlan_hdd_save_default_scan_ies(hdd_ctx, adapter, - scan_ie, scan_ie_len)) - hdd_err("Failed to save default scan IEs"); - - if (adapter->device_mode == QDF_STA_MODE) { - status = sme_set_default_scan_ie(hdd_ctx->hHal, - adapter->session_id, scan_ie, - scan_ie_len); - if (QDF_STATUS_SUCCESS != status) - ret_val = -EPERM; - } - } else - ret_val = -EPERM; - } + attr = tb[QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES]; + ret = hdd_config_scan_default_ies(adapter, attr); + if (ret) + ret_val = ret; if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION] || tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION]) { diff --git a/core/hdd/src/wlan_hdd_cfg80211.h b/core/hdd/src/wlan_hdd_cfg80211.h index 1a6e5db41a75..bea34786278e 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.h +++ b/core/hdd/src/wlan_hdd_cfg80211.h @@ -216,6 +216,21 @@ typedef enum { #define CFG_PROPAGATION_DELAY_BASE (64) #define CFG_AGG_RETRY_MIN (5) +/** + * hdd_is_ie_valid() - Determine if an IE sequence is valid + * @ie: Pointer to the IE buffer + * @ie_len: Length of the IE buffer @ie + * + * This function validates that the IE sequence is valid by verifying + * that the sum of the lengths of the embedded elements match the + * length of the sequence. + * + * Note well that a 0-length IE sequence is considered valid. + * + * Return: true if the IE sequence is valid, false if it is invalid + */ +bool hdd_is_ie_valid(const uint8_t *ie, size_t ie_len); + struct cfg80211_bss * wlan_hdd_cfg80211_update_bss_db(struct hdd_adapter *adapter, struct csr_roam_info *roam_info);