Still more fixes coming in, notably:

- ath11k: avoid running out of stations on HW restart
  - mac80211:
    - drop too large fragmented MPDUs
    - mesh path handling fixes
    - validation improvements
    - reject CSA with bad 320 MHz bandwidth
  - cfg80211: fix RTS for single radio devices
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEpeA8sTs3M8SN2hR410qiO8sPaAAFAmq9AkAACgkQ10qiO8sP
 aAD8mBAAhYUPz08049fTa9JV2yYjuSpeQPk6G0GgEWCxCeAFN/2MvYYB+/dP1lST
 4hNUMq3b8JWh3n7jJopkpmFsiMRP2yslXq7osTrDuliuv2cTgRJiUaEF+jVplANQ
 kMPvOgeGhks9MAZSD5tLf2wBHNJ3hk7OR8YniUxymvSxh51yg4j4eFvklVgd1m5S
 5TJAQeT8iUkDjo08SHkhEab8D4tkkapdXJA4itnKD9kvT/jM/HPc7FjRdDE23zmD
 2jbF+oiUDfcUqQ81sBhCTUfU+MdeWLiNvMz6kvMOl9jhH2tBoVSaDgiL20vU9a4N
 1rdbHowwb6bGdl857q2ET+X5UhR3ygpF8wsUlAykJmKpCSk9KRfHSCOhd28CikIm
 sDJO9+gU3tqiGPjYH344XM5NGXs18QM/v6SVnsKxyEtDVK9sXc09JKcAMF1rM+0u
 YRIF7+Rl4cbfmO0ys7wKG7gJV48iZPFJbtFwU80aDizpfGk/eGkCLpEc3z4UROHL
 YfRpziOD2QNbj8MuGbRYUMQYudYcV7FCaBmbzRYrbtuyC0SYJwBquXOcG+GTJy6d
 tZ6ZahMc3o8oHjIxf9lfJ594DAw42ClL4PT/yhuP72TQR1oIoFq+pKYhLK5oc/Qt
 mIDjZ6LMzGd3Ob0pk0kVHvpjKq509sLUAXoW6k2pHSne5EU3YRs=
 =2Up4
 -----END PGP SIGNATURE-----

Merge tag 'wireless-2026-09-30' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless

Johannes Berg says:

====================
Still more fixes coming in, notably:
 - ath11k: avoid running out of stations on HW restart
 - mac80211:
   - drop too large fragmented MPDUs
   - mesh path handling fixes
   - validation improvements
   - reject CSA with bad 320 MHz bandwidth
 - cfg80211: fix RTS for single radio devices

* tag 'wireless-2026-09-30' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless: (27 commits)
  wifi: mac80211: fix slab-out-of-bounds read in ieee80211_monitor_select_queue()
  wifi: mac80211: reject invalid 320 MHz CSA bandwidth
  wifi: mac80211: set info->band for 802.3 encap offload frames
  wifi: mac80211: prevent AP VLAN tx from other interfaces
  wifi: cfg80211: preserve hidden-group beacon IE ownership
  wifi: mac80211: keep fallback association elements alive
  wifi: mac80211: shut down RX BA session timer on teardown
  wifi: mac80211: validate TX status rate metadata
  wifi: ath9k_htc: bound TX aggregation to MAX_TX_BUF_SIZE
  wifi: ath9k: reject short WMI command responses
  wifi: ath9k: Clean up device initialisation guards
  wifi: ath11k: reset ar->num_stations on hardware start
  wifi: cfg80211: fix RTS threshold setting for single-radio PHY
  wifi: mac80211: handle empty FILS association request payload
  wifi: mac80211: minstrel_ht: validate fixed rate index
  wifi: p54: validate firmware record lengths
  wifi: mac80211: fix mesh fast xmit path deletion UAF
  wifi: mac80211: drop oversized fragments to avoid extra_len overflow
  wifi: wlcore: Fix runtime PM leak in wlcore_remove()
  wifi: mac80211: drain PS delivery work during station teardown
  ...
====================

Link: https://patch.msgid.link/20260930124440.224799-3-johannes@sipsolutions.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-09-30 14:05:59 -07:00
commit be35a3e003
29 changed files with 395 additions and 99 deletions

View File

@ -6716,6 +6716,7 @@ static int ath11k_mac_op_start(struct ieee80211_hw *hw)
ar->num_started_vdevs = 0;
ar->num_created_vdevs = 0;
ar->num_peers = 0;
ar->num_stations = 0;
ar->allocated_vdev_map = 0;
/* Configure monitor status ring with default rx_filter to get rx status

View File

@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/math.h>
#include <linux/unaligned.h>
#include "htc.h"
@ -328,11 +329,14 @@ static int __hif_usb_tx(struct hif_device_usb *hif_dev)
tx_skb_cnt = min_t(u16, hif_dev->tx.tx_skb_cnt, MAX_TX_AGGR_NUM);
for (i = 0; i < tx_skb_cnt; i++) {
nskb = skb_peek(&hif_dev->tx.tx_skb_queue);
if (!nskb)
break;
if (tx_buf->offset + nskb->len + 4 > MAX_TX_BUF_SIZE)
break;
nskb = __skb_dequeue(&hif_dev->tx.tx_skb_queue);
/* Should never be NULL */
BUG_ON(!nskb);
hif_dev->tx.tx_skb_cnt--;
buf = tx_buf->buf;
@ -342,13 +346,8 @@ static int __hif_usb_tx(struct hif_device_usb *hif_dev)
*hdr++ = cpu_to_le16(ATH_USB_TX_STREAM_MODE_TAG);
buf += 4;
memcpy(buf, nskb->data, nskb->len);
tx_buf->len = nskb->len + 4;
if (i < (tx_skb_cnt - 1))
tx_buf->offset += (((tx_buf->len - 1) / 4) + 1) * 4;
if (i == (tx_skb_cnt - 1))
tx_buf->len += tx_buf->offset;
tx_buf->len = tx_buf->offset + nskb->len + 4;
tx_buf->offset += round_up(nskb->len + 4, 4);
__skb_queue_tail(&tx_buf->skb_queue, nskb);
TX_STAT_INC(hif_dev, skb_queued);

