cfg80211-wext: return -E2BIG when buffer can't hold full BSS entry
authorJames Minor <james.minor@ni.com>
Tue, 24 Feb 2015 18:58:20 +0000 (12:58 -0600)
committerJohannes Berg <johannes.berg@intel.com>
Sat, 28 Feb 2015 20:31:12 +0000 (21:31 +0100)
When using the wext compatibility code in cfg80211, part of the IEs
can be truncated if the passed user buffer is large enough for part
of the BSS but not large enough for all of the IEs.  This can cause
an EAP network to show up as a PSK network.

Always return -E2BIG in this case to avoid truncating data.

Since this changes the control flow, use an on-stack variable for
a small buffer instead of allocating it.

Signed-off-by: James Minor <james.minor@ni.com>
[rework patch to error out immediately, use _check wrappers]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/wireless/scan.c

index 25e1e1fad9050f2100eb1b4caf95f6969b64c00f..ceb8f0040dae22662adb17347c888c96a7ed2344 100644 (file)
@@ -1239,15 +1239,15 @@ int cfg80211_wext_siwscan(struct net_device *dev,
 }
 EXPORT_WEXT_HANDLER(cfg80211_wext_siwscan);
 
-static void ieee80211_scan_add_ies(struct iw_request_info *info,
-                                  const struct cfg80211_bss_ies *ies,
-                                  char **current_ev, char *end_buf)
+static char *ieee80211_scan_add_ies(struct iw_request_info *info,
+                                   const struct cfg80211_bss_ies *ies,
+                                   char *current_ev, char *end_buf)
 {
        const u8 *pos, *end, *next;
        struct iw_event iwe;
 
        if (!ies)
-               return;
+               return current_ev;
 
        /*
         * If needed, fragment the IEs buffer (at IE boundaries) into short
@@ -1264,10 +1264,11 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
                memset(&iwe, 0, sizeof(iwe));
                iwe.cmd = IWEVGENIE;
                iwe.u.data.length = next - pos;
-               *current_ev = iwe_stream_add_point(info, *current_ev,
-                                                  end_buf, &iwe,
-                                                  (void *)pos);
-
+               current_ev = iwe_stream_add_point_check(info, current_ev,
+                                                       end_buf, &iwe,
+                                                       (void *)pos);
+               if (IS_ERR(current_ev))
+                       return current_ev;
                pos = next;
        }
 
@@ -1275,10 +1276,14 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
                memset(&iwe, 0, sizeof(iwe));
                iwe.cmd = IWEVGENIE;
                iwe.u.data.length = end - pos;
-               *current_ev = iwe_stream_add_point(info, *current_ev,
-                                                  end_buf, &iwe,
-                                                  (void *)pos);
+               current_ev = iwe_stream_add_point_check(info, current_ev,
+                                                       end_buf, &iwe,
+                                                       (void *)pos);
+               if (IS_ERR(current_ev))
+                       return current_ev;
        }
+
+       return current_ev;
 }
 
 static char *
@@ -1289,7 +1294,8 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
        const struct cfg80211_bss_ies *ies;
        struct iw_event iwe;
        const u8 *ie;
-       u8 *buf, *cfg, *p;
+       u8 buf[50];
+       u8 *cfg, *p, *tmp;
        int rem, i, sig;
        bool ismesh = false;
 
@@ -1297,22 +1303,28 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
        iwe.cmd = SIOCGIWAP;
        iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
        memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN);
-       current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
-                                         IW_EV_ADDR_LEN);
+       current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
+                                               IW_EV_ADDR_LEN);
+       if (IS_ERR(current_ev))
+               return current_ev;
 
        memset(&iwe, 0, sizeof(iwe));
        iwe.cmd = SIOCGIWFREQ;
        iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq);
        iwe.u.freq.e = 0;
-       current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
-                                         IW_EV_FREQ_LEN);
+       current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
+                                               IW_EV_FREQ_LEN);
+       if (IS_ERR(current_ev))
+               return current_ev;
 
        memset(&iwe, 0, sizeof(iwe));
        iwe.cmd = SIOCGIWFREQ;
        iwe.u.freq.m = bss->pub.channel->center_freq;
        iwe.u.freq.e = 6;
-       current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
-                                         IW_EV_FREQ_LEN);
+       current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
+                                               IW_EV_FREQ_LEN);
+       if (IS_ERR(current_ev))
+               return current_ev;
 
        if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) {
                memset(&iwe, 0, sizeof(iwe));
@@ -1341,8 +1353,11 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
                        /* not reached */
                        break;
                }
