diff options
author | Insun Song <insun.song@broadcom.com> | 2016-11-08 11:19:43 -0800 |
---|---|---|
committer | Pat Tjin <pattjin@google.com> | 2016-11-17 19:17:52 +0000 |
commit | 9fe09cec7171521cdfb472635a1d6099169d5944 (patch) | |
tree | 602d6be8acccdbaaec307a5403eff1db75dc0798 | |
parent | 642ae8a410d8725a9bc53d0b55f773b3c5e5aba3 (diff) | |
download | x86_64-9fe09cec7171521cdfb472635a1d6099169d5944.tar.gz |
net: wireless: bcmdhd: fix overrun in dhd_pno_set_cfg_gscan
1. added limit check for GSCAN-PNO max channel bucket
2. added length check in each NL TLV parsing and error handling
Signed-off-by: Insun Song <insun.song@broadcom.com>
Bug: 32174590
Change-Id: Ic946bfa3b3ab6b2b201043371c27ee7dbedb7e75
-rw-r--r-- | drivers/net/wireless/bcmdhd/wl_cfgvendor.c | 201 |
1 files changed, 141 insertions, 60 deletions
diff --git a/drivers/net/wireless/bcmdhd/wl_cfgvendor.c b/drivers/net/wireless/bcmdhd/wl_cfgvendor.c index babadce6067c..c443ee14c951 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgvendor.c +++ b/drivers/net/wireless/bcmdhd/wl_cfgvendor.c @@ -498,23 +498,113 @@ static int wl_cfgvendor_enable_full_scan_result(struct wiphy *wiphy, return err; } -static int wl_cfgvendor_set_scan_cfg(struct wiphy *wiphy, - struct wireless_dev *wdev, const void *data, int len) +static int +wl_cfgvendor_set_scan_cfg_bucket(const struct nlattr *prev, + gscan_scan_params_t *scan_param, int num) +{ + struct dhd_pno_gscan_channel_bucket *ch_bucket; + int k = 0; + int type, err = 0, rem; + const struct nlattr *cur, *next; + + nla_for_each_nested(cur, prev, rem) { + type = nla_type(cur); + ch_bucket = scan_param->channel_bucket; + switch (type) { + case GSCAN_ATTRIBUTE_BUCKET_ID: + break; + case GSCAN_ATTRIBUTE_BUCKET_PERIOD: + if (nla_len(cur) != sizeof(uint32)) { + err = -EINVAL; + goto exit; + } + + ch_bucket[num].bucket_freq_multiple = + nla_get_u32(cur) / MSEC_PER_SEC; + break; + case GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS: + if (nla_len(cur) != sizeof(uint32)) { + err = -EINVAL; + goto exit; + } + ch_bucket[num].num_channels = nla_get_u32(cur); + if (ch_bucket[num].num_channels > + GSCAN_MAX_CHANNELS_IN_BUCKET) { + WL_DBG(("channel range:%d,bucket:%d\n", + ch_bucket[num].num_channels, + num)); + err = -EINVAL; + goto exit; + } + break; + case GSCAN_ATTRIBUTE_BUCKET_CHANNELS: + nla_for_each_nested(next, cur, rem) { + if (k >= GSCAN_MAX_CHANNELS_IN_BUCKET) + break; + if (nla_len(next) != sizeof(uint32)) { + err = -EINVAL; + goto exit; + } + ch_bucket[num].chan_list[k] = nla_get_u32(next); + k++; + } + break; + case GSCAN_ATTRIBUTE_BUCKETS_BAND: + if (nla_len(cur) != sizeof(uint32)) { + err = -EINVAL; + goto exit; + } + ch_bucket[num].band = (uint16)nla_get_u32(cur); + break; + case GSCAN_ATTRIBUTE_REPORT_EVENTS: + if (nla_len(cur) != sizeof(uint32)) { + err = -EINVAL; + goto exit; + } + ch_bucket[num].report_flag = (uint8)nla_get_u32(cur); + break; + case GSCAN_ATTRIBUTE_BUCKET_STEP_COUNT: + if (nla_len(cur) != sizeof(uint32)) { + err = -EINVAL; + goto exit; + } + ch_bucket[num].repeat = (uint16)nla_get_u32(cur); + break; + case GSCAN_ATTRIBUTE_BUCKET_MAX_PERIOD: + if (nla_len(cur) != sizeof(uint32)) { + err = -EINVAL; + goto exit; + } + ch_bucket[num].bucket_max_multiple = + nla_get_u32(cur) / MSEC_PER_SEC; + break; + default: + WL_DBG(("unknown attr type:%d\n", type)); + err = -EINVAL; + goto exit; + } + } + +exit: + return err; +} + +static int +wl_cfgvendor_set_scan_cfg(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int len) { int err = 0; struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); gscan_scan_params_t *scan_param; int j = 0; - int type, tmp, tmp1, tmp2, k = 0; - const struct nlattr *iter, *iter1, *iter2; - struct dhd_pno_gscan_channel_bucket *ch_bucket; + int type, tmp; + const struct nlattr *iter; scan_param = kzalloc(sizeof(gscan_scan_params_t), GFP_KERNEL); if (!scan_param) { WL_ERR(("Could not set GSCAN scan cfg, mem alloc failure\n")); err = -EINVAL; return err; - } scan_param->scan_fr = PNO_SCAN_MIN_FW_SEC; @@ -525,69 +615,60 @@ static int wl_cfgvendor_set_scan_cfg(struct wiphy *wiphy, break; switch (type) { - case GSCAN_ATTRIBUTE_BASE_PERIOD: - scan_param->scan_fr = nla_get_u32(iter)/1000; - break; - case GSCAN_ATTRIBUTE_NUM_BUCKETS: - scan_param->nchannel_buckets = nla_get_u32(iter); - break; - case GSCAN_ATTRIBUTE_CH_BUCKET_1: - case GSCAN_ATTRIBUTE_CH_BUCKET_2: - case GSCAN_ATTRIBUTE_CH_BUCKET_3: - case GSCAN_ATTRIBUTE_CH_BUCKET_4: - case GSCAN_ATTRIBUTE_CH_BUCKET_5: - case GSCAN_ATTRIBUTE_CH_BUCKET_6: - case GSCAN_ATTRIBUTE_CH_BUCKET_7: - nla_for_each_nested(iter1, iter, tmp1) { - type = nla_type(iter1); - ch_bucket = - scan_param->channel_bucket; + case GSCAN_ATTRIBUTE_BASE_PERIOD: + if (nla_len(iter) != sizeof(uint32)) { + err = -EINVAL; + goto exit; + } + scan_param->scan_fr = nla_get_u32(iter) / MSEC_PER_SEC; + break; + case GSCAN_ATTRIBUTE_NUM_BUCKETS: + if (nla_len(iter) != sizeof(uint32)) { + err = -EINVAL; + goto exit; + } + scan_param->nchannel_buckets = nla_get_u32(iter); + if (scan_param->nchannel_buckets >= + GSCAN_MAX_CH_BUCKETS) { + WL_DBG(("ncha_buck out of range %d\n", + scan_param->nchannel_buckets)); + err = -EINVAL; + goto exit; + } + break; + case GSCAN_ATTRIBUTE_CH_BUCKET_1: + case GSCAN_ATTRIBUTE_CH_BUCKET_2: + case GSCAN_ATTRIBUTE_CH_BUCKET_3: + case GSCAN_ATTRIBUTE_CH_BUCKET_4: + case GSCAN_ATTRIBUTE_CH_BUCKET_5: + case GSCAN_ATTRIBUTE_CH_BUCKET_6: + case GSCAN_ATTRIBUTE_CH_BUCKET_7: + err = wl_cfgvendor_set_scan_cfg_bucket(iter, + scan_param, j); + if (err < 0) { + WL_DBG(("set_scan_cfg_buck error:%d\n", err)); + goto exit; + } - switch (type) { - case GSCAN_ATTRIBUTE_BUCKET_ID: - break; - case GSCAN_ATTRIBUTE_BUCKET_PERIOD: - ch_bucket[j].bucket_freq_multiple = - nla_get_u32(iter1)/1000; - break; - case GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS: - ch_bucket[j].num_channels = - nla_get_u32(iter1); - break; - case GSCAN_ATTRIBUTE_BUCKET_CHANNELS: - nla_for_each_nested(iter2, iter1, tmp2) { - if (k >= PFN_SWC_RSSI_WINDOW_MAX) - break; - ch_bucket[j].chan_list[k] = - nla_get_u32(iter2); - k++; - } - k = 0; - break; - case GSCAN_ATTRIBUTE_BUCKETS_BAND: - ch_bucket[j].band = (uint16) - nla_get_u32(iter1); - break; - case GSCAN_ATTRIBUTE_REPORT_EVENTS: - ch_bucket[j].report_flag = (uint8) - nla_get_u32(iter1); - break; - } - } - j++; - break; + j++; + break; + default: + WL_DBG(("Unknown attr type %d\n", type)); + err = -EINVAL; + goto exit; } } - if (dhd_dev_pno_set_cfg_gscan(bcmcfg_to_prmry_ndev(cfg), - DHD_PNO_SCAN_CFG_ID, scan_param, 0) < 0) { - WL_ERR(("Could not set GSCAN scan cfg\n")); + err = dhd_dev_pno_set_cfg_gscan(bcmcfg_to_prmry_ndev(cfg), + DHD_PNO_SCAN_CFG_ID, scan_param, 0); + if (err < 0) { + WL_ERR(("Could not set GSCAN scan cfg error %d\n", err)); err = -EINVAL; } +exit: kfree(scan_param); return err; - } static int wl_cfgvendor_hotlist_cfg(struct wiphy *wiphy, |