View File

@ -281,7 +281,6 @@ struct ath9k_htc_rxbuf {
struct ath9k_htc_rx {
struct list_head rxbuf;
spinlock_t rxbuflock;
bool initialized;
};
#define ATH9K_HTC_TX_CLEANUP_INTERVAL 50 /* ms */

View File

@ -910,6 +910,9 @@ static int ath9k_init_device(struct ath9k_htc_priv *priv,
ath9k_init_leds(priv);
ath9k_start_rfkill_poll(priv);
/* signal completion to ath9k_htc_rxep() and ath9k_wmi_event_tasklet() */
smp_store_release(&priv->initialized, true);
return 0;
err_world:
@ -966,10 +969,6 @@ int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev,
htc_handle->drv_priv = priv;
/* Allow ath9k_wmi_event_tasklet() to operate. */
smp_wmb();
priv->initialized = true;
return 0;
err_init:

View File

@ -1141,8 +1141,8 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb,
struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL;
unsigned long flags;
/* Check if ath9k_rx_init() completed. */
if (!data_race(priv->rx.initialized))
/* Check if ath9k_init_device() completed. */
if (!smp_load_acquire(&priv->initialized))
goto err;
spin_lock_irqsave(&priv->rx.rxbuflock, flags);
@ -1200,10 +1200,6 @@ int ath9k_rx_init(struct ath9k_htc_priv *priv)
list_add_tail(&rxbuf->list, &priv->rx.rxbuf);
}
/* Allow ath9k_htc_rxep() to operate. */
smp_wmb();
priv->rx.initialized = true;
return 0;
err:

View File

@ -146,6 +146,12 @@ void ath9k_wmi_event_tasklet(struct tasklet_struct *t)
unsigned long flags;
u16 cmd_id;
/* Check if ath9k_init_device() completed. */
if (!smp_load_acquire(&priv->initialized)) {
tasklet_schedule(t);
return;
}
do {
spin_lock_irqsave(&wmi->wmi_lock, flags);
skb = __skb_dequeue(&wmi->wmi_event_queue);
@ -155,12 +161,6 @@ void ath9k_wmi_event_tasklet(struct tasklet_struct *t)
}
spin_unlock_irqrestore(&wmi->wmi_lock, flags);
/* Check if ath9k_htc_probe_device() completed. */
if (!data_race(priv->initialized)) {
kfree_skb(skb);
continue;
}
hdr = (struct wmi_cmd_hdr *) skb->data;
cmd_id = be16_to_cpu(hdr->command_id);
wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr));
@ -206,9 +206,16 @@ static void ath9k_wmi_rsp_callback(struct wmi *wmi, struct sk_buff *skb)
{
skb_pull(skb, sizeof(struct wmi_cmd_hdr));
if (wmi->cmd_rsp_buf != NULL && wmi->cmd_rsp_len != 0)
if (wmi->cmd_rsp_buf && wmi->cmd_rsp_len) {
if (skb->len < wmi->cmd_rsp_len) {
wmi->cmd_rsp_status = -EMSGSIZE;
goto complete;
}
memcpy(wmi->cmd_rsp_buf, skb->data, wmi->cmd_rsp_len);
}
wmi->cmd_rsp_status = 0;
complete:
complete(&wmi->cmd_wait);
}
@ -300,6 +307,7 @@ static int ath9k_wmi_cmd_issue(struct wmi *wmi,
/* record the rsp buffer and length */
wmi->cmd_rsp_buf = rsp_buf;
wmi->cmd_rsp_len = rsp_len;
wmi->cmd_rsp_status = 0;
wmi->last_seq_id = wmi->tx_seq_id;
spin_unlock_irqrestore(&wmi->wmi_lock, flags);
@ -356,9 +364,13 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
return -ETIMEDOUT;
}
spin_lock_irqsave(&wmi->wmi_lock, flags);
ret = wmi->cmd_rsp_status;
spin_unlock_irqrestore(&wmi->wmi_lock, flags);
mutex_unlock(&wmi->op_mutex);
return 0;
return ret;
out:
ath_dbg(common, WMI, "WMI failure for: %s\n", wmi_cmd_to_name(cmd_id));

View File

