From 4e38c573e81eb76f09bae425f035be392fbab370 Mon Sep 17 00:00:00 2001 From: Insun Song Date: Fri, 24 Mar 2017 14:04:03 -0700 Subject: [PATCH] net: wireless: bcmdhd: fix for IOVAR GET failed 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 Bug: 36000515 Change-Id: I0afedcc29b05b12f42ebc619e6feeaa868fc00de --- drivers/net/wireless/bcmdhd/dhd_linux.c | 81 ++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 17e503c6d6b35..0b66e914c15a0 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -6257,45 +6257,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; }