From 5575ff40a53a954ec942ff0c17b193433e72c132 Mon Sep 17 00:00:00 2001 From: Sudhir Kohalli Date: Wed, 14 Jun 2017 11:36:22 -0700 Subject: net: wireless: bcmdhd: add boundary check in GSCAN full result handler validtating each length fields before not to overflow allocated data type. it prevent possiblity heap memory corrupted. Signed-off-by: Sudhir Kohalli Bug: 37357704 Change-Id: I7c04b93f3843c8100bd932fb9b7c67ef76b93050 --- drivers/net/wireless/bcmdhd/dhd_linux.c | 5 +++-- drivers/net/wireless/bcmdhd/dhd_pno.c | 37 ++++++++++++++++++++++++------- drivers/net/wireless/bcmdhd/dhd_pno.h | 7 +++--- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 7 ++++-- 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 0b66e91..abc4331 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -8420,11 +8420,12 @@ void * dhd_dev_hotlist_scan_event(struct net_device *dev, /* Linux wrapper to call common dhd_process_full_gscan_result */ void * dhd_dev_process_full_gscan_result(struct net_device *dev, -const void *data, int *send_evt_bytes) +const void *data, uint32 len, int *send_evt_bytes) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); - return (dhd_process_full_gscan_result(&dhd->pub, data, send_evt_bytes)); + return dhd_process_full_gscan_result(&dhd->pub, data, len, + send_evt_bytes); } void dhd_dev_gscan_hotlist_cache_cleanup(struct net_device *dev, hotlist_type_t type) diff --git a/drivers/net/wireless/bcmdhd/dhd_pno.c b/drivers/net/wireless/bcmdhd/dhd_pno.c index c80adec..3103f89 100644 --- a/drivers/net/wireless/bcmdhd/dhd_pno.c +++ b/drivers/net/wireless/bcmdhd/dhd_pno.c @@ -47,6 +47,7 @@ #ifdef GSCAN_SUPPORT #include #endif /* GSCAN_SUPPORT */ +#include #ifdef __BIG_ENDIAN #include @@ -3693,7 +3694,8 @@ void dhd_gscan_hotlist_cache_cleanup(dhd_pub_t *dhd, hotlist_type_t type) } void * -dhd_process_full_gscan_result(dhd_pub_t *dhd, const void *data, int *size) +dhd_process_full_gscan_result(dhd_pub_t *dhd, const void *data, uint32 len, + int *size) { wl_bss_info_t *bi = NULL; wl_gscan_result_t *gscan_result; @@ -3702,15 +3704,25 @@ dhd_process_full_gscan_result(dhd_pub_t *dhd, const void *data, int *size) uint8 channel; uint32 mem_needed; struct timespec ts; + u32 bi_ie_length = 0; + u32 bi_ie_offset = 0; *size = 0; - gscan_result = (wl_gscan_result_t *)data; - if (!gscan_result) { DHD_ERROR(("Invalid gscan result (NULL pointer)\n")); goto exit; } + + if ((len < sizeof(*gscan_result)) || + (len < dtoh32(gscan_result->buflen)) || + (dtoh32(gscan_result->buflen) > + (sizeof(*gscan_result) + WL_SCAN_IE_LEN_MAX))) { + DHD_ERROR(("%s: invalid gscan buflen:%u\n", __func__, + dtoh32(gscan_result->buflen))); + goto exit; + } + if (!gscan_result->bss_info) { DHD_ERROR(("Invalid gscan bss info (NULL pointer)\n")); goto exit; @@ -3722,12 +3734,21 @@ dhd_process_full_gscan_result(dhd_pub_t *dhd, const void *data, int *size) DHD_ERROR(("Invalid bss_info length %d: ignoring\n", bi_length)); goto exit; } + + bi_ie_offset = dtoh32(bi->ie_offset); + bi_ie_length = dtoh32(bi->ie_length); + if ((bi_ie_offset + bi_ie_length) > bi_length) { + DHD_ERROR(("%s: Invalid ie_length:%u or ie_offset:%u\n", + __func__, bi_ie_length, bi_ie_offset)); + goto exit; + } if (bi->SSID_len > DOT11_MAX_SSID_LEN) { - DHD_ERROR(("Invalid SSID length %d: trimming it to max\n", bi->SSID_len)); - bi->SSID_len = DOT11_MAX_SSID_LEN; + DHD_ERROR(("%s: Invalid SSID length %u\n", + __func__, bi->SSID_len)); + goto exit; } - mem_needed = OFFSETOF(wifi_gscan_full_result_t, ie_data) + bi->ie_length; + mem_needed = OFFSETOF(wifi_gscan_full_result_t, ie_data) + bi_ie_length; result = (wifi_gscan_full_result_t *) kmalloc(mem_needed, GFP_KERNEL); if (!result) { @@ -3749,9 +3770,9 @@ dhd_process_full_gscan_result(dhd_pub_t *dhd, const void *data, int *size) result->fixed.ts = (uint64) TIMESPEC_TO_US(ts); result->fixed.beacon_period = dtoh16(bi->beacon_period); result->fixed.capability = dtoh16(bi->capability); - result->ie_length = dtoh32(bi->ie_length); + result->ie_length = bi_ie_length; memcpy(&result->fixed.macaddr, &bi->BSSID, ETHER_ADDR_LEN); - memcpy(result->ie_data, ((uint8 *)bi + bi->ie_offset), bi->ie_length); + memcpy(result->ie_data, ((uint8 *)bi + bi_ie_offset), bi_ie_length); *size = mem_needed; exit: return result; diff --git a/drivers/net/wireless/bcmdhd/dhd_pno.h b/drivers/net/wireless/bcmdhd/dhd_pno.h index a0edf54..3398752 100644 --- a/drivers/net/wireless/bcmdhd/dhd_pno.h +++ b/drivers/net/wireless/bcmdhd/dhd_pno.h @@ -512,7 +512,7 @@ int dhd_retreive_batch_scan_results(dhd_pub_t *dhd); extern void * dhd_dev_hotlist_scan_event(struct net_device *dev, const void *data, int *send_evt_bytes, hotlist_type_t type); void * dhd_dev_process_full_gscan_result(struct net_device *dev, - const void *data, int *send_evt_bytes); + const void *data, uint32 len, int *send_evt_bytes); extern int dhd_dev_gscan_batch_cache_cleanup(struct net_device *dev); extern void dhd_dev_gscan_hotlist_cache_cleanup(struct net_device *dev, hotlist_type_t type); extern int dhd_dev_wait_batch_results_complete(struct net_device *dev); @@ -563,8 +563,9 @@ extern int dhd_dev_retrieve_batch_scan(struct net_device *dev); extern void *dhd_handle_swc_evt(dhd_pub_t *dhd, const void *event_data, int *send_evt_bytes); extern void *dhd_handle_hotlist_scan_evt(dhd_pub_t *dhd, const void *event_data, int *send_evt_bytes, hotlist_type_t type); -extern void *dhd_process_full_gscan_result(dhd_pub_t *dhd, const void *event_data, - int *send_evt_bytes); +extern void * +dhd_process_full_gscan_result(dhd_pub_t *dhd, const void *event_data, + uint32 len, int *send_evt_bytes); extern int dhd_gscan_batch_cache_cleanup(dhd_pub_t *dhd); 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); diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index d8c748d..1d1e2a8 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -9452,10 +9452,13 @@ wl_notify_gscan_event(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, err = -EINVAL; break; case WLC_E_PFN_GSCAN_FULL_RESULT: - ptr = dhd_dev_process_full_gscan_result(ndev, data, &send_evt_bytes); + ptr = + dhd_dev_process_full_gscan_result(ndev, data, len, + &send_evt_bytes); if (ptr) { wl_cfgvendor_send_async_event(wiphy, ndev, - GOOGLE_SCAN_FULL_RESULTS_EVENT, ptr, send_evt_bytes); + GOOGLE_SCAN_FULL_RESULTS_EVENT, ptr, + send_evt_bytes); kfree(ptr); } else err = -ENOMEM; -- cgit v1.1