@@ -521,6 +521,33 @@ iwe_stream_add_event(struct iw_request_info *info, char *stream, char *ends,
/*------------------------------------------------------------------*/
/*
+ * Wrapper to add an Wireless Event to a stream of events.
+ * Also returns indication of overflow conditions.
+ */
+static inline int
+iwe_stream_add_event_check(struct iw_request_info *info, char **stream,
+ char *ends, struct iw_event *iwe, int event_len)
+{
+ int lcp_len = iwe_stream_lcp_len(info);
+
+ event_len = iwe_stream_event_len_adjust(info, event_len);
+
+ /* Check if it's possible */
+ if (likely((*stream + event_len) < ends)) {
+ iwe->len = event_len;
+ /* Beware of alignement issues on 64 bits */
+ memcpy(*stream, (char *) iwe, IW_EV_LCP_PK_LEN);
+ memcpy(*stream + lcp_len, &iwe->u,
+ event_len - lcp_len);
+ *stream += event_len;
+ return 0;
+ } else {
+ return -E2BIG;
+ }
+}
+
+/*------------------------------------------------------------------*/
+/*
* Wrapper to add an short Wireless Event containing a pointer to a
* stream of events.
*/
@@ -547,6 +574,35 @@ iwe_stream_add_point(struct iw_request_info *info, char *stream, char *ends,
/*------------------------------------------------------------------*/
/*
+ * Wrapper to add an short Wireless Event containing a pointer to a
+ * stream of events.
+ * Also returns indication of overflow conditions.
+ */
+static inline int
+iwe_stream_add_point_check(struct iw_request_info *info, char **stream,
+ char *ends, struct iw_event *iwe, char *extra)
+{
+ int event_len = iwe_stream_point_len(info) + iwe->u.data.length;
+ int point_len = iwe_stream_point_len(info);
+ int lcp_len = iwe_stream_lcp_len(info);
+
+ /* Check if it's possible */
+ if (likely((*stream + event_len) < ends)) {
+ iwe->len = event_len;
+ memcpy(*stream, (char *) iwe, IW_EV_LCP_PK_LEN);
+ memcpy(*stream + lcp_len,
+ ((char *) &iwe->u) + IW_EV_POINT_OFF,
+ IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
+ memcpy(*stream + point_len, extra, iwe->u.data.length);
+ *stream += event_len;
+ return 0;
+ } else {
+ return -E2BIG;
+ }
+}
+
+/*------------------------------------------------------------------*/
+/*
* Wrapper to add a value to a Wireless Event in a stream of events.
* Be careful, this one is tricky to use properly :
* At the first run, you need to have (value = event + IW_EV_LCP_LEN).
@@ -572,4 +628,35 @@ iwe_stream_add_value(struct iw_request_info *info, char *event, char *value,
return value;
}
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper to add a value to a Wireless Event in a stream of events.
+ * Be careful, this one is tricky to use properly :
+ * At the first run, you need to have (value = event + IW_EV_LCP_LEN).
+ * Also returns indication of overflow conditions.
+ */
+static inline int
+iwe_stream_add_value_check(struct iw_request_info *info, char *event,
+ char **value, char *ends, struct iw_event *iwe,
+ int event_len)
+{
+ int lcp_len = iwe_stream_lcp_len(info);
+
+ /* Don't duplicate LCP */
+ event_len -= IW_EV_LCP_LEN;
+
+ /* Check if it's possible */
+ if (likely((*value + event_len) < ends)) {
+ /* Add new value */
+ memcpy(*value, &iwe->u, event_len);
+ *value += event_len;
+ /* Patch LCP */
+ iwe->len = *value - event;
+ memcpy(event, (char *) iwe, lcp_len);
+ return 0;
+ } else {
+ return -E2BIG;
+ }
+}
+
#endif /* _IW_HANDLER_H */
@@ -1239,15 +1239,16 @@ int cfg80211_wext_siwscan(struct net_device *dev,
}
EXPORT_SYMBOL_GPL(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 int 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;
+ int err = 0;
if (!ies)
- return;
+ return err;
/*
* If needed, fragment the IEs buffer (at IE boundaries) into short
@@ -1264,10 +1265,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);
-
+ err = iwe_stream_add_point_check(info, current_ev,
+ end_buf, &iwe,
+ (void *)pos);
+ if (err)
+ return err;
pos = next;
}
@@ -1275,44 +1277,55 @@ 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);
+ err = iwe_stream_add_point_check(info, current_ev,
+ end_buf, &iwe,
+ (void *)pos);
+ if (err)
+ return err;
}
+ return 0;
}
-static char *
+static int
ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
- struct cfg80211_internal_bss *bss, char *current_ev,
+ struct cfg80211_internal_bss *bss, char **current_ev,
char *end_buf)
{
const struct cfg80211_bss_ies *ies;
struct iw_event iwe;
const u8 *ie;
- u8 *buf, *cfg, *p;
+ u8 *buf, *cfg;
+ char *p;
int rem, i, sig;
bool ismesh = false;
+ int err;
memset(&iwe, 0, sizeof(iwe));
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);
+ err = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
+ IW_EV_ADDR_LEN);
+ if (err)
+ return err;
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);
+ err = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
+ IW_EV_FREQ_LEN);
+ if (err)
+ return err;
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);
+ err = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
+ IW_EV_FREQ_LEN);
+ if (err)
+ return err;
if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) {
memset(&iwe, 0, sizeof(iwe));
@@ -1341,8 +1354,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);
+ err = iwe_stream_add_event_check(info, current_ev, end_buf,
+ &iwe, IW_EV_QUAL_LEN);
+ if (err)
+ return err;
+
}
memset(&iwe, 0, sizeof(iwe));
@@ -1352,8 +1368,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, "");
+ err = iwe_stream_add_point_check(info, current_ev, end_buf,
+ &iwe, "");
+ if (err)
+ return err;
rcu_read_lock();
ies = rcu_dereference(bss->pub.ies);
@@ -1371,16 +1389,22 @@ 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);
+ err = iwe_stream_add_point_check(info, current_ev,
+ end_buf, &iwe,
+ (u8 *)ie + 2);
+ if (err)
+ return err;
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);
+ err = iwe_stream_add_point_check(info, current_ev,
+ end_buf, &iwe,
+ (u8 *)ie + 2);
+ if (err)
+ return err;
break;
case WLAN_EID_MESH_CONFIG:
ismesh = true;
@@ -1395,47 +1419,62 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
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);
+ err = iwe_stream_add_point_check(info, current_ev,
+ end_buf,
+ &iwe, buf);
+ if (err)
+ goto out_fail_mesh;
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);
+ err = iwe_stream_add_point_check(info, current_ev,
+ end_buf,
+ &iwe, buf);
+ if (err)
+ goto out_fail_mesh;
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);
+ err = iwe_stream_add_point_check(info, current_ev,
+ end_buf,
+ &iwe, buf);
+ if (err)
+ goto out_fail_mesh;
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);
+ err = iwe_stream_add_point_check(info, current_ev,
+ end_buf,
+ &iwe, buf);
+ if (err)
+ goto out_fail_mesh;
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);
+ err = iwe_stream_add_point_check(info, current_ev,
+ end_buf,
+ &iwe, buf);
+ if (err)
+ goto out_fail_mesh;
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);
+ err = iwe_stream_add_point_check(info, current_ev,
+ end_buf,
+ &iwe, buf);
+ if (err)
+ goto out_fail_mesh;
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);
+ err = iwe_stream_add_point_check(info, current_ev,
+ end_buf,
+ &iwe, buf);
+out_fail_mesh:
kfree(buf);
+ if (err)
+ return err;
break;
case WLAN_EID_SUPP_RATES:
case WLAN_EID_EXT_SUPP_RATES:
/* display all supported rates in readable format */
- p = current_ev + iwe_stream_lcp_len(info);
+ p = *current_ev + iwe_stream_lcp_len(info);
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = SIOCGIWRATE;
@@ -1445,10 +1484,13 @@ 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);
- p = iwe_stream_add_value(info, current_ev, p,
- end_buf, &iwe, IW_EV_PARAM_LEN);
+ err = iwe_stream_add_value_check(
+ info, *current_ev, &p, end_buf, &iwe,
+ IW_EV_PARAM_LEN);
+ if (err)
+ return err;
}
- current_ev = p;
+ *current_ev = p;
break;
}
rem -= ie[1] + 2;
@@ -1465,8 +1507,10 @@ 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);
+ err = iwe_stream_add_event_check(info, current_ev, end_buf,
+ &iwe, IW_EV_UINT_LEN);
+ if (err)
+ return err;
}
buf = kmalloc(31, GFP_ATOMIC);
@@ -1475,22 +1519,26 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
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);
+ err = iwe_stream_add_point_check(info, current_ev, end_buf,
+ &iwe, buf);
+ if (err)
+ return err;
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);
+ err = iwe_stream_add_point_check(info, current_ev,
+ end_buf, &iwe, buf);
kfree(buf);
+ if (err)
+ return err;
}
- ieee80211_scan_add_ies(info, ies, ¤t_ev, end_buf);
+ err = ieee80211_scan_add_ies(info, ies, current_ev, end_buf);
rcu_read_unlock();
- return current_ev;
+ return err;
}
@@ -1501,20 +1549,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;
+ goto out_fail;
}
- current_ev = ieee80211_bss(&rdev->wiphy, info, bss,
- current_ev, end_buf);
+ err = ieee80211_bss(&rdev->wiphy, info, bss,
+ ¤t_ev, end_buf);
+ if (err)
+ goto out_fail;
}
+ out_fail:
spin_unlock_bh(&rdev->bss_lock);
- return current_ev - buf;
+ if (err)
+ return err;
+ else
+ return current_ev - buf;
}