summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorInsun Song <insun.song@broadcom.com>2016-10-25 13:33:18 -0700
committerPat Tjin <pattjin@google.com>2016-11-17 19:19:00 +0000
commit55e73d20f66202aa9765ab7b3fc834b178ee5899 (patch)
tree023383715de7c93d0c56f9c8b023b4cffe1dcb88
parent9903a7d3d5723c8742938e47b7f7cc27811fbed5 (diff)
downloadx86_64-55e73d20f66202aa9765ab7b3fc834b178ee5899.tar.gz
net: wireless: bcmdhd: fix buffer overrun in private command path
buffer overrun case found when length parameter manipulated. 1. if input parameter buffer length is less than 4k, then allocate 4k by default. It help to get enough margin for output string overwritten. 2. added additional length check not to override user space allocated buffer size. Signed-off-by: Insun Song <insun.song@broadcom.com> Bug: 29000183 Change-Id: I0c15d764c1648920f0214ec47ada689ca44ebfba
-rw-r--r--drivers/net/wireless/bcmdhd/wl_android.c93
-rw-r--r--drivers/net/wireless/bcmdhd/wl_android.h1
2 files changed, 59 insertions, 35 deletions
diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c
index 0c9f1f47f180..46b00bd91383 100644
--- a/drivers/net/wireless/bcmdhd/wl_android.c
+++ b/drivers/net/wireless/bcmdhd/wl_android.c
@@ -246,11 +246,18 @@ static int wl_android_get_rssi(struct net_device *net, char *command, int total_
return -1;
if ((ssid.SSID_len == 0) || (ssid.SSID_len > DOT11_MAX_SSID_LEN)) {
DHD_ERROR(("%s: wldev_get_ssid failed\n", __FUNCTION__));
+ } else if (total_len <= ssid.SSID_len) {
+ return -ENOMEM;
} else {
memcpy(command, ssid.SSID, ssid.SSID_len);
bytes_written = ssid.SSID_len;
}
- bytes_written += snprintf(&command[bytes_written], total_len, " rssi %d", rssi);
+ if ((total_len - bytes_written) < (strlen(" rssi -XXX") + 1))
+ return -ENOMEM;
+ bytes_written += scnprintf(&command[bytes_written],
+ total_len - bytes_written, " rssi %d", rssi);
+ command[bytes_written] = '\0';
+
DHD_INFO(("%s: command result is %s (%d)\n", __FUNCTION__, command, bytes_written));
return bytes_written;
}
@@ -1283,10 +1290,13 @@ int wl_keep_alive_set(struct net_device *dev, char* extra, int total_len)
int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
{
#define PRIVATE_COMMAND_MAX_LEN 8192
+#define PRIVATE_COMMAND_DEF_LEN 4096
+
int ret = 0;
char *command = NULL;
int bytes_written = 0;
android_wifi_priv_cmd priv_cmd;
+ int buf_size = 0;
net_os_wake_lock(net);
@@ -1320,12 +1330,17 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
goto exit;
}
}
+
if ((priv_cmd.total_len > PRIVATE_COMMAND_MAX_LEN) || (priv_cmd.total_len < 0)) {
- DHD_ERROR(("%s: too long priavte command\n", __FUNCTION__));
+ DHD_ERROR(("%s: buf length invalid:%d\n", __FUNCTION__,
+ priv_cmd.total_len));
ret = -EINVAL;
goto exit;
}
- command = kmalloc((priv_cmd.total_len + 1), GFP_KERNEL);
+
+ buf_size = max(priv_cmd.total_len, PRIVATE_COMMAND_DEF_LEN);
+ command = kmalloc((buf_size + 1), GFP_KERNEL);
+
if (!command)
{
DHD_ERROR(("%s: failed to allocate memory\n", __FUNCTION__));
@@ -1340,6 +1355,41 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
DHD_INFO(("%s: Android private cmd \"%s\" on %s\n", __FUNCTION__, command, ifr->ifr_name));
+ bytes_written = wl_handle_private_cmd(net, command, priv_cmd.total_len);
+ if (bytes_written >= 0) {
+ if ((bytes_written == 0) && (priv_cmd.total_len > 0))
+ command[0] = '\0';
+ if (bytes_written >= priv_cmd.total_len) {
+ DHD_ERROR(("%s: err. b_w:%d >= tot:%d\n", __FUNCTION__,
+ bytes_written, priv_cmd.total_len));
+ ret = BCME_BUFTOOSHORT;
+ goto exit;
+ }
+ bytes_written++;
+ priv_cmd.used_len = bytes_written;
+ if (copy_to_user(priv_cmd.buf, command, bytes_written)) {
+ DHD_ERROR(("%s: failed copy to user\n", __FUNCTION__));
+ ret = -EFAULT;
+ }
+ } else {
+ ret = bytes_written;
+ }
+
+exit:
+ net_os_wake_unlock(net);
+ kfree(command);
+ return ret;
+}
+
+int
+wl_handle_private_cmd(struct net_device *net, char *command, u32 buf_size)
+{
+ int bytes_written = 0;
+ android_wifi_priv_cmd priv_cmd;
+
+ bzero(&priv_cmd, sizeof(android_wifi_priv_cmd));
+ priv_cmd.total_len = buf_size;
+
if (strnicmp(command, CMD_START, strlen(CMD_START)) == 0) {
DHD_INFO(("%s, Received regular START command\n", __FUNCTION__));
bytes_written = wl_android_wifi_on(net);
@@ -1349,10 +1399,9 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
}
if (!g_wifi_on) {
- DHD_ERROR(("%s: Ignore private cmd \"%s\" - iface %s is down\n",
- __FUNCTION__, command, ifr->ifr_name));
- ret = 0;
- goto exit;
+ DHD_ERROR(("%s: Ignore private cmd \"%s\" - iface is down\n",
+ __FUNCTION__, command));
+ return 0;
}
if (strnicmp(command, CMD_STOP, strlen(CMD_STOP)) == 0) {
@@ -1509,36 +1558,10 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
}
else {
DHD_ERROR(("Unknown PRIVATE command %s - ignored\n", command));
- snprintf(command, 3, "OK");
- bytes_written = strlen("OK");
+ bytes_written = scnprintf(command, sizeof("FAIL"), "FAIL");
}
- if (bytes_written >= 0) {
- if ((bytes_written == 0) && (priv_cmd.total_len > 0))
- command[0] = '\0';
- if (bytes_written >= priv_cmd.total_len) {
- DHD_ERROR(("%s: bytes_written = %d\n", __FUNCTION__, bytes_written));
- bytes_written = priv_cmd.total_len;
- } else {
- bytes_written++;
- }
- priv_cmd.used_len = bytes_written;
- if (copy_to_user(priv_cmd.buf, command, bytes_written)) {
- DHD_ERROR(("%s: failed to copy data to user buffer\n", __FUNCTION__));
- ret = -EFAULT;
- }
- }
- else {
- ret = bytes_written;
- }
-
-exit:
- net_os_wake_unlock(net);
- if (command) {
- kfree(command);
- }
-
- return ret;
+ return bytes_written;
}
int wl_android_init(void)
diff --git a/drivers/net/wireless/bcmdhd/wl_android.h b/drivers/net/wireless/bcmdhd/wl_android.h
index 282713278b82..f62b646a0eaa 100644
--- a/drivers/net/wireless/bcmdhd/wl_android.h
+++ b/drivers/net/wireless/bcmdhd/wl_android.h
@@ -53,6 +53,7 @@ void wl_android_post_init(void);
int wl_android_wifi_on(struct net_device *dev);
int wl_android_wifi_off(struct net_device *dev, bool on_failure);
int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd);
+int wl_handle_private_cmd(struct net_device *net, char *command, u32 cmd_len);
/* hostap mac mode */