aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2026-09-30 14:05:59 -0700
committerJakub Kicinski <kuba@kernel.org>2026-09-30 14:06:00 -0700
commitbe35a3e003941fc25b4e72d8d4fd44f8a98ac1af (patch)
tree7f4b26d1bde1d388ddd773a005e8dc131ff2fb65
parent99b43ede9e355ba35244cc9470bf1819774ce39d (diff)
parent6f63e919fe1e335b8abcb3a28bfd4804a98d875a (diff)
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>
-rw-r--r--drivers/net/wireless/ath/ath11k/mac.c1
-rw-r--r--drivers/net/wireless/ath/ath9k/hif_usb.c19
-rw-r--r--drivers/net/wireless/ath/ath9k/htc.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_init.c7
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_txrx.c8
-rw-r--r--drivers/net/wireless/ath/ath9k/wmi.c28
-rw-r--r--drivers/net/wireless/ath/ath9k/wmi.h1
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945-mac.c1
-rw-r--r--drivers/net/wireless/intersil/p54/fwio.c42
-rw-r--r--drivers/net/wireless/silabs/wfx/hif_tx.c4
-rw-r--r--drivers/net/wireless/st/cw1200/bh.c8
-rw-r--r--drivers/net/wireless/st/cw1200/txrx.c14
-rw-r--r--drivers/net/wireless/ti/wlcore/main.c4
-rw-r--r--net/mac80211/agg-rx.c2
-rw-r--r--net/mac80211/chan.c50
-rw-r--r--net/mac80211/drop.h1
-rw-r--r--net/mac80211/fils_aead.c8
-rw-r--r--net/mac80211/ieee80211_i.h2
-rw-r--r--net/mac80211/iface.c4
-rw-r--r--net/mac80211/mesh_pathtbl.c30
-rw-r--r--net/mac80211/mlme.c4
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c89
-rw-r--r--net/mac80211/rx.c6
-rw-r--r--net/mac80211/spectmgmt.c4
-rw-r--r--net/mac80211/sta_info.c6
-rw-r--r--net/mac80211/status.c70
-rw-r--r--net/mac80211/tx.c45
-rw-r--r--net/wireless/nl80211.c11
-rw-r--r--net/wireless/scan.c22
29 files changed, 394 insertions, 98 deletions
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index ae91b57c8422..c1fa42edd363 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -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
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index d3491ff08e6e..7d2f292e20c5 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -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_dequeue(&hif_dev->tx.tx_skb_queue);
+ nskb = skb_peek(&hif_dev->tx.tx_skb_queue);
+ if (!nskb)
+ break;
- /* Should never be NULL */
- BUG_ON(!nskb);
+ if (tx_buf->offset + nskb->len + 4 > MAX_TX_BUF_SIZE)
+ break;
+ nskb = __skb_dequeue(&hif_dev->tx.tx_skb_queue);
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);
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index 6c33e898b300..4d2b75d0212e 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -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 */
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
index 6de78ae85726..911eaa4f776a 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -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:
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index bed7ea2425a0..95f48c7dca15 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -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:
diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c
index 284e8c13b043..4a08babb6d9d 100644
--- a/drivers/net/wireless/ath/ath9k/wmi.c
+++ b/drivers/net/wireless/ath/ath9k/wmi.c
@@ -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));
diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h
index 5c3b710b8f31..0bb9d31f1a1c 100644
--- a/drivers/net/wireless/ath/ath9k/wmi.h
+++ b/drivers/net/wireless/ath/ath9k/wmi.h
@@ -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;
diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
index cbaf250626c5..f7ca182d1494 100644
--- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c
+++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
@@ -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);
diff --git a/drivers/net/wireless/intersil/p54/fwio.c b/drivers/net/wireless/intersil/p54/fwio.c
index a3d9053f043c..f028a7bfa734 100644
--- a/drivers/net/wireless/intersil/p54/fwio.c
+++ b/drivers/net/wireless/intersil/p54/fwio.c
@@ -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;
diff --git a/drivers/net/wireless/silabs/wfx/hif_tx.c b/drivers/net/wireless/silabs/wfx/hif_tx.c
index 9f403d275cb1..9efb108bd877 100644
--- a/drivers/net/wireless/silabs/wfx/hif_tx.c
+++ b/drivers/net/wireless/silabs/wfx/hif_tx.c
@@ -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
diff --git a/drivers/net/wireless/st/cw1200/bh.c b/drivers/net/wireless/st/cw1200/bh.c
index b034bab4b489..9367a981f1de 100644
--- a/drivers/net/wireless/st/cw1200/bh.c
+++ b/drivers/net/wireless/st/cw1200/bh.c
@@ -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);
diff --git a/drivers/net/wireless/st/cw1200/txrx.c b/drivers/net/wireless/st/cw1200/txrx.c
index 084d52b11f5b..e13695667a00 100644
--- a/drivers/net/wireless/st/cw1200/txrx.c
+++ b/drivers/net/wireless/st/cw1200/txrx.c
@@ -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);
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index edf6ca23c6c3..26aabf4ea127 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -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);
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 9629e00069a1..01d6703767c6 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -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);
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 75bb204ad743..9cba79507767 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -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 @@ out:
}
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 @@ use_reserved:
}
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 @@ use_reserved:
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 @@ use_reserved:
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;
diff --git a/net/mac80211/drop.h b/net/mac80211/drop.h
index f06a8aa905c5..f433ee17cce5 100644
--- a/net/mac80211/drop.h
+++ b/net/mac80211/drop.h
@@ -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 */
diff --git a/net/mac80211/fils_aead.c b/net/mac80211/fils_aead.c
index 293590976489..9c4f00d61981 100644
--- a/net/mac80211/fils_aead.c
+++ b/net/mac80211/fils_aead.c
@@ -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.
*/
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index d05f59467399..9374c2942489 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -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,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 889c32fd8de1..88942317fec8 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -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);
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index 03171cf00855..07a21dfa8674 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -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
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index f51167f0fc46..3c31c16860ab 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -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;