cfg80211-wext: return -E2BIG when buffer can't hold full BSS entry

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>
This commit is contained in:
James Minor 2015-02-24 12:58:20 -06:00 committed by Johannes Berg
parent 36ef906ee8
commit 76a70e9c4b

View 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);
current_ev = iwe_stream_add_event_check(info, current_ev,
end_buf, &iwe,
IW_EV_UINT_LEN);
if (IS_ERR(current_ev))
goto unlock;
}
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);
}
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;
ieee80211_scan_add_ies(info, ies, &current_ev, end_buf);
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;
}