summaryrefslogtreecommitdiff
path: root/net/wireless/scan.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless/scan.c')
-rw-r--r--net/wireless/scan.c123
1 files changed, 83 insertions, 40 deletions
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 328af43ef832..9e934b185e34 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -5,7 +5,7 @@
* Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2016 Intel Deutschland GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
*/
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -205,7 +205,7 @@ bool cfg80211_is_element_inherited(const struct element *elem,
return true;
if (elem->id == WLAN_EID_EXTENSION) {
- if (!ext_id_len)
+ if (!ext_id_len || !elem->datalen)
return true;
loop_len = ext_id_len;
list = &non_inherit_elem->data[3 + id_len];
@@ -326,8 +326,11 @@ cfg80211_gen_new_ie(const u8 *ie, size_t ielen,
/* For ML probe response, match the MLE in the frame body with
* MLD id being 'bssid_index'
*/
- if (parent->id == WLAN_EID_EXTENSION && parent->datalen > 1 &&
+ if (parent->id == WLAN_EID_EXTENSION &&
parent->data[0] == WLAN_EID_EXT_EHT_MULTI_LINK &&
+ ieee80211_mle_type_ok(parent->data + 1,
+ IEEE80211_ML_CONTROL_TYPE_BASIC,
+ parent->datalen - 1) &&
bssid_index == ieee80211_mle_get_mld_id(parent->data + 1)) {
if (!cfg80211_copy_elem_with_frags(parent,
ie, ielen,
@@ -1071,6 +1074,7 @@ int cfg80211_scan(struct cfg80211_registered_device *rdev)
struct cfg80211_scan_request_int *request;
struct cfg80211_scan_request_int *rdev_req = rdev->scan_req;
u32 n_channels = 0, idx, i;
+ int err;
if (!(rdev->wiphy.flags & WIPHY_FLAG_SPLIT_SCAN_6GHZ)) {
rdev_req->req.first_part = true;
@@ -1100,8 +1104,14 @@ int cfg80211_scan(struct cfg80211_registered_device *rdev)
rdev_req->req.scan_6ghz = false;
rdev_req->req.first_part = true;
+ err = rdev_scan(rdev, request);
+ if (err) {
+ kfree(request);
+ return err;
+ }
+
rdev->int_scan_req = request;
- return rdev_scan(rdev, request);
+ return 0;
}
void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
@@ -1602,10 +1612,12 @@ struct cfg80211_bss *__cfg80211_get_bss(struct wiphy *wiphy,
const u8 *ssid, size_t ssid_len,
enum ieee80211_bss_type bss_type,
enum ieee80211_privacy privacy,
- u32 use_for)
+ u32 use_for,
+ struct netlink_ext_ack *extack)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
struct cfg80211_internal_bss *bss, *res = NULL;
+ bool expired = false, unusable = false;
unsigned long now = jiffies;
int bss_privacy;
@@ -1627,22 +1639,48 @@ struct cfg80211_bss *__cfg80211_get_bss(struct wiphy *wiphy,
continue;
if (!is_valid_ether_addr(bss->pub.bssid))
continue;
- if ((bss->pub.use_for & use_for) != use_for)
+ if (!is_bss(&bss->pub, bssid, ssid, ssid_len))
continue;
+
+ /*
+ * The identity checks above must all come first so that
+ * the expired/unusable classification below only ever
+ * applies to entries that actually match the request.
+ */
+
/* Don't get expired BSS structs */
if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) &&
- !atomic_read(&bss->hold))
+ !atomic_read(&bss->hold)) {
+ expired = true;
+ continue;
+ }
+
+ if ((bss->pub.use_for & use_for) != use_for) {
+ unusable = true;
continue;
- if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
- res = bss;
- bss_ref_get(rdev, res);
- break;
}
+
+ res = bss;
+ bss_ref_get(rdev, res);
+ break;
}
spin_unlock_bh(&rdev->bss_lock);
- if (!res)
+ if (!res) {
+ if (expired && unusable)
+ NL_SET_ERR_MSG(extack,
+ "BSS entries are expired or cannot be used for the requested operation");
+ else if (unusable)
+ NL_SET_ERR_MSG(extack,
+ "BSS cannot be used for the requested operation");
+ else if (expired)
+ NL_SET_ERR_MSG(extack,
+ "BSS entry in scan results is expired");
+ else
+ NL_SET_ERR_MSG(extack,
+ "BSS not found in scan results");
return NULL;
+ }
trace_cfg80211_return_bss(&res->pub);
return &res->pub;
}
@@ -2396,12 +2434,11 @@ drop:
return NULL;
}
-static const struct element
-*cfg80211_get_profile_continuation(const u8 *ie, size_t ielen,
- const struct element *mbssid_elem,
- const struct element *sub_elem)
+static bool cfg80211_iter_profile_continuation(const u8 *ie, size_t ielen,
+ const struct element **mbssid,
+ const struct element **sub_elem)
{
- const u8 *mbssid_end = mbssid_elem->data + mbssid_elem->datalen;
+ const u8 *mbssid_end = (*mbssid)->data + (*mbssid)->datalen;
const struct element *next_mbssid;
const struct element *next_sub;
@@ -2413,30 +2450,34 @@ static const struct element
* If it is not the last subelement in current MBSSID IE or there isn't
* a next MBSSID IE - profile is complete.
*/
- if ((sub_elem->data + sub_elem->datalen < mbssid_end - 1) ||
+ if (((*sub_elem)->data + (*sub_elem)->datalen < mbssid_end - 1) ||
!next_mbssid)
- return NULL;
+ return false;
- /* For any length error, just return NULL */
+ /* For any length error, just return false to stop iteration */
if (next_mbssid->datalen < 4)
- return NULL;
+ return false;
next_sub = (void *)&next_mbssid->data[1];
if (next_mbssid->data + next_mbssid->datalen <
next_sub->data + next_sub->datalen)
- return NULL;
+ return false;
if (next_sub->id != 0 || next_sub->datalen < 2)
- return NULL;
+ return false;
/*
* Check if the first element in the next sub element is a start
* of a new profile
*/
- return next_sub->data[0] == WLAN_EID_NON_TX_BSSID_CAP ?
- NULL : next_mbssid;
+ if (next_sub->data[0] == WLAN_EID_NON_TX_BSSID_CAP)
+ return false;
+
+ *mbssid = next_mbssid;
+ *sub_elem = next_sub;
+ return true;
}
size_t cfg80211_merge_profile(const u8 *ie, size_t ielen,
@@ -2445,23 +2486,20 @@ size_t cfg80211_merge_profile(const u8 *ie, size_t ielen,
u8 *merged_ie, size_t max_copy_len)
{
size_t copied_len = sub_elem->datalen;
- const struct element *next_mbssid;
if (sub_elem->datalen > max_copy_len)
return 0;
memcpy(merged_ie, sub_elem->data, sub_elem->datalen);
- while ((next_mbssid = cfg80211_get_profile_continuation(ie, ielen,
- mbssid_elem,
- sub_elem))) {
- const struct element *next_sub = (void *)&next_mbssid->data[1];
-
- if (copied_len + next_sub->datalen > max_copy_len)
+ while (cfg80211_iter_profile_continuation(ie, ielen,
+ &mbssid_elem,
+ &sub_elem)) {
+ if (copied_len + sub_elem->datalen > max_copy_len)
break;
- memcpy(merged_ie + copied_len, next_sub->data,
- next_sub->datalen);
- copied_len += next_sub->datalen;
+ memcpy(merged_ie + copied_len, sub_elem->data,
+ sub_elem->datalen);
+ copied_len += sub_elem->datalen;
}
return copied_len;
@@ -2600,7 +2638,9 @@ ssize_t cfg80211_defragment_element(const struct element *elem, const u8 *ies,
ssize_t copied;
u8 elem_datalen;
- if (!elem)
+ if (!elem || (const u8 *)elem < ies ||
+ (const u8 *)elem + sizeof(*elem) > ies + ieslen ||
+ (const u8 *)elem + sizeof(*elem) + elem->datalen > ies + ieslen)
return -EINVAL;
/* elem might be invalid after the memmove */
@@ -3299,14 +3339,15 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
bssid = ext->u.s1g_beacon.sa;
capability = le16_to_cpu(compat->compat_info);
beacon_interval = le16_to_cpu(compat->beacon_int);
+ tsf = le32_to_cpu(ext->u.s1g_beacon.timestamp);
+ tsf |= (u64)le32_to_cpu(compat->tsf_completion) << 32;
} else {
bssid = mgmt->bssid;
beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
+ tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
}
- tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
-
if (ieee80211_is_probe_resp(mgmt->frame_control))
ftype = CFG80211_BSS_FTYPE_PRESP;
else if (ext)
@@ -3600,8 +3641,10 @@ int cfg80211_wext_siwscan(struct net_device *dev,
/* translate "Scan for SSID" request */
if (wreq) {
if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
- if (wreq->essid_len > IEEE80211_MAX_SSID_LEN)
- return -EINVAL;
+ if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) {
+ err = -EINVAL;
+ goto out;
+ }
memcpy(creq->req.ssids[0].ssid, wreq->essid,
wreq->essid_len);
creq->req.ssids[0].ssid_len = wreq->essid_len;