-               current_ev = iwe_stream_add_event(info, current_ev, end_buf,
-                                                 &iwe, IW_EV_QUAL_LEN);
+               current_ev = iwe_stream_add_event_check(info, current_ev,
+                                                       end_buf, &iwe,
+                                                       IW_EV_QUAL_LEN);
+               if (IS_ERR(current_ev))
+                       return current_ev;
        }
 
        memset(&iwe, 0, sizeof(iwe));
@@ -1352,8 +1367,10 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
        else
                iwe.u.data.flags = IW_ENCODE_DISABLED;
        iwe.u.data.length = 0;
-       current_ev = iwe_stream_add_point(info, current_ev, end_buf,
-                                         &iwe, "");
+       current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
+                                               &iwe, "");
+       if (IS_ERR(current_ev))
+               return current_ev;
 
        rcu_read_lock();
        ies = rcu_dereference(bss->pub.ies);
@@ -1371,66 +1388,91 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
                        iwe.cmd = SIOCGIWESSID;
                        iwe.u.data.length = ie[1];
                        iwe.u.data.flags = 1;
-                       current_ev = iwe_stream_add_point(info, current_ev, end_buf,
-                                                         &iwe, (u8 *)ie + 2);
+                       current_ev = iwe_stream_add_point_check(info,
+                                                               current_ev,
+                                                               end_buf, &iwe,
+                                                               (u8 *)ie + 2);
+                       if (IS_ERR(current_ev))
+                               goto unlock;
                        break;
                case WLAN_EID_MESH_ID:
                        memset(&iwe, 0, sizeof(iwe));
                        iwe.cmd = SIOCGIWESSID;
                        iwe.u.data.length = ie[1];
                        iwe.u.data.flags = 1;
-                       current_ev = iwe_stream_add_point(info, current_ev, end_buf,
-                                                         &iwe, (u8 *)ie + 2);
+                       current_ev = iwe_stream_add_point_check(info,
+                                                               current_ev,
+                                                               end_buf, &iwe,
+                                                               (u8 *)ie + 2);
+                       if (IS_ERR(current_ev))
+                               goto unlock;
                        break;
                case WLAN_EID_MESH_CONFIG:
                        ismesh = true;
                        if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
                                break;
-                       buf = kmalloc(50, GFP_ATOMIC);
-                       if (!buf)
-                               break;
                        cfg = (u8 *)ie + 2;
                        memset(&iwe, 0, sizeof(iwe));
                        iwe.cmd = IWEVCUSTOM;
                        sprintf(buf, "Mesh Network Path Selection Protocol ID: "
                                "0x%02X", cfg[0]);
                        iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
+                       current_ev = iwe_stream_add_point_check(info,
+                                                               current_ev,
+                                                               end_buf,
+                                                               &iwe, buf);
+                       if (IS_ERR(current_ev))
+                               goto unlock;
                        sprintf(buf, "Path Selection Metric ID: 0x%02X",
                                cfg[1]);
                        iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
+                       current_ev = iwe_stream_add_point_check(info,
+                                                               current_ev,
+                                                               end_buf,
+                                                               &iwe, buf);
+                       if (IS_ERR(current_ev))
+                               goto unlock;
                        sprintf(buf, "Congestion Control Mode ID: 0x%02X",
                                cfg[2]);
                        iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
+                       current_ev = iwe_stream_add_point_check(info,
+                                                               current_ev,
+                                                               end_buf,
+                                                               &iwe, buf);
+                       if (IS_ERR(current_ev))
+                               goto unlock;
                        sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]);
                        iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
+                       current_ev = iwe_stream_add_point_check(info,
+                                                               current_ev,
+                                                               end_buf,
+                                                               &iwe, buf);
+                       if (IS_ERR(current_ev))
+                               goto unlock;
                        sprintf(buf, "Authentication ID: 0x%02X", cfg[4]);
                        iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
+                       current_ev = iwe_stream_add_point_check(info,
+                                                               current_ev,
+                                                               end_buf,
+                                                               &iwe, buf);
+                       if (IS_ERR(current_ev))
+                               goto unlock;
                        sprintf(buf, "Formation Info: 0x%02X", cfg[5]);
                        iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
