From 35c5a5ea4418571997a7e7af3e7e8922b136f6e6 Mon Sep 17 00:00:00 2001 From: Weigang He Date: Thu, 22 Oct 2026 01:58:07 +1100 Subject: [PATCH 1/6] wifi: mac80211: reload TDLS header pointer after skb_linearize() ieee80211_process_tdls_channel_switch() sets tf = skb->data before it calls skb_linearize(), and reads tf->action_code afterwards. ieee80211_rx_h_data() queues TDLS channel switch requests and responses to the interface after pulling only offsetof(struct ieee80211_tdls_data, u) bytes, so the skb can still be non-linear when the interface work handles it. Drivers that build paged RX skbs do this for every frame that does not fit into the skb head, e.g. iwlwifi (mvm), which is also the only mac80211 driver that sets NL80211_FEATURE_TDLS_CHANNEL_SWITCH. If the remaining data does not fit into the tailroom, skb_linearize() moves the data to a new head and frees the old one, and the action code is then read from freed memory. Depending on what that memory holds by then, the frame is handled as before or ends in the WARN_ON_ONCE() of the default case. The frame only has to be a data frame with the TDLS ethertype from an authorized peer, e.g. relayed by the AP from another station in the BSS; the read happens before the sender is checked to be a TDLS peer. Load tf after skb_linearize(), as the request and response handlers already do. Found by static analysis tool CodeQL. Fixes: 8a4d32f30d11 ("mac80211: add TDLS channel-switch Rx flow") Cc: stable@vger.kernel.org Assisted-by: LLM codeql Signed-off-by: Weigang He Link: https://patch.msgid.link/20261021145807.3018338-1-geoffreyhe2@gmail.com Signed-off-by: Johannes Berg --- net/mac80211/tdls.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index f663d28d9209..71db07aa301b 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -2021,7 +2021,7 @@ void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { - struct ieee80211_tdls_data *tf = (void *)skb->data; + struct ieee80211_tdls_data *tf; struct wiphy *wiphy = sdata->local->hw.wiphy; lockdep_assert_wiphy(wiphy); @@ -2037,6 +2037,7 @@ ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata, * The packet/size was already validated by mac80211 Rx path, only look * at the action type. */ + tf = (void *)skb->data; switch (tf->action_code) { case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: ieee80211_process_tdls_channel_switch_req(sdata, skb); From 7ed5dce55fa8393972b1bf2bf5af1c818c5e7dc2 Mon Sep 17 00:00:00 2001 From: Jason Perlow Date: Wed, 21 Oct 2026 22:52:38 +0530 Subject: [PATCH 2/6] wifi: brcmfmac: map msgbuf RX packet IDs bidirectional Commit f0ba5fd51ff2 ("wifi: brcmfmac: Set DMA direction for msgbuf packet IDs") maps the RX packet ID buffers DMA_FROM_DEVICE. The BCM4364 firmware also reads posted RX buffers, so with VT-d translation enabled every such read faults ("PTE Read access is not set") and the firmware crashes and resets in a loop. Map the RX pool DMA_BIDIRECTIONAL again, as before that commit. TX keeps DMA_TO_DEVICE. Fixes: f0ba5fd51ff2 ("wifi: brcmfmac: Set DMA direction for msgbuf packet IDs") Cc: stable@vger.kernel.org Signed-off-by: Jason Perlow Signed-off-by: Aditya Garg Acked-by: Arend van Spriel Link: https://patch.msgid.link/20261021172238.10004-1-aditya.garg@linux.dev Signed-off-by: Johannes Berg --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c index 069ba7016654..ff47d4827722 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c @@ -1639,7 +1639,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) if (!msgbuf->tx_pktids) goto fail; msgbuf->rx_pktids = brcmf_msgbuf_init_pktids(NR_RX_PKTIDS, - DMA_FROM_DEVICE); + DMA_BIDIRECTIONAL); if (!msgbuf->rx_pktids) goto fail; From 48c0045366bec142d053f7cf76fb9dd7127cd231 Mon Sep 17 00:00:00 2001 From: Cosmin Ratiu Date: Wed, 21 Oct 2026 17:41:11 +0300 Subject: [PATCH 3/6] wifi: wext: Limit notifier event flushes to the device's netns With mlx5 netdev instance locking enabled, registering an IPoIB netdev calls the WEXT notifier while holding the instance mutex. Its call to wireless_nlevent_flush() takes net_rwsem to walk every namespace, completing the following lockdep dependency cycle: netdev instance mutex -> net_rwsem (read) -> GID table mutex -> event_handler_rwsem (read) -> netdev instance mutex RoCE GID enumeration takes the GID table mutex under net_rwsem. GID updates dispatch events under the table mutex, taking event_handler_rwsem. The IPoIB event handler takes the netdev instance mutex in ipoib_queue_work() to traverse child interfaces. The all-namespace walk originated in the single global worker servicing per-namespace WEXT queues. The ordering fixes later reused that routine in netdevice notifiers to prevent delayed WEXT RTM_NEWLINK messages from following RTM_DELLINK or reporting stale interface flags. A notifier only needs to drain the affected device's namespace; there is no reason for it to walk and flush all namespaces. Pass dev_net(dev) from both notifier callers and make the flush helper operate on that namespace. Keep the all-namespace walk and net_rwsem in the existing worker. This removes the instance-mutex -> net_rwsem dependency while retaining synchronous flushing from the notifiers. It may seem strange that the fix for a lock cycle across multiple subsystems lies in a completely unrelated part of the code, but this seemed the most justified lock cycle edge to break. Fixes: 8f7b00307bf1 ("net/mlx5e: Convert mlx5 netdevs to instance locking") Signed-off-by: Cosmin Ratiu Link: https://patch.msgid.link/20261021144111.611840-1-cratiu@nvidia.com Signed-off-by: Johannes Berg --- include/net/iw_handler.h | 8 +++++--- net/wireless/core.c | 2 +- net/wireless/wext-core.c | 33 +++++++++++++++++---------------- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/include/net/iw_handler.h b/include/net/iw_handler.h index b80e474cb0aa..632618d9ff93 100644 --- a/include/net/iw_handler.h +++ b/include/net/iw_handler.h @@ -201,6 +201,8 @@ #include /* IOCTL user space API */ #include +struct net; + /***************************** VERSION *****************************/ /* * This constant is used to know which version of the driver API is @@ -411,10 +413,10 @@ struct iw_spy_data { void wireless_send_event(struct net_device *dev, unsigned int cmd, union iwreq_data *wrqu, const char *extra); #ifdef CONFIG_WEXT_CORE -/* flush all previous wext events - if work is done from netdev notifiers */ -void wireless_nlevent_flush(void); +/* Flush previous wext events in this netns from netdev notifiers. */ +void wireless_nlevent_flush(struct net *net); #else -static inline void wireless_nlevent_flush(void) {} +static inline void wireless_nlevent_flush(struct net *net) {} #endif /* We may need a function to send a stream of events to user space. diff --git a/net/wireless/core.c b/net/wireless/core.c index cde3ca85494d..2f49ed14b26e 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -1878,7 +1878,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, return NOTIFY_DONE; } - wireless_nlevent_flush(); + wireless_nlevent_flush(dev_net(dev)); return NOTIFY_OK; } diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index db77912b3994..f09c589ff94c 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -342,32 +342,28 @@ static const int compat_event_type_size[] = { /* IW event code */ -void wireless_nlevent_flush(void) +void wireless_nlevent_flush(struct net *net) { struct sk_buff *skb; - struct net *net; - down_read(&net_rwsem); - for_each_net(net) { - while ((skb = skb_dequeue(&net->wext_nlevents))) - rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, - GFP_KERNEL); - } - up_read(&net_rwsem); + while ((skb = skb_dequeue(&net->wext_nlevents))) + rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL); } EXPORT_SYMBOL_GPL(wireless_nlevent_flush); static int wext_netdev_notifier_call(struct notifier_block *nb, unsigned long state, void *ptr) { + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + /* - * When a netdev changes state in any way, flush all pending messages - * to avoid them going out in a strange order, e.g. RTM_NEWLINK after - * RTM_DELLINK, or with IFF_UP after without IFF_UP during dev_close() - * or similar - all of which could otherwise happen due to delays from - * schedule_work(). + * When a netdev changes state in any way, flush all pending messages in + * its network namespace to avoid them going out in a strange order, + * e.g. RTM_NEWLINK after RTM_DELLINK, or with IFF_UP after without + * IFF_UP during dev_close() or similar - all of which could otherwise + * happen due to delays from schedule_work(). */ - wireless_nlevent_flush(); + wireless_nlevent_flush(dev_net(dev)); return NOTIFY_OK; } @@ -410,7 +406,12 @@ subsys_initcall(wireless_nlevent_init); /* Process events generated by the wireless layer or the driver. */ static void wireless_nlevent_process(struct work_struct *work) { - wireless_nlevent_flush(); + struct net *net; + + down_read(&net_rwsem); + for_each_net(net) + wireless_nlevent_flush(net); + up_read(&net_rwsem); } static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process); From 71f756a4c064b0312f46617c20893ecc17c332e4 Mon Sep 17 00:00:00 2001 From: Alex Kazinsky Date: Thu, 15 Oct 2026 11:20:05 +0400 Subject: [PATCH 4/6] wifi: rsi: Skip TX aggregation setup for non-QoS frames When transmitting a non-QoS unicast frame to a station in AP mode, rsi_core_xmit() assigns IEEE80211_NONQOS_TID (16) to tid. The station lookup succeeds, allowing tid to index start_tx_aggr[], which has only IEEE80211_NUM_TIDS (16) elements. This causes an out-of-bounds read and, if the value read is zero, an out-of-bounds write. Only access the aggregation state for tids below IEEE80211_NUM_TIDS, excluding the non-QoS sentinel from TX aggregation setup. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 19844c0a9a19 ("rsi: data and managemet path changes for AP mode") Cc: stable@vger.kernel.org Signed-off-by: Alex Kazinsky Link: https://patch.msgid.link/20261015072010.742369-1-a.kazinsky@ideco.ru Signed-off-by: Johannes Berg --- drivers/net/wireless/rsi/rsi_91x_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_core.c b/drivers/net/wireless/rsi/rsi_91x_core.c index ead4d4e04328..3281fc94e107 100644 --- a/drivers/net/wireless/rsi/rsi_91x_core.c +++ b/drivers/net/wireless/rsi/rsi_91x_core.c @@ -458,7 +458,7 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb) tx_params->sta_id = 0; } - if (rsta) { + if (rsta && tid < IEEE80211_NUM_TIDS) { /* Start aggregation if not done for this tid */ if (!rsta->start_tx_aggr[tid]) { rsta->start_tx_aggr[tid] = true; From 9d8ec0b5b5295bd23d979d1b2b905de25c36fff2 Mon Sep 17 00:00:00 2001 From: Zijing Yin Date: Thu, 15 Oct 2026 06:31:57 -0700 Subject: [PATCH 5/6] wifi: cfg80211: fix out-of-bounds read in valid_rule() valid_rule() validates a struct fwdb_rule from the regulatory database firmware file, but bounds checks only the first byte of the structure, the length field itself. It then uses that length, which comes straight out of the file, to decide whether the optional fields are present, and reads rule->wmm_ptr at offset 18 of the 20-byte structure once the length claims 20 bytes or more. A rule placed in the last bytes of the file therefore makes the parser read up to 19 bytes past the end of the firmware image. The image comes from vmalloc() in kernel_read_file(), so with the rule at the end of a page the read falls into the guard page and oopses, with no memory debugging enabled. A length of at least offsetofend(max_bw) but below offsetofend(wmm_ptr) takes the other branch, so valid_rule() returns true having read nothing out of bounds, and the database is accepted. regdb_query_country() does not re-check the rule, and reads rule->start, rule->end, rule->max_bw and rule->cac_timeout off the end of the kmemdup()ed copy. A malformed regulatory.db is parsed by cfg80211's own request_firmware_nowait() at init time, and on demand from user space through NL80211_CMD_RELOAD_REGDB, which needs CAP_NET_ADMIN but no wireless hardware. Both paths require write access to the firmware search path and CONFIG_CFG80211_REQUIRE_SIGNED_REGDB=n. Bound the whole declared rule, not just its length field. The existing check on sizeof(rule->len) is still needed, since rule->len has to be shown to be inside the file before it can be read, so the new test goes after it. A rule reaching past the end of the file is now rejected, which fails the database rather than reading out of bounds. That also gives regdb_query_country() the bound it already assumes. Fixes: 230ebaa189af ("cfg80211: read wmm rules from regulatory database") Cc: stable@vger.kernel.org Signed-off-by: Zijing Yin Link: https://patch.msgid.link/20261015133159.1499728-1-yzjaurora@gmail.com Signed-off-by: Johannes Berg --- net/wireless/reg.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 11665e0a7efc..44226687349e 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -679,6 +679,9 @@ static bool valid_rule(const u8 *data, unsigned int size, u16 rule_ptr) /* mandatory fields */ if (rule->len < offsetofend(struct fwdb_rule, max_bw)) return false; + /* the rule must lie entirely within the database */ + if ((u8 *)rule + rule->len > data + size) + return false; if (rule->len >= offsetofend(struct fwdb_rule, wmm_ptr)) { u32 wmm_ptr = be16_to_cpu(rule->wmm_ptr) << 2; struct fwdb_wmm_rule *wmm; From 25e73f1d6868c759c8eb56cad4e411692c5341b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Fri, 23 Oct 2026 13:00:51 +0200 Subject: [PATCH 6/6] wifi: mac80211: reinstate IEEE80211_TX_CTL_AMPDU flag in ieee80211_tx_prep_agg() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commit 592234e941f1 ("wifi: mac80211: Fix iTXQ AMPDU fragmentation handling") changed the way handling of the IEEE80211_TX_CTL_AMPDU flag is done in mac80211, so the only place it is set is in ieee80211_tx_dequeue(). This means that any frames that are sent to the driver via drv_tx() will no longer have the flag set. In particular, this affects packets going through ieee80211_tx_pending(). Evan reported that this can result in the ath9k driver failing to account such packets in its block ack window, eventually overrunning the window and stalling TX. Fix this by reinstating the flag in ieee80211_tx_prep_agg() when inside a valid block ack window. This should be harmless for the TXQ path, and will reinstate the previous behaviour for drivers that use the flag as a signal to track the packet in the window. Fixes: 592234e941f1 ("wifi: mac80211: Fix iTXQ AMPDU fragmentation handling") Cc: stable@vger.kernel.org Reported-by: Evan Severson Link: https://lore.kernel.org/all/20261014025734.2288693-1-eseverson@gmail.com/ Signed-off-by: Toke Høiland-Jørgensen Tested-by: Evan Severson Link: https://patch.msgid.link/20261023110052.650519-1-toke@toke.dk Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index d51f810dd2c5..32f94761cf32 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1118,6 +1118,7 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx, struct sk_buff *purge_skb = NULL; if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { + info->flags |= IEEE80211_TX_CTL_AMPDU; reset_agg_timer = true; } else if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) { /* @@ -1149,6 +1150,7 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx, if (!tid_tx) { /* do nothing, let packet pass through */ } else if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { + info->flags |= IEEE80211_TX_CTL_AMPDU; reset_agg_timer = true; } else { queued = true;