From 8f1621cd0d0ca0bc494a926a1331f582b27b913e Mon Sep 17 00:00:00 2001 From: Insun Song Date: Thu, 3 Nov 2016 10:53:51 -0700 Subject: [PATCH] net: wireless: bcmdhd: fix buffer overrun in wl_cfgvendor_hotlist_cfg fix buffer overrun found where user manipulated input parameters 1. allocate local buffer with max length than input sized. 2. length check added in each tlv parsing and added error handling. 3. limit max hotlist count to PFN_SW_MAX_NUM_APS(16). bug=32474971 Signed-off-by: Insun Song Change-Id: I60d513a30875f6a8ee8cfdc557bdec1436416fe7 --- drivers/net/wireless/bcmdhd/wl_cfgvendor.c | 107 +++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 29 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/wl_cfgvendor.c b/drivers/net/wireless/bcmdhd/wl_cfgvendor.c index eb83c8339e471..d578026885619 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgvendor.c +++ b/drivers/net/wireless/bcmdhd/wl_cfgvendor.c @@ -691,14 +691,22 @@ static int wl_cfgvendor_hotlist_cfg(struct wiphy *wiphy, struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); gscan_hotlist_scan_params_t *hotlist_params; int tmp, tmp1, tmp2, type, j = 0, dummy; - const struct nlattr *outer, *inner, *iter; - bool flush = FALSE; + const struct nlattr *outer, *inner = NULL, *iter; + uint8 flush = 0; struct bssid_t *pbssid; - hotlist_params = (gscan_hotlist_scan_params_t *)kzalloc(len, GFP_KERNEL); + if (len < sizeof(*hotlist_params) || len >= WLC_IOCTL_MAXLEN) { + WL_ERR(("buffer length :%d wrong - bail out.\n", len)); + return -EINVAL; + } + + hotlist_params = kzalloc(sizeof(*hotlist_params) + + (sizeof(struct bssid_t) * (PFN_SWC_MAX_NUM_APS - 1)), + GFP_KERNEL); + if (!hotlist_params) { WL_ERR(("Cannot Malloc mem to parse config commands size - %d bytes \n", len)); - return -1; + return -ENOMEM; } hotlist_params->lost_ap_window = GSCAN_LOST_AP_WINDOW_DEFAULT; @@ -706,37 +714,78 @@ static int wl_cfgvendor_hotlist_cfg(struct wiphy *wiphy, nla_for_each_attr(iter, data, len, tmp2) { type = nla_type(iter); switch (type) { - case GSCAN_ATTRIBUTE_HOTLIST_BSSIDS: - pbssid = hotlist_params->bssid; - nla_for_each_nested(outer, iter, tmp) { - nla_for_each_nested(inner, outer, tmp1) { - type = nla_type(inner); + case GSCAN_ATTRIBUTE_HOTLIST_BSSIDS: + pbssid = hotlist_params->bssid; + nla_for_each_nested(outer, iter, tmp) { + nla_for_each_nested(inner, outer, tmp1) { + type = nla_type(inner); - switch (type) { - case GSCAN_ATTRIBUTE_BSSID: - memcpy(&(pbssid[j].macaddr), - nla_data(inner), ETHER_ADDR_LEN); - break; - case GSCAN_ATTRIBUTE_RSSI_LOW: - pbssid[j].rssi_reporting_threshold = - (int8) nla_get_u8(inner); - break; - case GSCAN_ATTRIBUTE_RSSI_HIGH: - dummy = (int8) nla_get_u8(inner); - break; + switch (type) { + case GSCAN_ATTRIBUTE_BSSID: + if (nla_len(inner) != sizeof(pbssid[j].macaddr)) { + WL_DBG(("type:%d length:%d not matching.\n", + type, nla_len(inner))); + err = -EINVAL; + goto exit; } + memcpy( + &pbssid[j].macaddr, + nla_data(inner), + sizeof(pbssid[j].macaddr)); + break; + case GSCAN_ATTRIBUTE_RSSI_LOW: + if (nla_len(inner) != sizeof(uint8)) { + WL_DBG(("type:%d length:%d not matching.\n", + type, nla_len(inner))); + err = -EINVAL; + goto exit; + } + pbssid[j].rssi_reporting_threshold = + (int8)nla_get_u8(inner); + break; + case GSCAN_ATTRIBUTE_RSSI_HIGH: + if (nla_len(inner) != sizeof(uint8)) { + WL_DBG(("type:%d length:%d not matching.\n", + type, nla_len(inner))); + err = -EINVAL; + goto exit; + } + dummy = (int8)nla_get_u8(inner); + break; } - j++; + } + if (++j > PFN_SWC_MAX_NUM_APS) { + WL_DBG(("nbssid:%d exeed limit.\n", + hotlist_params->nbssid)); + err = -EINVAL; + goto exit; } hotlist_params->nbssid = j; - break; - case GSCAN_ATTRIBUTE_HOTLIST_FLUSH: - flush = (bool) nla_get_u8(iter); - break; - case GSCAN_ATTRIBUTE_LOST_AP_SAMPLE_SIZE: - hotlist_params->lost_ap_window = nla_get_u32(iter); - break; } + break; + case GSCAN_ATTRIBUTE_HOTLIST_FLUSH: + if (nla_len(iter) != sizeof(uint8)) { + WL_DBG(("type:%d length:%d not matching.\n", + type, nla_len(inner))); + err = -EINVAL; + goto exit; + } + flush = nla_get_u8(iter); + break; + case GSCAN_ATTRIBUTE_LOST_AP_SAMPLE_SIZE: + if (nla_len(iter) != sizeof(uint32)) { + WL_DBG(("type:%d length:%d not matching.\n", + type, nla_len(inner))); + err = -EINVAL; + goto exit; + } + hotlist_params->lost_ap_window = (uint16)nla_get_u32(iter); + break; + default: + WL_DBG(("Unknown type %d\n", type)); + err = -EINVAL; + goto exit; + } }