+                       current_ev = iwe_stream_add_point_check(info,
+                                                               current_ev,
+                                                               end_buf,
+                                                               &iwe, buf);
+                       if (IS_ERR(current_ev))
+                               goto unlock;
                        sprintf(buf, "Capabilities: 0x%02X", cfg[6]);
                        iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
-                       kfree(buf);
+                       current_ev = iwe_stream_add_point_check(info,
+                                                               current_ev,
+                                                               end_buf,
+                                                               &iwe, buf);
+                       if (IS_ERR(current_ev))
+                               goto unlock;
                        break;
                case WLAN_EID_SUPP_RATES:
                case WLAN_EID_EXT_SUPP_RATES:
@@ -1445,8 +1487,14 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
                        for (i = 0; i < ie[1]; i++) {
                                iwe.u.bitrate.value =
                                        ((ie[i + 2] & 0x7f) * 500000);
+                               tmp = p;
                                p = iwe_stream_add_value(info, current_ev, p,
-                                               end_buf, &iwe, IW_EV_PARAM_LEN);
+                                                        end_buf, &iwe,
+                                                        IW_EV_PARAM_LEN);
+                               if (p == tmp) {
+                                       current_ev = ERR_PTR(-E2BIG);
+                                       goto unlock;
+                               }
                        }
                        current_ev = p;
                        break;
@@ -1465,31 +1513,35 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
                        iwe.u.mode = IW_MODE_MASTER;
                else
                        iwe.u.mode = IW_MODE_ADHOC;
-               current_ev = iwe_stream_add_event(info, current_ev, end_buf,
-                                                 &iwe, IW_EV_UINT_LEN);
-       }
-
-       buf = kmalloc(31, GFP_ATOMIC);
-       if (buf) {
-               memset(&iwe, 0, sizeof(iwe));
-               iwe.cmd = IWEVCUSTOM;
-               sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf));
-               iwe.u.data.length = strlen(buf);
-               current_ev = iwe_stream_add_point(info, current_ev, end_buf,
-                                                 &iwe, buf);
-               memset(&iwe, 0, sizeof(iwe));
-               iwe.cmd = IWEVCUSTOM;
-               sprintf(buf, " Last beacon: %ums ago",
-                       elapsed_jiffies_msecs(bss->ts));
-               iwe.u.data.length = strlen(buf);
-               current_ev = iwe_stream_add_point(info, current_ev,
-                                                 end_buf, &iwe, buf);
-               kfree(buf);
+               current_ev = iwe_stream_add_event_check(info, current_ev,
+                                                       end_buf, &iwe,
+                                                       IW_EV_UINT_LEN);
+               if (IS_ERR(current_ev))
+                       goto unlock;
        }
 
-       ieee80211_scan_add_ies(info, ies, &current_ev, end_buf);
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = IWEVCUSTOM;
+       sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf));
+       iwe.u.data.length = strlen(buf);
+       current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
+                                               &iwe, buf);
+       if (IS_ERR(current_ev))
+               goto unlock;
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = IWEVCUSTOM;
+       sprintf(buf, " Last beacon: %ums ago",
+               elapsed_jiffies_msecs(bss->ts));
+       iwe.u.data.length = strlen(buf);
+       current_ev = iwe_stream_add_point_check(info, current_ev,
+                                               end_buf, &iwe, buf);
+       if (IS_ERR(current_ev))
+               goto unlock;
+
+       current_ev = ieee80211_scan_add_ies(info, ies, current_ev, end_buf);
+
+ unlock:
        rcu_read_unlock();
-
        return current_ev;
 }
 
@@ -1501,19 +1553,27 @@ static int ieee80211_scan_results(struct cfg80211_registered_device *rdev,
        char *current_ev = buf;
        char *end_buf = buf + len;
        struct cfg80211_internal_bss *bss;
+       int err = 0;
 
        spin_lock_bh(&rdev->bss_lock);
        cfg80211_bss_expire(rdev);
 
        list_for_each_entry(bss, &rdev->bss_list, list) {
                if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
-                       spin_unlock_bh(&rdev->bss_lock);
-                       return -E2BIG;
+                       err = -E2BIG;
+                       break;
                }
                current_ev = ieee80211_bss(&rdev->wiphy, info, bss,
                                           current_ev, end_buf);
+               if (IS_ERR(current_ev)) {
+                       err = PTR_ERR(current_ev);
+                       break;
+               }
        }
        spin_unlock_bh(&rdev->bss_lock);
+
+       if (err)
+               return err;
        return current_ev - buf;
 }