aboutsummaryrefslogtreecommitdiffstatshomepage
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
parentnet: microchip: vcap: stop scanning after deleting key field (diff)
parentwifi: mac80211: fix slab-out-of-bounds read in ieee80211_monitor_select_queue() (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;
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;
}
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index b73ef3adfcc5..45e7a7ea7690 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -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
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 5e26be8e27d8..7ead509304eb 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -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;
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
index 880f4625775d..f5e4970869b6 100644
--- a/net/mac80211/spectmgmt.c
+++ b/net/mac80211/spectmgmt.c
@@ -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;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index fdf00cbf49d8..e81cd155197b 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -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++;
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 3d811f652603..414979e2a1d9 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -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))
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 814399989b5e..d51f810dd2c5 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -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();
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 9fafd7673d85..ddce61ec016a 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -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;
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index caa9c6495f20..d35c826c4b1a 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -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;