@ -157,6 +157,7 @@ struct wmi {
u16 tx_seq_id;
u8 *cmd_rsp_buf;
u32 cmd_rsp_len;
int cmd_rsp_status;
bool stopped;
struct list_head pending_tx_events;

View File

@ -3807,6 +3807,7 @@ il3945_pci_remove(struct pci_dev *pdev)
il_free_channel_map(il);
il_free_geos(il);
il_eeprom_free(il);
kfree(il->scan_cmd);
dev_kfree_skb(il->beacon_skb);
ieee80211_free_hw(il->hw);

View File

@ -52,6 +52,13 @@ int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
u32 code = le32_to_cpu(bootrec->code);
switch (code) {
case BR_CODE_COMPONENT_ID:
if (len < sizeof(struct bootrec_comp_id) /
sizeof(*bootrec->data)) {
wiphy_err(priv->hw->wiphy,
"firmware component ID is too short\n");
return -EINVAL;
}
priv->fw_interface = be32_to_cpup((__be32 *)
bootrec->data);
switch (priv->fw_interface) {
@ -72,16 +79,41 @@ int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
}
break;
case BR_CODE_COMPONENT_VERSION:
if (len < DIV_ROUND_UP(sizeof(struct bootrec_comp_ver),
sizeof(*bootrec->data))) {
wiphy_err(priv->hw->wiphy,
"firmware component version is too short\n");
return -EINVAL;
}
/* 24 bytes should be enough for all firmwares */
if (strnlen((unsigned char *) bootrec->data, 24) < 24)
fw_version = (unsigned char *) bootrec->data;
if (strnlen((unsigned char *)bootrec->data,
sizeof(struct bootrec_comp_ver)) <
sizeof(struct bootrec_comp_ver))
fw_version = (unsigned char *)bootrec->data;
break;
case BR_CODE_DESCR: {
struct bootrec_desc *desc =
(struct bootrec_desc *)bootrec->data;
priv->rx_start = le32_to_cpu(desc->rx_start);
/* FIXME add sanity checking */
priv->rx_end = le32_to_cpu(desc->rx_end) - 0x3500;
u32 rx_start, rx_end;
/* 0xa is the shortest descriptor in supported firmware. */
if (len < 0xa) {
wiphy_err(priv->hw->wiphy,
"firmware descriptor is too short\n");
return -EINVAL;
}
rx_start = le32_to_cpu(desc->rx_start);
rx_end = le32_to_cpu(desc->rx_end);
if (rx_end < 0x3500 || rx_end - 0x3500 <= rx_start) {
wiphy_err(priv->hw->wiphy,
"firmware descriptor has invalid RX range\n");
return -EINVAL;
}
priv->rx_start = rx_start;
priv->rx_end = rx_end - 0x3500;
priv->headroom = desc->headroom;
priv->tailroom = desc->tailroom;
priv->privacy_caps = desc->privacy_caps;

View File

@ -207,9 +207,11 @@ int wfx_hif_read_mib(struct wfx_dev *wdev, int vif_id, u16 mib_id, void *val, si
dev_warn(wdev->dev, "%s: confirmation mismatch request\n", __func__);
ret = -EIO;
}
if (ret == -ENOMEM)
if (!ret && le16_to_cpu(reply->length) > val_len) {
dev_err(wdev->dev, "buffer is too small to receive %s (%zu < %d)\n",
wfx_get_mib_name(mib_id), val_len, le16_to_cpu(reply->length));
ret = -EIO;
}
if (!ret)
memcpy(val, &reply->mib_data, le16_to_cpu(reply->length));
else

View File

@ -344,6 +344,7 @@ static int cw1200_bh_tx_helper(struct cw1200_common *priv,
int *tx_burst)
{
size_t tx_len;
size_t aligned_len;
u8 *data;
int ret;
struct wsm_hdr *wsm;
@ -376,9 +377,14 @@ static int cw1200_bh_tx_helper(struct cw1200_common *priv,
atomic_inc(&priv->bh_tx);
tx_len = priv->hwbus_ops->align_size(
aligned_len = priv->hwbus_ops->align_size(
priv->hwbus_priv, tx_len);
/* Zero the bus alignment padding so no stale data is sent. */
if (aligned_len > tx_len)
memset(data + tx_len, 0, aligned_len - tx_len);
tx_len = aligned_len;
/* Check if not exceeding CW1200 capabilities */
if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE))
pr_debug("Write aligned len: %zu\n", tx_len);

View File

@ -14,6 +14,7 @@
#include "wsm.h"
#include "bh.h"
#include "sta.h"
#include "hwbus.h"
#include "debug.h"
#define CW1200_INVALID_RATE_ID (0xFF)
@ -716,6 +717,7 @@ void cw1200_tx(struct ieee80211_hw *dev,
struct ieee80211_sta *sta;
struct wsm_tx *wsm;
bool tid_update = false;
size_t pad_len;
u8 flags = 0;
int ret;
@ -764,6 +766,18 @@ void cw1200_tx(struct ieee80211_hw *dev,
sta = t.sta;
/*
* The bus transfer is padded up to the bus alignment and the
* padding is transferred from the frame buffer. Make sure the
* buffer has room for the padding so the device never receives
* data past the end of the frame.
*/
pad_len = priv->hwbus_ops->align_size(priv->hwbus_priv, skb->len) -
skb->len;
if (skb_tailroom(skb) < pad_len &&
pskb_expand_head(skb, 0, pad_len, GFP_ATOMIC))
goto drop;
spin_lock_bh(&priv->ps_state_lock);
{
tid_update = cw1200_tx_h_pm_state(priv, &t);

View File

@ -6809,8 +6809,10 @@ void wlcore_remove(struct platform_device *pdev)
if (pdev_data->family && pdev_data->family->nvs_name)
wait_for_completion(&wl->nvs_loading_complete);
if (!wl->initialized)
if (!wl->initialized) {
pm_runtime_put_noidle(wl->dev);
return;
}
if (wl->wakeirq >= 0) {
dev_pm_clear_wake_irq(wl->dev);

View File

@ -102,7 +102,7 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
if (!tid_rx)
return;
timer_delete_sync(&tid_rx->session_timer);
timer_shutdown_sync(&tid_rx->session_timer);
/* make sure ieee80211_sta_reorder_release() doesn't re-arm the timer */
spin_lock_bh(&tid_rx->reorder_lock);

View File

@ -231,6 +231,13 @@ int ieee80211_chanctx_refcount(struct ieee80211_local *local,
return num;
}
static bool
ieee80211_chanctx_has_replace_partner(struct ieee80211_chanctx *ctx)
{
return ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED &&
ctx->replace_ctx;
}
static int ieee80211_num_chanctx(struct ieee80211_local *local, int radio_idx)
{
struct ieee80211_chanctx *ctx;
@ -1405,6 +1412,7 @@ void ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link)
{
struct ieee80211_sub_if_data *sdata = link->sdata;
struct ieee80211_chanctx *ctx = link->reserved_chanctx;
struct ieee80211_chanctx *old_ctx = NULL;
lockdep_assert_wiphy(sdata->local->hw.wiphy);
@ -1418,6 +1426,7 @@ void ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link)
if (WARN_ON(!ctx->replace_ctx))
return;
old_ctx = ctx->replace_ctx;
WARN_ON(ctx->replace_ctx->replace_state !=
IEEE80211_CHANCTX_WILL_BE_REPLACED);
WARN_ON(ctx->replace_ctx->replace_ctx != ctx);
@ -1428,6 +1437,11 @@ void ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link)
list_del_rcu(&ctx->list);
kfree_rcu(ctx, rcu_head);
if (ieee80211_chanctx_refcount(sdata->local,
old_ctx) == 0)
ieee80211_free_chanctx(sdata->local, old_ctx,
false);
} else {
ieee80211_free_chanctx(sdata->local, ctx, false);
}
@ -1707,7 +1721,8 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link)
ieee80211_check_fast_xmit_iface(sdata);
if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
if (ieee80211_chanctx_refcount(local, old_ctx) == 0 &&
!ieee80211_chanctx_has_replace_partner(old_ctx))
ieee80211_free_chanctx(local, old_ctx, false);
ieee80211_recalc_chanctx_min_def(local, new_ctx);
@ -1772,7 +1787,8 @@ ieee80211_link_use_reserved_assign(struct ieee80211_link_data *link)
}
static bool
ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link)
ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link,
struct ieee80211_chanctx *ctx)
{
struct ieee80211_sub_if_data *sdata = link->sdata;
struct ieee80211_chanctx *old_ctx, *new_ctx;
@ -1782,6 +1798,9 @@ ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link)
new_ctx = link->reserved_chanctx;
old_ctx = ieee80211_link_get_chanctx(link);
if (new_ctx != ctx)
return false;
if (!old_ctx)
return false;
@ -1794,6 +1813,12 @@ ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link)
if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
return false;
if (new_ctx->replace_ctx != old_ctx)
return false;
if (old_ctx->replace_ctx != new_ctx)
return false;
return true;
}
@ -1823,7 +1848,8 @@ static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local,
}
for_each_chanctx_user_reserved(local, ctx, &iter) {
if (!ieee80211_link_has_in_place_reservation(iter.link))
if (!ieee80211_link_has_in_place_reservation(iter.link,
ctx))
continue;
old_ctx = ieee80211_link_get_chanctx(iter.link);
@ -1925,7 +1951,9 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
for_each_chanctx_user_assigned(local, ctx->replace_ctx, &iter) {
n_assigned++;
if (iter.link && iter.link->reserved_chanctx) {
if (iter.link &&
ieee80211_link_has_in_place_reservation(iter.link,
ctx)) {
n_reserved++;
if (iter.link->reserved_ready)
n_ready++;
@ -1950,7 +1978,8 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
use_reserved:
ctx->conf.radar_enabled = false;
for_each_chanctx_user_reserved(local, ctx, &iter) {
if (ieee80211_link_has_in_place_reservation(iter.link) &&
if (ieee80211_link_has_in_place_reservation(iter.link,
ctx) &&
!iter.link->reserved_ready)
return -EAGAIN;
@ -1991,7 +2020,8 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
}
for_each_chanctx_user_reserved(local, ctx, &iter) {
if (!ieee80211_link_has_in_place_reservation(iter.link))
if (!ieee80211_link_has_in_place_reservation(iter.link,
ctx))
continue;
ieee80211_chan_bw_change(local,
@ -2040,7 +2070,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
struct ieee80211_bss_conf *link_conf = link->conf;
u64 changed = 0;
if (!ieee80211_link_has_in_place_reservation(link))
if (!ieee80211_link_has_in_place_reservation(link, ctx))
continue;
rcu_assign_pointer(link_conf->chanctx_conf,
@ -2091,7 +2121,8 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
for_each_chanctx_user_reserved(local, ctx, &iter) {
struct ieee80211_link_data *link = iter.link;
if (WARN_ON(ieee80211_link_has_in_place_reservation(link)))
if (WARN_ON(ieee80211_link_has_in_place_reservation(link,
ctx)))
continue;
if (!link->reserved_ready)
@ -2176,7 +2207,8 @@ void __ieee80211_link_release_channel(struct ieee80211_link_data *link,
}
ieee80211_assign_link_chanctx(link, NULL, false);
if (ieee80211_chanctx_refcount(local, ctx) == 0)
if (ieee80211_chanctx_refcount(local, ctx) == 0 &&
!ieee80211_chanctx_has_replace_partner(ctx))
ieee80211_free_chanctx(local, ctx, skip_idle_recalc);
link->radar_required = false;

View File

@ -108,6 +108,7 @@ typedef unsigned int __bitwise ieee80211_rx_result;
R(RX_DROP_U_UNHANDLED_PREQ) \
R(RX_DROP_U_UNHANDLED_MGMT_STYPE) \
R(RX_DROP_U_NO_LINK) \
R(RX_DROP_U_DEFRAG_OVERFLOW) \
/* this line for the trailing \ - add before this */
/* having two enums allows for checking ieee80211_rx_result use with sparse */

View File

@ -89,6 +89,14 @@ static int aes_siv_encrypt(const u8 *key, size_t key_len,
if (res)
return res;
/* With an empty plaintext there is no ciphertext to produce and
* the S2V result is the complete output.
*/
if (!plain_len) {
memcpy(out, v, AES_BLOCK_SIZE);
return 0;
}
/* Use a temporary buffer of the plaintext to handle need for
* overwriting this during AES-CTR.
*/

View File

@ -2475,7 +2475,7 @@ static inline bool ieee80211_require_encrypted_assoc(__le16 fc,
/* sta_out needs to be checked for ERR_PTR() before using */
int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb,
struct sta_info **sta_out);
struct sta_info **sta_out, bool bss);
static inline void
ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,

View File

@ -998,8 +998,8 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev,
len_rthdr = ieee80211_get_radiotap_len(skb->data);
hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr);
if (skb->len < len_rthdr + 2 ||
skb->len < len_rthdr + ieee80211_hdrlen(hdr->frame_control))
if (skb_headlen(skb) < len_rthdr + 2 ||
skb_headlen(skb) < len_rthdr + ieee80211_hdrlen(hdr->frame_control))
return 0; /* doesn't matter, frame will be dropped */
return ieee80211_select_queue_80211(sdata, skb, hdr);

View File

@ -577,6 +577,12 @@ void mesh_fast_tx_cache(struct ieee80211_sub_if_data *sdata,
goto unlock_sta;
spin_lock(&cache->walk_lock);
if ((READ_ONCE(mpath->flags) & MESH_PATH_DELETED) ||
(mppath && (READ_ONCE(mppath->flags) & MESH_PATH_DELETED))) {
kfree(entry);
goto unlock_cache;
}
prev = rhashtable_lookup_get_insert_fast(&cache->rht,
&entry->rhash,
fast_tx_rht_params);
@ -694,8 +700,10 @@ struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata,
return ERR_PTR(-ENOSPC);
new_mpath = mesh_path_new(sdata, dst, GFP_ATOMIC);
if (!new_mpath)
if (!new_mpath) {
atomic_dec(&sdata->u.mesh.mpaths);
return ERR_PTR(-ENOMEM);
}
tbl = &sdata->u.mesh.mesh_paths;
spin_lock_bh(&tbl->walk_lock);
@ -708,6 +716,7 @@ struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata,
if (mpath) {
kfree(new_mpath);
atomic_dec(&sdata->u.mesh.mpaths);
if (IS_ERR(mpath))
return mpath;
@ -733,10 +742,15 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata,
if (is_multicast_ether_addr(dst))
return -EOPNOTSUPP;
if (!atomic_add_unless(&sdata->u.mesh.mpaths, 1, MESH_MAX_MPATHS))
return -ENOSPC;
new_mpath = mesh_path_new(sdata, dst, GFP_ATOMIC);
if (!new_mpath)
if (!new_mpath) {
atomic_dec(&sdata->u.mesh.mpaths);
return -ENOMEM;
}
memcpy(new_mpath->mpp, mpp, ETH_ALEN);
tbl = &sdata->u.mesh.mpp_paths;
@ -749,10 +763,12 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata,
hlist_add_head_rcu(&new_mpath->walk_list, &tbl->walk_head);
spin_unlock_bh(&tbl->walk_lock);
if (ret)
if (ret) {
kfree(new_mpath);
else
atomic_dec(&sdata->u.mesh.mpaths);
} else {
mesh_fast_tx_flush_addr(sdata, dst);
}
sdata->u.mesh.mpp_paths_generation++;
return ret;
@ -798,7 +814,8 @@ static void mesh_path_free_rcu(struct mesh_table *tbl,
struct ieee80211_sub_if_data *sdata = mpath->sdata;
spin_lock_bh(&mpath->state_lock);
mpath->flags |= MESH_PATH_RESOLVING | MESH_PATH_DELETED;
WRITE_ONCE(mpath->flags,
mpath->flags | MESH_PATH_RESOLVING | MESH_PATH_DELETED);
mesh_gate_del(tbl, mpath);
spin_unlock_bh(&mpath->state_lock);
timer_shutdown_sync(&mpath->timer);
@ -812,6 +829,9 @@ static void __mesh_path_del(struct mesh_table *tbl, struct mesh_path *mpath)
{
hlist_del_rcu(&mpath->walk_list);
rhashtable_remove_fast(&tbl->rhead, &mpath->rhash, mesh_rht_params);
spin_lock_bh(&mpath->state_lock);
WRITE_ONCE(mpath->flags, mpath->flags | MESH_PATH_DELETED);
spin_unlock_bh(&mpath->state_lock);
if (tbl == &mpath->sdata->u.mesh.mpp_paths)
mesh_fast_tx_flush_addr(mpath->sdata, mpath->dst);
else

View File

@ -5877,6 +5877,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
bool is_s1g = cbss->channel->band == NL80211_BAND_S1GHZ;
const struct cfg80211_bss_ies *bss_ies = NULL;
struct ieee802_11_elems *bss_elems = NULL;
struct ieee80211_supported_band *sband;
struct ieee802_11_elems *elems;
u16 capab_info;
@ -6007,7 +6008,6 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
(is_5ghz && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_VHT &&
(!elems->vht_cap_elem || !elems->vht_operation)))) {
const struct cfg80211_bss_ies *ies;
struct ieee802_11_elems *bss_elems;
rcu_read_lock();
ies = rcu_dereference(cbss->ies);
@ -6069,7 +6069,6 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
"AP bug: VHT operation missing from AssocResp\n");
}
}
kfree(bss_elems);
}
/*
@ -6342,6 +6341,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
ret = true;
out:
kfree(elems);
kfree(bss_elems);
kfree(bss_ies);
return ret;
}

View File

@ -7,6 +7,7 @@
#include <linux/types.h>
#include <linux/skbuff.h>
#include <linux/debugfs.h>
#include <linux/limits.h>
#include <linux/random.h>
#include <linux/moduleparam.h>
#include <linux/ieee80211.h>
@ -1193,6 +1194,54 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
mi->sample_time = jiffies;
}
/*
* Check whether an HT/VHT rate from a TX status entry maps to an entry
* in the minstrel_ht rate tables. Values that cannot be represented
* there must not be used for indexing mi->groups[] and the MCS groups.
*/
static bool
minstrel_ht_txstat_rate_valid(struct ieee80211_tx_rate *rate)
{
unsigned int bw;
if (!(rate->flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS)))
return true;
if (rate->flags & IEEE80211_TX_RC_MCS) {
/* minstrel_ht supports up to MINSTREL_MAX_STREAMS streams */
return rate->idx < MINSTREL_MAX_STREAMS * 8;
}
/* minstrel_ht has no VHT groups for 160 MHz and wider */
bw = !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) +
2 * !!(rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH);
if ((rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH) || bw > BW_80)
return false;
return ieee80211_rate_get_vht_nss(rate) <= MINSTREL_MAX_STREAMS &&
ieee80211_rate_get_vht_mcs(rate) < MCS_GROUP_RATES;
}
static bool
minstrel_ht_ri_txstat_rate_valid(struct rate_info *rate)
{
if (!(rate->flags & (RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_VHT_MCS)))
return true;
if (rate->flags & RATE_INFO_FLAGS_MCS) {
/* minstrel_ht supports up to MINSTREL_MAX_STREAMS streams */
return rate->mcs < MINSTREL_MAX_STREAMS * 8;
}
/* minstrel_ht has VHT groups only for 20/40/80 MHz */
if (rate->bw != RATE_INFO_BW_20 && rate->bw != RATE_INFO_BW_40 &&
rate->bw != RATE_INFO_BW_80)
return false;
return rate->nss <= MINSTREL_MAX_STREAMS &&
rate->mcs < MCS_GROUP_RATES;
}
static bool
minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
struct ieee80211_tx_rate *rate)
@ -1205,9 +1254,8 @@ minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
if (!rate->count)
return false;
if (rate->flags & IEEE80211_TX_RC_MCS ||
rate->flags & IEEE80211_TX_RC_VHT_MCS)
return true;
if (rate->flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS))
return minstrel_ht_txstat_rate_valid(rate);
for (i = 0; i < ARRAY_SIZE(mp->cck_rates); i++)
if (rate->idx == mp->cck_rates[i])
@ -1235,9 +1283,9 @@ minstrel_ht_ri_txstat_valid(struct minstrel_priv *mp,
if (!rate_status->try_count)
return false;
if (rate_status->rate_idx.flags & RATE_INFO_FLAGS_MCS ||
rate_status->rate_idx.flags & RATE_INFO_FLAGS_VHT_MCS)
return true;
if (rate_status->rate_idx.flags &
(RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_VHT_MCS))
return minstrel_ht_ri_txstat_rate_valid(&rate_status->rate_idx);
for (i = 0; i < ARRAY_SIZE(mp->cck_rates); i++) {
if (rate_status->rate_idx.legacy ==
@ -1947,14 +1995,39 @@ minstrel_ht_alloc(struct ieee80211_hw *hw)
}
#ifdef CONFIG_MAC80211_DEBUGFS
static int minstrel_ht_fixed_rate_idx_get(void *data, u64 *val)
{
*val = *(u32 *)data;
return 0;
}
static int minstrel_ht_fixed_rate_idx_set(void *data, u64 val)
{
u32 idx = val;
/* U32_MAX is the default and keeps fixed rate processing disabled */
if (val != U32_MAX &&
(val > U16_MAX ||
MI_RATE_GROUP(idx) >= ARRAY_SIZE(minstrel_mcs_groups) ||
MI_RATE_IDX(idx) >= MCS_GROUP_RATES))
return -EINVAL;
*(u32 *)data = idx;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(minstrel_ht_fixed_rate_idx_fops,
minstrel_ht_fixed_rate_idx_get,
minstrel_ht_fixed_rate_idx_set, "%llu\n");
static void minstrel_ht_add_debugfs(struct ieee80211_hw *hw, void *priv,
struct dentry *debugfsdir)
{
struct minstrel_priv *mp = priv;
mp->fixed_rate_idx = (u32) -1;
debugfs_create_u32("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir,
&mp->fixed_rate_idx);
debugfs_create_file("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir,
&mp->fixed_rate_idx, &minstrel_ht_fixed_rate_idx_fops);
}
#endif

View File

@ -2499,6 +2499,12 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
}
skb_pull(rx->skb, ieee80211_hdrlen(fc));
if (unlikely((u32)entry->extra_len + rx->skb->len > U16_MAX)) {
I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
__skb_queue_purge(&entry->skb_list);
return RX_DROP_U_DEFRAG_OVERFLOW;
}
__skb_queue_tail(&entry->skb_list, rx->skb);
entry->last_frag = frag;
entry->extra_len += rx->skb->len;

View File

@ -111,8 +111,8 @@ validate_chandef_by_ht_vht_oper(struct ieee80211_sub_if_data *sdata,
switch (chan_width) {
case NL80211_CHAN_WIDTH_320:
WARN_ON(1);
break;
chandef->chan = NULL;
return;
case NL80211_CHAN_WIDTH_160:
vht_oper.chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
vht_oper.center_freq_seg1_idx = vht_oper.center_freq_seg0_idx;

View File

@ -137,6 +137,8 @@ static void __cleanup_single_sta(struct sta_info *sta)
struct ieee80211_local *local = sdata->local;
struct ps_data *ps;
cancel_work_sync(&sta->drv_deliver_wk);
if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
test_sta_flag(sta, WLAN_STA_PS_DRIVER) ||
test_sta_flag(sta, WLAN_STA_PS_DELIVER)) {
@ -166,8 +168,6 @@ static void __cleanup_single_sta(struct sta_info *sta)
if (ieee80211_vif_is_mesh(&sdata->vif))
mesh_sta_cleanup(sta);
cancel_work_sync(&sta->drv_deliver_wk);
/*
* Destroy aggregation state here. It would be nice to wait for the
* driver to finish aggregation stop and then clean up, but for now
@ -1582,6 +1582,8 @@ static void __sta_info_destroy_part2(struct sta_info *sta, bool recalc)
sta->dead = true;
cancel_work_sync(&sta->drv_deliver_wk);
local->num_sta--;
local->sta_generation++;

View File

@ -1164,6 +1164,73 @@ void ieee80211_tx_status_skb(struct ieee80211_hw *hw, struct sk_buff *skb)
}
EXPORT_SYMBOL(ieee80211_tx_status_skb);
/*
* Check whether the HT/VHT rate information in a TX status entry is
* valid for its encoding. This is not specific to any rate control
* algorithm; all status consumers rely on the values being sane.
*/
static bool ieee80211_tx_status_rate_info_valid(const struct rate_info *rate)
{
if (rate->flags & RATE_INFO_FLAGS_MCS)
return rate->mcs < 32;
if (rate->flags & RATE_INFO_FLAGS_VHT_MCS)
return rate->nss >= 1 && rate->nss <= 8 && rate->mcs <= 11;
return true;
}
static bool
ieee80211_tx_status_tx_rate_valid(const struct ieee80211_tx_rate *rate)
{
if (rate->flags & IEEE80211_TX_RC_MCS)
return rate->idx < 32;
if (rate->flags & IEEE80211_TX_RC_VHT_MCS)
return ieee80211_rate_get_vht_mcs(rate) <= 11;
return true;
}
/*
* Drop TX status rate entries that don't describe a valid rate. The
* status information is used by rate control and by other mac80211
* code, and a malformed entry must not be able to corrupt state beyond
* the driver that reported it.
*/
static void
ieee80211_tx_status_drop_invalid_rates(struct ieee80211_tx_status *status)
{
int i;
for (i = 0; i < status->n_rates; i++) {
struct ieee80211_rate_status *rs = &status->rates[i];
if (ieee80211_tx_status_rate_info_valid(&rs->rate_idx))
continue;
rs->try_count = 0;
memset(&rs->rate_idx, 0, sizeof(rs->rate_idx));
}
if (!status->info)
return;
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
struct ieee80211_tx_rate *rate;
rate = &status->info->status.rates[i];
if (rate->idx < 0)
break;
if (ieee80211_tx_status_tx_rate_valid(rate))
continue;
rate->idx = -1;
rate->count = 0;
}
}
void ieee80211_tx_status_ext(struct ieee80211_hw *hw,
struct ieee80211_tx_status *status)
{
@ -1176,6 +1243,8 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw,
bool acked, noack_success, ack_signal_valid;
u16 tx_time_est;
ieee80211_tx_status_drop_invalid_rates(status);
if (pubsta) {
sta = container_of(pubsta, struct sta_info, sta);
@ -1300,6 +1369,7 @@ void ieee80211_tx_rate_update(struct ieee80211_hw *hw,
.sta = pubsta,
};
ieee80211_tx_status_drop_invalid_rates(&status);
rate_control_tx_status(local, &status);
if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL))

View File

@ -2091,7 +2091,7 @@ static bool ieee80211_validate_radiotap_len(struct sk_buff *skb)
(struct ieee80211_radiotap_header *)skb->data;
/* check for not even having the fixed radiotap header part */
if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header)))
if (unlikely(skb_headlen(skb) < sizeof(struct ieee80211_radiotap_header)))
return false; /* too short to be possibly valid */
/* is it a header version we can trust to find length from? */
@ -2099,7 +2099,7 @@ static bool ieee80211_validate_radiotap_len(struct sk_buff *skb)
return false; /* only version 0 is supported */
/* does the skb contain enough to deliver on the alleged length? */
if (unlikely(skb->len < ieee80211_get_radiotap_len(skb->data)))
if (unlikely(skb_headlen(skb) < ieee80211_get_radiotap_len(skb->data)))
return false; /* skb too short for claimed rt header extent */
return true;
@ -2388,13 +2388,13 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
skb_set_network_header(skb, len_rthdr);
skb_set_transport_header(skb, len_rthdr);
if (skb->len < len_rthdr + 2)
if (skb_headlen(skb) < len_rthdr + 2)
goto fail;
hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr);
hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (skb->len < len_rthdr + hdrlen)
if (skb_headlen(skb) < len_rthdr + hdrlen)
goto fail;
/*
@ -2402,7 +2402,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
* carrying a rfc1042 header
*/
if (ieee80211_is_data(hdr->frame_control) &&
skb->len >= len_rthdr + hdrlen + sizeof(rfc1042_header) + 2) {
skb_headlen(skb) >= len_rthdr + hdrlen + sizeof(rfc1042_header) + 2) {
u8 *payload = (u8 *)hdr + hdrlen;
if (ether_addr_equal(payload, rfc1042_header))
@ -2532,7 +2532,7 @@ static inline bool ieee80211_is_tdls_setup(struct sk_buff *skb)
int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb,
struct sta_info **sta_out)
struct sta_info **sta_out, bool bss)
{
struct sta_info *sta;
@ -2553,7 +2553,10 @@ int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
*sta_out = ERR_PTR(-ENOENT);
return 0;
}
sta = sta_info_get_bss(sdata, skb->data);
if (bss)
sta = sta_info_get_bss(sdata, skb->data);
else
sta = sta_info_get(sdata, skb->data);
break;
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
@ -4419,7 +4422,8 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
ieee80211_mesh_xmit_fast(sdata, skb, ctrl_flags))
goto out;
if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
if (ieee80211_lookup_ra_sta(sdata, skb, &sta,
skb->protocol == sdata->control_port_protocol))
goto out_free;
if (IS_ERR(sta))
@ -4731,6 +4735,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_tx_info *info;
struct ieee80211_local *local = sdata->local;
struct ieee80211_chanctx_conf *chanctx_conf;
struct tid_ampdu_tx *tid_tx = NULL;
struct sk_buff *seg, *next;
unsigned int skbs = 0, len = 0;
@ -4780,6 +4785,16 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data, u.ap);
/* MLD transmissions must not rely on the band */
if (!ieee80211_vif_is_mld(&sdata->vif)) {
chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
if (unlikely(!chanctx_conf)) {
kfree_skb_list(skb);
return;
}
info->band = chanctx_conf->def.chan->band;
}
info->flags |= IEEE80211_TX_CTL_HW_80211_ENCAP;
info->control.vif = &sdata->vif;
@ -4848,7 +4863,10 @@ static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
rcu_read_lock();
if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
if (unlikely(sdata->control_port_protocol == ehdr->h_proto))
goto skip_offload;
if (ieee80211_lookup_ra_sta(sdata, skb, &sta, false)) {
kfree_skb(skb);
goto out;
}
@ -4869,8 +4887,7 @@ static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
link = &sdata->deflink;
key = rcu_dereference(link->default_multicast_key);
} else if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded ||
!test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
sdata->control_port_protocol == ehdr->h_proto)) {
!test_sta_flag(sta, WLAN_STA_AUTHORIZED))) {
goto skip_offload;
} else {
key = rcu_dereference(sta->ptk[sta->ptk_idx]);
@ -4931,7 +4948,7 @@ ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
rcu_read_lock();
if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
if (ieee80211_lookup_ra_sta(sdata, skb, &sta, false)) {
kfree_skb(skb);
skb = ERR_PTR(-EINVAL);
goto out;
@ -5002,7 +5019,7 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
}
result = ieee80211_tx(sdata, NULL, skb, true);
} else if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
if (ieee80211_lookup_ra_sta(sdata, skb, &sta, true)) {
dev_kfree_skb(skb);
return true;
}
@ -6639,7 +6656,7 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
* AF_PACKET
*/
rcu_read_lock();
err = ieee80211_lookup_ra_sta(sdata, skb, &sta);
err = ieee80211_lookup_ra_sta(sdata, skb, &sta, true);
if (err) {
dev_kfree_skb(skb);
rcu_read_unlock();

View File

@ -4563,12 +4563,13 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
rdev->wiphy.retry_long = retry_long;
if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
rdev->wiphy.frag_threshold = frag_threshold;
if ((changed & WIPHY_PARAM_RTS_THRESHOLD) &&
old_radio_rts_threshold) {
if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
rdev->wiphy.rts_threshold = rts_threshold;
for (i = 0 ; i < rdev->wiphy.n_radio; i++)
rdev->wiphy.radio_cfg[i].rts_threshold =
rdev->wiphy.rts_threshold;
if (old_radio_rts_threshold) {
for (i = 0; i < rdev->wiphy.n_radio; i++)
rdev->wiphy.radio_cfg[i].rts_threshold =
rdev->wiphy.rts_threshold;
}
}
if (changed & WIPHY_PARAM_COVERAGE_CLASS)
rdev->wiphy.coverage_class = coverage_class;

View File

@ -1992,20 +1992,22 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,
return false;
}
old = rcu_access_pointer(known->pub.beacon_ies);
if (!new->pub.hidden_beacon_bss) {
old = rcu_access_pointer(known->pub.beacon_ies);
rcu_assign_pointer(known->pub.beacon_ies, new->pub.beacon_ies);
rcu_assign_pointer(known->pub.beacon_ies, new->pub.beacon_ies);
/* Override IEs if they were from a beacon before */
if (old == rcu_access_pointer(known->pub.ies))
rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies);
/* Override IEs if they were from a beacon before */
if (old == rcu_access_pointer(known->pub.ies))
rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies);
cfg80211_update_hidden_bsses(known,
rcu_access_pointer(new->pub.beacon_ies),
old);
cfg80211_update_hidden_bsses(known,
rcu_access_pointer(new->pub.beacon_ies),
old);
if (old)
kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
if (old)
kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
}
}
known->pub.beacon_interval = new->pub.beacon_interval;