From 5fd537ad96aec7b0a2e2f3e19046dcfebf6ab689 Mon Sep 17 00:00:00 2001
From: Dmitry Shmidt <dimitrysh@google.com>
Date: Fri, 29 Jul 2011 13:45:06 -0700
Subject: [PATCH 514/696] net: wireless: bcmdhd: Fix memory corruption in wl_android_get_rssi()

In case of FW problem wldev_get_ssid() doesn't return error and ssid
structure has garbage.

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
---
 drivers/net/wireless/bcmdhd/wl_android.c |   54 +++++++++++++++++------------
 1 files changed, 32 insertions(+), 22 deletions(-)

diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c
index 8314bbc..830dbe5 100644
--- a/drivers/net/wireless/bcmdhd/wl_android.c
+++ b/drivers/net/wireless/bcmdhd/wl_android.c
@@ -149,22 +149,25 @@ static int wl_android_get_link_speed(struct net_device *net, char *command, int
 
 static int wl_android_get_rssi(struct net_device *net, char *command, int total_len)
 {
-	wlc_ssid_t ssid;
+	wlc_ssid_t ssid = {0};
 	int rssi;
-	int bytes_written;
+	int bytes_written = 0;
 	int error;
 
 	error = wldev_get_rssi(net, &rssi);
 	if (error)
 		return -1;
-
 	error = wldev_get_ssid(net, &ssid);
 	if (error)
 		return -1;
-	memcpy(command, ssid.SSID, ssid.SSID_len);
-	bytes_written = ssid.SSID_len;
+	if ((ssid.SSID_len == 0) || (ssid.SSID_len > DOT11_MAX_SSID_LEN)) {
+		DHD_ERROR(("%s: wldev_get_ssid failed\n", __FUNCTION__));
+	} else {
+		memcpy(command, ssid.SSID, ssid.SSID_len);
+		bytes_written = ssid.SSID_len;
+	}
 	bytes_written += snprintf(&command[bytes_written], total_len, " rssi %d", rssi);
-	DHD_INFO(("%s: command result is %s \n", __FUNCTION__, command));
+	DHD_INFO(("%s: command result is %s (%d)\n", __FUNCTION__, command, bytes_written));
 	return bytes_written;
 }
 
@@ -367,24 +370,26 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
 	int ret = 0;
 	char *command = NULL;
 	int bytes_written = 0;
-	android_wifi_priv_cmd *priv_cmd;
+	android_wifi_priv_cmd priv_cmd;
 
 	net_os_wake_lock(net);
 
-	priv_cmd = (android_wifi_priv_cmd*)ifr->ifr_data;
-	if (!priv_cmd)
-	{
+	if (!ifr->ifr_data) {
 		ret = -EINVAL;
 		goto exit;
 	}
-	command = kmalloc(priv_cmd->total_len, GFP_KERNEL);
+	if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(android_wifi_priv_cmd))) {
+		ret = -EFAULT;
+		goto exit;
+	}
+	command = kmalloc(priv_cmd.total_len, GFP_KERNEL);
 	if (!command)
 	{
 		DHD_ERROR(("%s: failed to allocate memory\n", __FUNCTION__));
 		ret = -ENOMEM;
 		goto exit;
 	}
-	if (copy_from_user(command, priv_cmd->buf, priv_cmd->total_len)) {
+	if (copy_from_user(command, priv_cmd.buf, priv_cmd.total_len)) {
 		ret = -EFAULT;
 		goto exit;
 	}
@@ -396,7 +401,7 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
 		bytes_written = wl_android_wifi_on(net);
 	}
 	else if (strnicmp(command, CMD_SETFWPATH, strlen(CMD_SETFWPATH)) == 0) {
-		bytes_written = wl_android_set_fwpath(net, command, priv_cmd->total_len);
+		bytes_written = wl_android_set_fwpath(net, command, priv_cmd.total_len);
 	}
 
 	if (!g_wifi_on) {
@@ -416,10 +421,10 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
 		/* TBD: SCAN-PASSIVE */
 	}
 	else if (strnicmp(command, CMD_RSSI, strlen(CMD_RSSI)) == 0) {
-		bytes_written = wl_android_get_rssi(net, command, priv_cmd->total_len);
+		bytes_written = wl_android_get_rssi(net, command, priv_cmd.total_len);
 	}
 	else if (strnicmp(command, CMD_LINKSPEED, strlen(CMD_LINKSPEED)) == 0) {
-		bytes_written = wl_android_get_link_speed(net, command, priv_cmd->total_len);
+		bytes_written = wl_android_get_link_speed(net, command, priv_cmd.total_len);
 	}
 	else if (strnicmp(command, CMD_RXFILTER_START, strlen(CMD_RXFILTER_START)) == 0) {
 		bytes_written = net_os_set_packet_filter(net, 1);
@@ -445,14 +450,14 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
 		/* TBD: BTCOEXMODE */
 	}
 	else if (strnicmp(command, CMD_SETSUSPENDOPT, strlen(CMD_SETSUSPENDOPT)) == 0) {
-		bytes_written = wl_android_set_suspendopt(net, command, priv_cmd->total_len);
+		bytes_written = wl_android_set_suspendopt(net, command, priv_cmd.total_len);
 	}
 	else if (strnicmp(command, CMD_SETBAND, strlen(CMD_SETBAND)) == 0) {
 		uint band = *(command + strlen(CMD_SETBAND) + 1) - '0';
 		bytes_written = wldev_set_band(net, band);
 	}
 	else if (strnicmp(command, CMD_GETBAND, strlen(CMD_GETBAND)) == 0) {
-		bytes_written = wl_android_get_band(net, command, priv_cmd->total_len);
+		bytes_written = wl_android_get_band(net, command, priv_cmd.total_len);
 	}
 	else if (strnicmp(command, CMD_COUNTRY, strlen(CMD_COUNTRY)) == 0) {
 		char *country_code = command + strlen(CMD_COUNTRY) + 1;
@@ -463,7 +468,7 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
 		bytes_written = dhd_dev_pno_reset(net);
 	}
 	else if (strnicmp(command, CMD_PNOSETUP_SET, strlen(CMD_PNOSETUP_SET)) == 0) {
-		bytes_written = wl_android_set_pno_setup(net, command, priv_cmd->total_len);
+		bytes_written = wl_android_set_pno_setup(net, command, priv_cmd.total_len);
 	}
 	else if (strnicmp(command, CMD_PNOENABLE_SET, strlen(CMD_PNOENABLE_SET)) == 0) {
 		uint pfn_enabled = *(command + strlen(CMD_PNOENABLE_SET) + 1) - '0';
@@ -471,7 +476,7 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
 	}
 #endif
 	else if (strnicmp(command, CMD_P2P_DEV_ADDR, strlen(CMD_P2P_DEV_ADDR)) == 0) {
-		bytes_written = wl_android_get_p2p_dev_addr(net, command, priv_cmd->total_len);
+		bytes_written = wl_android_get_p2p_dev_addr(net, command, priv_cmd.total_len);
 	} else {
 		DHD_ERROR(("Unknown PRIVATE command %s - ignored\n", command));
 		snprintf(command, 3, "OK");
@@ -479,9 +484,14 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
 	}
 
 	if (bytes_written > 0) {
-		bytes_written++;
-		priv_cmd->used_len = bytes_written;
-		if (copy_to_user(priv_cmd->buf, command, bytes_written)) {
+		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;
 		}
-- 
1.7.1


