diff options
author | Insun Song <insun.song@broadcom.com> | 2017-03-24 14:04:03 -0700 |
---|---|---|
committer | Nick Desaulniers <ndesaulniers@google.com> | 2017-03-24 22:36:37 +0000 |
commit | 80ba085a0d135c952fcbbb0868ccccff20d0247b (patch) | |
tree | 4647788edb5765c3ffb3ad7f50dc370a23c70bb2 | |
parent | 93cae63c95cbc507da089b070dd11ba3730483aa (diff) |
net: wireless: bcmdhd: fix for IOVAR GET failedandroid-7.1.1_r0.58
found some case that IOVAR callers set response buffer not enough to
contain input command string + argument. so it finally fail in IOVAR
transaction by its shorter buffer length.
proposed fix is taking care this case by providing enough local
buffer inside dhd_iovar, which enough to input/output.
Signed-off-by: Insun Song <insun.song@broadcom.com>
Bug : 36000515
Change-Id: I0afedcc29b05b12f42ebc619e6feeaa868fc00de
-rw-r--r-- | drivers/net/wireless/bcmdhd/dhd_linux.c | 81 |
1 files changed, 59 insertions, 22 deletions
diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index b14930e4ee3a..14dbb80bca46 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -6131,45 +6131,82 @@ dhd_iovar(dhd_pub_t *pub, int ifidx, char *name, char *param_buf, return BCME_BADARG; input_len = strlen(name) + 1 + param_len; + if (input_len > WLC_IOCTL_MAXLEN) + return BCME_BADARG; + buf = NULL; if (set) { if (res_buf || res_len != 0) { DHD_ERROR(("%s: SET wrong arguemnet\n", __FUNCTION__)); return BCME_BADARG; } - buf = kzalloc(input_len, GFP_ATOMIC); + buf = kzalloc(input_len, GFP_KERNEL); if (!buf) { DHD_ERROR(("%s: mem alloc failed\n", __FUNCTION__)); return BCME_NOMEM; } ret = bcm_mkiovar(name, param_buf, param_len, buf, input_len); + if (!ret) { + ret = BCME_NOMEM; + goto exit; + } + + ioc.cmd = WLC_SET_VAR; + ioc.buf = buf; + ioc.len = input_len; + ioc.set = set; + + ret = dhd_wl_ioctl(pub, ifidx, &ioc, ioc.buf, ioc.len); + } else { - if (!res_buf) { - DHD_ERROR(("%s: GET failed. resp_buf NULL\n", + if (!res_buf || res_len == 0) { + DHD_ERROR(("%s: GET failed. resp_buf NULL or len:0\n", __FUNCTION__)); return BCME_NOMEM; } if (res_len < input_len) { - DHD_ERROR(("%s: res_len(%d) < input_len(%d)\n", - __FUNCTION__, res_len, input_len)); - return BCME_NOMEM; - } - memset(res_buf, 0, res_len); - ret = bcm_mkiovar(name, param_buf, param_len, res_buf, res_len); - } - if (ret == 0) { - if (set) - kfree(buf); - return BCME_NOMEM; - } + DHD_INFO(("%s: res_len(%d) < input_len(%d)\n", + __FUNCTION__, res_len, input_len)); + buf = kzalloc(input_len, GFP_KERNEL); + if (!buf) { + DHD_ERROR(("%s: mem alloc failed\n", + __FUNCTION__)); + return BCME_NOMEM; + } + ret = bcm_mkiovar(name, param_buf, param_len, buf, + input_len); + if (!ret) { + ret = BCME_NOMEM; + goto exit; + } - ioc.cmd = set ? WLC_SET_VAR : WLC_GET_VAR; - ioc.buf = set ? buf : res_buf; - ioc.len = set ? ret : res_len; - ioc.set = set; + ioc.cmd = WLC_GET_VAR; + ioc.buf = buf; + ioc.len = input_len; + ioc.set = set; - ret = dhd_wl_ioctl(pub, ifidx, &ioc, ioc.buf, ioc.len); - if (set) - kfree(buf); + ret = dhd_wl_ioctl(pub, ifidx, &ioc, ioc.buf, ioc.len); + + if (ret == BCME_OK) + memcpy(res_buf, buf, res_len); + } else { + memset(res_buf, 0, res_len); + ret = bcm_mkiovar(name, param_buf, param_len, res_buf, + res_len); + if (!ret) { + ret = BCME_NOMEM; + goto exit; + } + + ioc.cmd = WLC_GET_VAR; + ioc.buf = res_buf; + ioc.len = res_len; + ioc.set = set; + + ret = dhd_wl_ioctl(pub, ifidx, &ioc, ioc.buf, ioc.len); + } + } +exit: + kfree(buf); return ret; } |