aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorInsun Song <insun.song@broadcom.com>2017-03-24 14:04:03 -0700
committerNick Desaulniers <ndesaulniers@google.com>2017-03-24 22:36:37 +0000
commit80ba085a0d135c952fcbbb0868ccccff20d0247b (patch)
tree4647788edb5765c3ffb3ad7f50dc370a23c70bb2
parent93cae63c95cbc507da089b070dd11ba3730483aa (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.c81
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;
}