From e5c1b001a822e8b38680655c400e7b3f67cc3323 Mon Sep 17 00:00:00 2001 From: Insun Song Date: Thu, 10 Nov 2016 15:01:31 -0800 Subject: [PATCH] net: wireless: bcmdhd: fix buffer overrun in anqpo config 1. memory leak fix when input packet content corrupted. 2. reduced unnecessary debug messages Signed-off-by: Insun Song Bug: 32219453 Change-Id: I0f79310c97571cd46afff29f58f66b17a2471927 --- drivers/net/wireless/bcmdhd/dhd_linux.c | 2 + drivers/net/wireless/bcmdhd/dhd_pno.c | 3 +- drivers/net/wireless/bcmdhd/dhd_pno.h | 17 ++-- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 14 +++ drivers/net/wireless/bcmdhd/wl_cfgvendor.c | 141 ++++++++++++++++++++--------- 5 files changed, 127 insertions(+), 50 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 2fd2934a7e851..00201de5de5b8 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -8621,6 +8621,7 @@ int dhd_dev_set_whitelist_ssid(struct net_device *dev, wl_ssid_whitelist_t *ssid return err; } +#ifdef DHD_ANQPO_SUPPORT void * dhd_dev_process_anqpo_result(struct net_device *dev, const void *data, uint32 event, int *send_evt_bytes) { @@ -8628,6 +8629,7 @@ void * dhd_dev_process_anqpo_result(struct net_device *dev, return (dhd_pno_process_anqpo_result(&dhd->pub, data, event, send_evt_bytes)); } +#endif /* DHD_ANQPO_SUPPORT */ #endif /* GSCAN_SUPPORT */ int dhd_dev_set_rssi_monitor_cfg(struct net_device *dev, int start, diff --git a/drivers/net/wireless/bcmdhd/dhd_pno.c b/drivers/net/wireless/bcmdhd/dhd_pno.c index 8d6d234cd11b3..a88d1e2e41320 100644 --- a/drivers/net/wireless/bcmdhd/dhd_pno.c +++ b/drivers/net/wireless/bcmdhd/dhd_pno.c @@ -3798,6 +3798,7 @@ dhd_pno_process_epno_result(dhd_pub_t *dhd, const void *data, uint32 event, int return results; } +#ifdef DHD_ANQPO_SUPPORT void * dhd_pno_process_anqpo_result(dhd_pub_t *dhd, const void *data, uint32 event, int *size) { @@ -3849,7 +3850,7 @@ dhd_pno_process_anqpo_result(dhd_pub_t *dhd, const void *data, uint32 event, int return result; } - +#endif /* DHD_ANQPO_SUPPORT */ void *dhd_handle_hotlist_scan_evt(dhd_pub_t *dhd, const void *event_data, int *send_evt_bytes, hotlist_type_t type) diff --git a/drivers/net/wireless/bcmdhd/dhd_pno.h b/drivers/net/wireless/bcmdhd/dhd_pno.h index b61d0fd866364..a0edf54049acf 100644 --- a/drivers/net/wireless/bcmdhd/dhd_pno.h +++ b/drivers/net/wireless/bcmdhd/dhd_pno.h @@ -98,8 +98,9 @@ #define CHANNEL_BUCKET_EMPTY_INDEX 0xFFFF #define GSCAN_RETRY_THRESHOLD 3 -#define MAX_EPNO_SSID_NUM 64 - +#define MAX_EPNO_SSID_NUM 64 +#define GSCAN_ANQPO_MAX_HS_LIST_SIZE 16 +#define ANQPO_MAX_HS_NAI_REALM_SIZE 256 #endif /* GSCAN_SUPPORT */ enum scan_status { @@ -351,10 +352,10 @@ typedef struct gscan_results_cache { } gscan_results_cache_t; typedef struct { - int id; /* identifier of this network block, report this in event */ - char realm[256]; /* null terminated UTF8 encoded realm, 0 if unspecified */ - int64_t roamingConsortiumIds[16]; /* roaming consortium ids to match, 0s if unspecified */ - uint8 plmn[3]; /* mcc/mnc combination as per rules, 0s if unspecified */ + int id; + char realm[ANQPO_MAX_HS_NAI_REALM_SIZE]; + int64_t roamingConsortiumIds[ANQPO_MAX_PFN_HS]; + uint8 plmn[ANQPO_MCC_LENGTH]; } wifi_passpoint_network; typedef struct dhd_pno_gscan_capabilities { @@ -517,8 +518,10 @@ extern void dhd_dev_gscan_hotlist_cache_cleanup(struct net_device *dev, hotlist_ extern int dhd_dev_wait_batch_results_complete(struct net_device *dev); extern void * dhd_dev_process_epno_result(struct net_device *dev, const void *data, uint32 event, int *send_evt_bytes); +#ifdef DHD_ANQPO_SUPPORT extern void * dhd_dev_process_anqpo_result(struct net_device *dev, const void *data, uint32 event, int *send_evt_bytes); +#endif /* DHD_ANQPO_SUPPORT */ extern int dhd_dev_set_epno(struct net_device *dev); extern int dhd_dev_flush_fw_epno(struct net_device *dev); #endif /* GSCAN_SUPPORT */ @@ -567,7 +570,9 @@ extern void dhd_gscan_hotlist_cache_cleanup(dhd_pub_t *dhd, hotlist_type_t type) extern int dhd_wait_batch_results_complete(dhd_pub_t *dhd); extern void * dhd_pno_process_epno_result(dhd_pub_t *dhd, const void *data, uint32 event, int *size); +#ifdef DHD_ANQPO_SUPPORT extern void * dhd_pno_process_anqpo_result(dhd_pub_t *dhd, const void *data, uint32 event, int *size); +#endif /* DHD_ANQPO_SUPPORT */ extern void dhd_pno_translate_epno_fw_flags(uint32 *flags); extern int dhd_pno_set_epno(dhd_pub_t *dhd); extern int dhd_pno_flush_fw_epno(dhd_pub_t *dhd); diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index a56ba6b82e197..3d70a82adfa5e 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -9423,6 +9423,16 @@ wl_notify_gscan_event(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, } else err = -ENOMEM; break; + case WLC_E_PFN_SSID_EXT: + ptr = dhd_dev_process_epno_result(ndev, data, event, &send_evt_bytes); + if (ptr) { + wl_cfgvendor_send_async_event(wiphy, ndev, + GOOGLE_SCAN_EPNO_EVENT, ptr, send_evt_bytes); + kfree(ptr); + } else + err = -ENOMEM; + break; +#ifdef DHD_ANQPO_SUPPORT case WLC_E_PFN_NET_FOUND: ptr = dhd_dev_process_anqpo_result(ndev, data, event, &len); if (ptr) { @@ -9432,6 +9442,7 @@ wl_notify_gscan_event(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, } else err = -ENOMEM; break; +#endif /* DHD_ANQPO_SUPPORT */ default: WL_ERR(("Unknown event %d\n", event)); break; @@ -10035,7 +10046,10 @@ static void wl_init_event_handler(struct bcm_cfg80211 *cfg) cfg->evt_handler[WLC_E_PFN_SWC] = wl_notify_gscan_event; cfg->evt_handler[WLC_E_PFN_BSSID_NET_FOUND] = wl_notify_gscan_event; cfg->evt_handler[WLC_E_PFN_BSSID_NET_LOST] = wl_notify_gscan_event; + cfg->evt_handler[WLC_E_PFN_SSID_EXT] = wl_notify_gscan_event; +#ifdef DHD_ANQPO_SUPPORT cfg->evt_handler[WLC_E_GAS_FRAGMENT_RX] = wl_notify_gscan_event; +#endif /* DHD_ANQPO_SUPPORT */ cfg->evt_handler[WLC_E_ROAM_EXP_EVENT] = wl_handle_roam_exp_event; #endif /* GSCAN_SUPPORT */ cfg->evt_handler[WLC_E_RSSI_LQM] = wl_handle_rssi_monitor_event; diff --git a/drivers/net/wireless/bcmdhd/wl_cfgvendor.c b/drivers/net/wireless/bcmdhd/wl_cfgvendor.c index 5be16a72aa43f..b156660ed053a 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgvendor.c +++ b/drivers/net/wireless/bcmdhd/wl_cfgvendor.c @@ -939,10 +939,13 @@ static int wl_cfgvendor_epno_cfg(struct wiphy *wiphy, return err; } +#ifdef DHD_ANQPO_SUPPORT static int wl_cfgvendor_gscan_anqpo_config(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { - int err = BCME_ERROR, rem, type, hs_list_size = 0, malloc_size, i = 0, j, k, num_oi, oi_len; + int err = BCME_ERROR, rem, type, malloc_size, i = 0; + uint32 hs_list_size = 0; + int j, k, num_oi, oi_len; wifi_passpoint_network *hs_list = NULL, *src_hs; wl_anqpo_pfn_hs_list_t *anqpo_hs_list; wl_anqpo_pfn_hs_t *dst_hs; @@ -953,52 +956,100 @@ static int wl_cfgvendor_gscan_anqpo_config(struct wiphy *wiphy, char *rcid; nla_for_each_attr(iter, data, len, rem) { - type = nla_type(iter); - switch (type) { - case GSCAN_ATTRIBUTE_ANQPO_HS_LIST: - if (hs_list_size > 0) { - hs_list = kmalloc(hs_list_size*sizeof(wifi_passpoint_network), GFP_KERNEL); - if (hs_list == NULL) { - WL_ERR(("failed to allocate hs_list\n")); - return -ENOMEM; - } - } - nla_for_each_nested(outer, iter, tmp) { - nla_for_each_nested(inner, outer, tmp1) { - type = nla_type(inner); + type = nla_type(iter); + switch (type) { + case GSCAN_ATTRIBUTE_ANQPO_HS_LIST: + if (hs_list) { + err = -EINVAL; + goto exit; + } + if (hs_list_size > GSCAN_ANQPO_MAX_HS_LIST_SIZE) { + err = -EINVAL; + goto exit; + } + if (hs_list_size > 0) { + hs_list = kzalloc(hs_list_size * + sizeof(wifi_passpoint_network), GFP_KERNEL); + if (!hs_list) { + WL_ERR(("failed to allocate hs_list\n")); + return -ENOMEM; + } + } + nla_for_each_nested(outer, iter, tmp) { + if (i == hs_list_size) + break; + nla_for_each_nested(inner, outer, tmp1) { + type = nla_type(inner); - switch (type) { - case GSCAN_ATTRIBUTE_ANQPO_HS_NETWORK_ID: - hs_list[i].id = nla_get_u32(inner); - WL_ERR(("%s: net id: %d\n", __func__, hs_list[i].id)); - break; - case GSCAN_ATTRIBUTE_ANQPO_HS_NAI_REALM: - memcpy(hs_list[i].realm, - nla_data(inner), 256); - WL_ERR(("%s: realm: %s\n", __func__, hs_list[i].realm)); - break; - case GSCAN_ATTRIBUTE_ANQPO_HS_ROAM_CONSORTIUM_ID: - memcpy(hs_list[i].roamingConsortiumIds, - nla_data(inner), 128); - break; - case GSCAN_ATTRIBUTE_ANQPO_HS_PLMN: - memcpy(hs_list[i].plmn, - nla_data(inner), 3); - WL_ERR(("%s: plmn: %c %c %c\n", __func__, hs_list[i].plmn[0], hs_list[i].plmn[1], hs_list[i].plmn[2])); - break; - } - } - i++; + switch (type) { + case GSCAN_ATTRIBUTE_ANQPO_HS_NETWORK_ID: + if (nla_len(inner) != sizeof(hs_list[i].id)) { + err = -EINVAL; + goto exit; } + hs_list[i].id = nla_get_u32(inner); + WL_DBG(("%s: net id: %d\n", + __func__, hs_list[i].id)); break; - case GSCAN_ATTRIBUTE_ANQPO_HS_LIST_SIZE: - hs_list_size = nla_get_u32(iter); - WL_ERR(("%s: ANQPO: %d\n", __func__, hs_list_size)); + case GSCAN_ATTRIBUTE_ANQPO_HS_NAI_REALM: + if (nla_len(inner) != + sizeof(hs_list[i].realm)) { + err = -EINVAL; + goto exit; + } + memcpy(hs_list[i].realm, nla_data(inner), + sizeof(hs_list[i].realm)); + WL_DBG(("%s: realm: %s\n", + __func__, hs_list[i].realm)); break; - default: - WL_ERR(("Unknown type: %d\n", type)); - return err; + case GSCAN_ATTRIBUTE_ANQPO_HS_ROAM_CONSORTIUM_ID: + if (nla_len(inner) != sizeof(hs_list[i]. + roamingConsortiumIds)) { + err = -EINVAL; + goto exit; + } + memcpy(hs_list[i].roamingConsortiumIds, + nla_data(inner), + sizeof(hs_list[i].roamingConsortiumIds)); + break; + case GSCAN_ATTRIBUTE_ANQPO_HS_PLMN: + if (nla_len(inner) != sizeof(hs_list[i].plmn)) { + err = -EINVAL; + goto exit; + } + memcpy(hs_list[i].plmn, + nla_data(inner), + sizeof(hs_list[i].plmn)); + WL_DBG(("%s: plmn: %c %c %c\n", + __func__, hs_list[i].plmn[0], + hs_list[i].plmn[1], + hs_list[i].plmn[2])); + break; + } + } + i++; } + break; + case GSCAN_ATTRIBUTE_ANQPO_HS_LIST_SIZE: + if (nla_len(iter) != sizeof(hs_list_size)) { + err = -EINVAL; + goto exit; + } + hs_list_size = nla_get_u32(iter); + if ((hs_list_size == 0) || + (hs_list_size > GSCAN_ANQPO_MAX_HS_LIST_SIZE)) { + WL_ERR(("%s: ANQPO: %d\n", __func__, hs_list_size)); + err = -EINVAL; + goto exit; + } + WL_DBG(("%s: ANQPO: %d\n", __func__, hs_list_size)); + break; + default: + WL_ERR(("Unknown type: %d\n", type)); + err = -EINVAL; + goto exit; + } + } malloc_size = OFFSETOF(wl_anqpo_pfn_hs_list_t, hs) + @@ -1046,7 +1097,7 @@ static int wl_cfgvendor_gscan_anqpo_config(struct wiphy *wiphy, kfree(hs_list); return err; } - +#endif /* DHD_ANQPO_SUPPORT */ static int wl_cfgvendor_set_batch_scan_cfg(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { @@ -3065,6 +3116,7 @@ static const struct wiphy_vendor_command wl_vendor_cmds [] = { .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = wl_cfgvendor_set_bssid_blacklist }, +#ifdef DHD_ANQPO_SUPPORT { { .vendor_id = OUI_GOOGLE, @@ -3073,6 +3125,7 @@ static const struct wiphy_vendor_command wl_vendor_cmds [] = { .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = wl_cfgvendor_gscan_anqpo_config }, +#endif /* DHD_ANQPO_SUPPORT */ #endif /* GSCAN_SUPPORT */ { { @@ -3233,7 +3286,9 @@ static const struct nl80211_vendor_cmd_info wl_vendor_events [] = { { OUI_GOOGLE, GOOGLE_SCAN_EPNO_EVENT }, { OUI_GOOGLE, GOOGLE_DEBUG_RING_EVENT }, { OUI_GOOGLE, GOOGLE_FW_DUMP_EVENT }, +#ifdef DHD_ANQPO_SUPPORT { OUI_GOOGLE, GOOGLE_PNO_HOTSPOT_FOUND_EVENT }, +#endif /* DHD_ANQPO_SUPPORT */ { OUI_GOOGLE, GOOGLE_RSSI_MONITOR_EVENT }, { OUI_GOOGLE, GOOGLE_MKEEP_ALIVE_EVENT } };