aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless
diff options
context:
space:
mode:
authorJussi Kivilinna <jussi.kivilinna@mbnet.fi>2010-12-21 15:44:05 -0500
committerJohn W. Linville <linville@tuxdriver.com>2010-12-22 15:43:30 -0500
commitbfe3850b0cfca6ba64395e2705d9a51cd044f374 (patch)
treef7a0a8c14ae3a55ba42d3361aa8d038ec1630870 /drivers/net/wireless
parentab72efdf107e5b0e0a05efb8f24cc6c598ae31ea (diff)
rndis_wlan: scanning, workaround device returning incorrect bssid-list item count.
Sometimes device returns wrong number of items in bssid-list. Appears that some specific beacons trigger this problem and leads to very poor scanning results. Workaround by ignoring num_items received from device and walkthrough full bssid-list buffer. v2: Fix buffer range checks and reading next item length. Old code read behind buffer on last item but didn't use those values as 'count' would also reach zero. Also fix resizing of buffer if device has larger buffer, old code assumed that BSSID-list OID would return same buffer size when it really can return yet another new larger length. Tested-by: Luís Picciochi <Pitxyoki@gmail.com> Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r--drivers/net/wireless/rndis_wlan.c80
1 files changed, 62 insertions, 18 deletions
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 4a4f00591447..de4c05019f1e 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -1967,8 +1967,8 @@ static struct cfg80211_bss *rndis_bss_info_update(struct usbnet *usbdev,
1967 int ie_len, bssid_len; 1967 int ie_len, bssid_len;
1968 u8 *ie; 1968 u8 *ie;
1969 1969
1970 netdev_dbg(usbdev->net, " found bssid: '%.32s' [%pM]\n", 1970 netdev_dbg(usbdev->net, " found bssid: '%.32s' [%pM], len: %d\n",
1971 bssid->ssid.essid, bssid->mac); 1971 bssid->ssid.essid, bssid->mac, le32_to_cpu(bssid->length));
1972 1972
1973 /* parse bssid structure */ 1973 /* parse bssid structure */
1974 bssid_len = le32_to_cpu(bssid->length); 1974 bssid_len = le32_to_cpu(bssid->length);
@@ -2002,54 +2002,98 @@ static struct cfg80211_bss *rndis_bss_info_update(struct usbnet *usbdev,
2002 GFP_KERNEL); 2002 GFP_KERNEL);
2003} 2003}
2004 2004
2005static struct ndis_80211_bssid_ex *next_bssid_list_item(
2006 struct ndis_80211_bssid_ex *bssid,
2007 int *bssid_len, void *buf, int len)
2008{
2009 void *buf_end, *bssid_end;
2010
2011 buf_end = (char *)buf + len;
2012 bssid_end = (char *)bssid + *bssid_len;
2013
2014 if ((int)(buf_end - bssid_end) < sizeof(bssid->length)) {
2015 *bssid_len = 0;
2016 return NULL;
2017 } else {
2018 bssid = (void *)((char *)bssid + *bssid_len);
2019 *bssid_len = le32_to_cpu(bssid->length);
2020 return bssid;
2021 }
2022}
2023
2024static bool check_bssid_list_item(struct ndis_80211_bssid_ex *bssid,
2025 int bssid_len, void *buf, int len)
2026{
2027 void *buf_end, *bssid_end;
2028
2029 if (!bssid || bssid_len <= 0 || bssid_len > len)
2030 return false;
2031
2032 buf_end = (char *)buf + len;
2033 bssid_end = (char *)bssid + bssid_len;
2034
2035 return (int)(buf_end - bssid_end) >= 0 && (int)(bssid_end - buf) >= 0;
2036}
2037
2005static int rndis_check_bssid_list(struct usbnet *usbdev, u8 *match_bssid, 2038static int rndis_check_bssid_list(struct usbnet *usbdev, u8 *match_bssid,
2006 bool *matched) 2039 bool *matched)
2007{ 2040{
2008 void *buf = NULL; 2041 void *buf = NULL;
2009 struct ndis_80211_bssid_list_ex *bssid_list; 2042 struct ndis_80211_bssid_list_ex *bssid_list;
2010 struct ndis_80211_bssid_ex *bssid; 2043 struct ndis_80211_bssid_ex *bssid;
2011 int ret = -EINVAL, len, count, bssid_len; 2044 int ret = -EINVAL, len, count, bssid_len, real_count, new_len;
2012 bool resized = false;
2013 2045
2014 netdev_dbg(usbdev->net, "check_bssid_list\n"); 2046 netdev_dbg(usbdev->net, "%s()\n", __func__);
2015 2047
2016 len = CONTROL_BUFFER_SIZE; 2048 len = CONTROL_BUFFER_SIZE;
2017resize_buf: 2049resize_buf:
2018 buf = kmalloc(len, GFP_KERNEL); 2050 buf = kzalloc(len, GFP_KERNEL);
2019 if (!buf) { 2051 if (!buf) {
2020 ret = -ENOMEM; 2052 ret = -ENOMEM;
2021 goto out; 2053 goto out;
2022 } 2054 }
2023 2055
2024 ret = rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &len); 2056 /* BSSID-list might have got bigger last time we checked, keep
2025 if (ret != 0) 2057 * resizing until it won't get any bigger.
2058 */
2059 new_len = len;
2060 ret = rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &new_len);
2061 if (ret != 0 || new_len < sizeof(struct ndis_80211_bssid_list_ex))
2026 goto out; 2062 goto out;
2027 2063
2028 if (!resized && len > CONTROL_BUFFER_SIZE) { 2064 if (new_len > len) {
2029 resized = true; 2065 len = new_len;
2030 kfree(buf); 2066 kfree(buf);
2031 goto resize_buf; 2067 goto resize_buf;
2032 } 2068 }
2033 2069
2070 len = new_len;
2071
2034 bssid_list = buf; 2072 bssid_list = buf;
2035 bssid = bssid_list->bssid;
2036 bssid_len = le32_to_cpu(bssid->length);
2037 count = le32_to_cpu(bssid_list->num_items); 2073 count = le32_to_cpu(bssid_list->num_items);
2038 netdev_dbg(usbdev->net, "check_bssid_list: %d BSSIDs found (buflen: %d)\n", 2074 real_count = 0;
2039 count, len); 2075 netdev_dbg(usbdev->net, "%s(): buflen: %d\n", __func__, len);
2076
2077 bssid_len = 0;
2078 bssid = next_bssid_list_item(bssid_list->bssid, &bssid_len, buf, len);
2040 2079
2041 while (count && ((void *)bssid + bssid_len) <= (buf + len)) { 2080 /* Device returns incorrect 'num_items'. Workaround by ignoring the
2081 * received 'num_items' and walking through full bssid buffer instead.
2082 */
2083 while (check_bssid_list_item(bssid, bssid_len, buf, len)) {
2042 if (rndis_bss_info_update(usbdev, bssid) && match_bssid && 2084 if (rndis_bss_info_update(usbdev, bssid) && match_bssid &&
2043 matched) { 2085 matched) {
2044 if (compare_ether_addr(bssid->mac, match_bssid)) 2086 if (compare_ether_addr(bssid->mac, match_bssid))
2045 *matched = true; 2087 *matched = true;
2046 } 2088 }
2047 2089
2048 bssid = (void *)bssid + bssid_len; 2090 real_count++;
2049 bssid_len = le32_to_cpu(bssid->length); 2091 bssid = next_bssid_list_item(bssid, &bssid_len, buf, len);
2050 count--;
2051 } 2092 }
2052 2093
2094 netdev_dbg(usbdev->net, "%s(): num_items from device: %d, really found:"
2095 " %d\n", __func__, count, real_count);
2096
2053out: 2097out:
2054 kfree(buf); 2098 kfree(buf);
2055 return ret; 2099 return ret;