net: wireless: bcmdhd: Fix memory corruption in wl_android_get_rssi()
authorDmitry Shmidt <dimitrysh@google.com>
Fri, 29 Jul 2011 20:45:06 +0000 (13:45 -0700)
committermgross <mark.gross@intel.com>
Wed, 9 Nov 2011 20:22:10 +0000 (12:22 -0800)
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

index 8314bbc..830dbe5 100644 (file)
@@ -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;
                }