* [PATCH 1/4] wifi: nxpwifi: cancel channel switch work on teardown
2026-10-28 14:18 [PATCH 0/4] wifi: nxpwifi: fix AP channel-switch lifetime and failures Zhao Li
@ 2026-10-28 14:18 ` Zhao Li
2026-10-28 14:18 ` [PATCH 2/4] wifi: nxpwifi: stop AP TX after preparing channel switch Zhao Li
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Zhao Li @ 2026-10-28 14:18 UTC (permalink / raw)
To: Jeff Chen
Cc: Francesco Dolcini, Johannes Berg, Arend van Spriel, Jiangshan Yi,
Linmao Li, Kees Cook, Rosen Penev, linux-wireless, linux-kernel,
stable
Stopping an AP while channel-switch work is pending leaves the delayed
work armed. After driver removal, the callback runs on freed driver
state. If the AP is restarted first, the callback switches it to the old
target channel. Reusing the interface slot can also reinitialize the
work while it is still pending.
Cancel the work when the AP stops. Interface deletion, type changes and
driver removal all stop a running AP first.
LLM assistance was used to find the bug and review.
Fixes: 73b01e57ed3e ("wifi: nxp: add nxpwifi driver for IW61x")
Cc: stable@vger.kernel.org
Signed-off-by: Zhao Li <enderaoelyther@gmail.com>
---
Validation: A W=1 module build with MODPOST passed. Not
reproduced; IW61x hardware was unavailable.
Sparse C=2 passed. Targeted Smatch introduced no new diagnostics.
drivers/net/wireless/nxp/nxpwifi/cfg80211.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c
index 473e81304ca8..384ba69dd058 100644
--- a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c
+++ b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c
@@ -1638,6 +1638,7 @@ static int nxpwifi_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
int ret;
+ wiphy_delayed_work_cancel(wiphy, &priv->dfs_chan_sw_work);
nxpwifi_abort_cac(priv);
if (nxpwifi_del_mgmt_ies(priv))
--
2.56.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 2/4] wifi: nxpwifi: stop AP TX after preparing channel switch
2026-10-28 14:18 [PATCH 0/4] wifi: nxpwifi: fix AP channel-switch lifetime and failures Zhao Li
2026-10-28 14:18 ` [PATCH 1/4] wifi: nxpwifi: cancel channel switch work on teardown Zhao Li
@ 2026-10-28 14:18 ` Zhao Li
2026-10-28 14:18 ` [PATCH 3/4] wifi: nxpwifi: own beacon data used by channel switch work Zhao Li
2026-10-28 14:18 ` [PATCH 4/4] wifi: nxpwifi: stop AP when delayed channel switch fails Zhao Li
3 siblings, 0 replies; 5+ messages in thread
From: Zhao Li @ 2026-10-28 14:18 UTC (permalink / raw)
To: Jeff Chen
Cc: Francesco Dolcini, Johannes Berg, Arend van Spriel, Jiangshan Yi,
Linmao Li, Kees Cook, Rosen Penev, linux-wireless, linux-kernel,
stable
A channel-switch request can leave AP traffic stopped if installing the
CSA management IEs fails or the driver's work wrapper refuses the request.
The driver disables carrier and the TX queues before programming the IEs,
and the wrapper silently drops work during removal, command timeout or
suspend.
Prepare the CSA IEs before stopping traffic. Queue the switch directly
with wiphy_delayed_work_queue(), since the driver's wrapper can silently
discard it after the IEs are installed and traffic is stopped. The wiphy
mutex serializes the callback and worker with AP-stop cancellation.
LLM assistance was used to find the bug and review.
Fixes: 73b01e57ed3e ("wifi: nxp: add nxpwifi driver for IW61x")
Cc: stable@vger.kernel.org
Signed-off-by: Zhao Li <enderaoelyther@gmail.com>
---
Validation: A W=1 module build with MODPOST passed. Not
reproduced; IW61x hardware was unavailable.
Sparse C=2 passed. Targeted Smatch introduced no new diagnostics.
drivers/net/wireless/nxp/nxpwifi/cfg80211.c | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c
index 384ba69dd058..0f7720df0304 100644
--- a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c
+++ b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c
@@ -3115,12 +3115,6 @@ nxpwifi_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
&priv->dfs_chandef))
return -EINVAL;
- if (params->block_tx) {
- netif_carrier_off(priv->netdev);
- nxpwifi_stop_net_dev_queue(priv->netdev, priv->adapter);
- priv->uap_stop_tx = true;
- }
-
ret = nxpwifi_del_mgmt_ies(priv);
if (ret)
nxpwifi_dbg(priv->adapter, ERROR,
@@ -3137,11 +3131,16 @@ nxpwifi_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
memcpy(&priv->ap_update_info.beacon, ¶ms->beacon_after,
sizeof(priv->ap_update_info.beacon));
+ if (params->block_tx) {
+ netif_carrier_off(priv->netdev);
+ nxpwifi_stop_net_dev_queue(priv->netdev, priv->adapter);
+ priv->uap_stop_tx = true;
+ }
+
chsw_msec = max(params->count * priv->bss_cfg.beacon_period, 100);
- nxpwifi_queue_delayed_wiphy_work(priv->adapter,
- &priv->dfs_chan_sw_work,
- msecs_to_jiffies(chsw_msec));
+ wiphy_delayed_work_queue(wiphy, &priv->dfs_chan_sw_work,
+ msecs_to_jiffies(chsw_msec));
done:
return ret;
--
2.56.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 3/4] wifi: nxpwifi: own beacon data used by channel switch work
2026-10-28 14:18 [PATCH 0/4] wifi: nxpwifi: fix AP channel-switch lifetime and failures Zhao Li
2026-10-28 14:18 ` [PATCH 1/4] wifi: nxpwifi: cancel channel switch work on teardown Zhao Li
2026-10-28 14:18 ` [PATCH 2/4] wifi: nxpwifi: stop AP TX after preparing channel switch Zhao Li
@ 2026-10-28 14:18 ` Zhao Li
2026-10-28 14:18 ` [PATCH 4/4] wifi: nxpwifi: stop AP when delayed channel switch fails Zhao Li
3 siblings, 0 replies; 5+ messages in thread
From: Zhao Li @ 2026-10-28 14:18 UTC (permalink / raw)
To: Jeff Chen
Cc: Francesco Dolcini, Johannes Berg, Arend van Spriel, Jiangshan Yi,
Linmao Li, Kees Cook, Rosen Penev, linux-wireless, linux-kernel,
stable+noautosel
Channel-switch work passes beacon data to nxpwifi_set_mgmt_ies() after
the cfg80211 callback returns, but the driver retains only a shallow copy
of pointers into the request. The request storage can be freed before the
delayed work runs.
The IE parser can then read freed memory. Stale bytes that pass its
element checks can be copied into management-IE commands sent to firmware.
Deep-copy the four beacon buffers consumed by nxpwifi_set_mgmt_ies() into
an owned cfg80211_ap_update. Keep the existing change_beacon path, reject
overlapping switches while the update is owned, and release it when the
work finishes or is canceled.
LLM assistance was used to find the bug and review.
Fixes: 73b01e57ed3e ("wifi: nxp: add nxpwifi driver for IW61x")
Cc: <stable+noautosel@kernel.org> # needs a smaller backport
Signed-off-by: Zhao Li <enderaoelyther@gmail.com>
---
Validation: A W=1 module build with MODPOST passed. Not
reproduced; IW61x hardware was unavailable.
Sparse C=2 passed. Targeted Smatch introduced no new diagnostics.
All four patches apply without changes to v7.3.
This 144-line hunk-body diff exceeds the stable tree's 100-line limit, so a
smaller reviewed backport is needed.
drivers/net/wireless/nxp/nxpwifi/11h.c | 14 +++--
drivers/net/wireless/nxp/nxpwifi/cfg80211.c | 58 ++++++++++++++++++++-
drivers/net/wireless/nxp/nxpwifi/main.h | 2 +-
3 files changed, 66 insertions(+), 8 deletions(-)
diff --git a/drivers/net/wireless/nxp/nxpwifi/11h.c b/drivers/net/wireless/nxp/nxpwifi/11h.c
index 058c319ff910..a6c87dfad9ee 100644
--- a/drivers/net/wireless/nxp/nxpwifi/11h.c
+++ b/drivers/net/wireless/nxp/nxpwifi/11h.c
@@ -301,21 +301,21 @@ void nxpwifi_dfs_chan_sw_work(struct wiphy *wiphy, struct wiphy_work *work)
if (!bss_cfg->beacon_period) {
nxpwifi_dbg(adapter, ERROR,
"channel switch: AP already stopped\n");
- return;
+ goto out;
}
if (nxpwifi_send_cmd(priv, HOST_CMD_UAP_BSS_STOP,
HOST_ACT_GEN_SET, 0, NULL, true)) {
nxpwifi_dbg(adapter, ERROR,
"channel switch: Failed to stop the BSS\n");
- return;
+ goto out;
}
if (nxpwifi_cfg80211_change_beacon(adapter->wiphy, priv->netdev,
- &priv->ap_update_info)) {
+ priv->ap_update_info)) {
nxpwifi_dbg(adapter, ERROR,
"channel switch: Failed to set beacon\n");
- return;
+ goto out;
}
nxpwifi_uap_set_channel(priv, bss_cfg, priv->dfs_chandef);
@@ -323,7 +323,7 @@ void nxpwifi_dfs_chan_sw_work(struct wiphy *wiphy, struct wiphy_work *work)
if (nxpwifi_config_start_uap(priv, bss_cfg)) {
nxpwifi_dbg(adapter, ERROR,
"Failed to start AP after channel switch\n");
- return;
+ goto out;
}
nxpwifi_dbg(adapter, MSG,
@@ -336,4 +336,8 @@ void nxpwifi_dfs_chan_sw_work(struct wiphy *wiphy, struct wiphy_work *work)
nxpwifi_wake_up_net_dev_queue(priv->netdev, adapter);
priv->uap_stop_tx = false;
}
+
+out:
+ kfree(priv->ap_update_info);
+ priv->ap_update_info = NULL;
}
diff --git a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c
index 0f7720df0304..5305eda83fa0 100644
--- a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c
+++ b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c
@@ -1639,6 +1639,8 @@ static int nxpwifi_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
int ret;
wiphy_delayed_work_cancel(wiphy, &priv->dfs_chan_sw_work);
+ kfree(priv->ap_update_info);
+ priv->ap_update_info = NULL;
nxpwifi_abort_cac(priv);
if (nxpwifi_del_mgmt_ies(priv))
@@ -3066,6 +3068,50 @@ static int nxpwifi_cfg80211_set_coalesce(struct wiphy *wiphy,
return nxpwifi_coalesce_cfg(priv, &coalesce_cfg);
}
+/* Keep only the beacon buffers consumed by nxpwifi_set_mgmt_ies(). */
+static struct cfg80211_ap_update *
+nxpwifi_dup_csa_update(const struct cfg80211_beacon_data *beacon)
+{
+ struct cfg80211_ap_update *update;
+ struct cfg80211_beacon_data *copy;
+ u8 *pos;
+ size_t len = beacon->tail_len + beacon->beacon_ies_len +
+ beacon->proberesp_ies_len + beacon->assocresp_ies_len;
+
+ /* Each length comes from a bounded netlink attribute. */
+ update = kzalloc(sizeof(*update) + len, GFP_KERNEL);
+ if (!update)
+ return NULL;
+
+ copy = &update->beacon;
+ pos = (u8 *)(update + 1);
+ if (beacon->tail_len) {
+ copy->tail = pos;
+ copy->tail_len = beacon->tail_len;
+ memcpy(pos, beacon->tail, beacon->tail_len);
+ pos += beacon->tail_len;
+ }
+ if (beacon->beacon_ies_len) {
+ copy->beacon_ies = pos;
+ copy->beacon_ies_len = beacon->beacon_ies_len;
+ memcpy(pos, beacon->beacon_ies, beacon->beacon_ies_len);
+ pos += beacon->beacon_ies_len;
+ }
+ if (beacon->proberesp_ies_len) {
+ copy->proberesp_ies = pos;
+ copy->proberesp_ies_len = beacon->proberesp_ies_len;
+ memcpy(pos, beacon->proberesp_ies, beacon->proberesp_ies_len);
+ pos += beacon->proberesp_ies_len;
+ }
+ if (beacon->assocresp_ies_len) {
+ copy->assocresp_ies = pos;
+ copy->assocresp_ies_len = beacon->assocresp_ies_len;
+ memcpy(pos, beacon->assocresp_ies, beacon->assocresp_ies_len);
+ }
+
+ return update;
+}
+
static int
nxpwifi_cfg80211_uap_add_station(struct nxpwifi_private *priv, const u8 *mac,
struct station_parameters *params)
@@ -3099,9 +3145,13 @@ nxpwifi_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_csa_settings *params)
{
struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ struct cfg80211_ap_update *update;
int chsw_msec;
int ret;
+ if (priv->ap_update_info)
+ return -EBUSY;
+
if (priv->adapter->scan_processing) {
nxpwifi_dbg(priv->adapter, ERROR,
"radar detection: scan in process...\n");
@@ -3115,6 +3165,10 @@ nxpwifi_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
&priv->dfs_chandef))
return -EINVAL;
+ update = nxpwifi_dup_csa_update(¶ms->beacon_after);
+ if (!update)
+ return -ENOMEM;
+
ret = nxpwifi_del_mgmt_ies(priv);
if (ret)
nxpwifi_dbg(priv->adapter, ERROR,
@@ -3124,12 +3178,12 @@ nxpwifi_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
if (ret) {
nxpwifi_dbg(priv->adapter, ERROR,
"%s: setting mgmt ies failed\n", __func__);
+ kfree(update);
goto done;
}
memcpy(&priv->dfs_chandef, ¶ms->chandef, sizeof(priv->dfs_chandef));
- memcpy(&priv->ap_update_info.beacon, ¶ms->beacon_after,
- sizeof(priv->ap_update_info.beacon));
+ priv->ap_update_info = update;
if (params->block_tx) {
netif_carrier_off(priv->netdev);
diff --git a/drivers/net/wireless/nxp/nxpwifi/main.h b/drivers/net/wireless/nxp/nxpwifi/main.h
index b25a6a4f2936..4d5718ed5031 100644
--- a/drivers/net/wireless/nxp/nxpwifi/main.h
+++ b/drivers/net/wireless/nxp/nxpwifi/main.h
@@ -619,7 +619,7 @@ struct nxpwifi_private {
struct wiphy_delayed_work dfs_cac_work;
struct wiphy_delayed_work dfs_chan_sw_work;
bool uap_stop_tx;
- struct cfg80211_ap_update ap_update_info;
+ struct cfg80211_ap_update *ap_update_info;
struct nxpwifi_11h_intf_state state_11h;
struct nxpwifi_ds_mem_rw mem_rw;
struct sk_buff_head bypass_txq;
--
2.56.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 4/4] wifi: nxpwifi: stop AP when delayed channel switch fails
2026-10-28 14:18 [PATCH 0/4] wifi: nxpwifi: fix AP channel-switch lifetime and failures Zhao Li
` (2 preceding siblings ...)
2026-10-28 14:18 ` [PATCH 3/4] wifi: nxpwifi: own beacon data used by channel switch work Zhao Li
@ 2026-10-28 14:18 ` Zhao Li
3 siblings, 0 replies; 5+ messages in thread
From: Zhao Li @ 2026-10-28 14:18 UTC (permalink / raw)
To: Jeff Chen
Cc: Francesco Dolcini, Johannes Berg, Arend van Spriel, Jiangshan Yi,
Linmao Li, Kees Cook, Rosen Penev, linux-wireless, linux-kernel,
stable+noautosel
A delayed AP channel switch can fail while stopping the BSS, programming
the new beacon, or restarting the AP. The worker returns without notifying
cfg80211, and traffic can remain enabled over a stopped BSS when the
request did not set block_tx.
An earlier stop failure can also clear the driver's AP configuration while
cfg80211 still considers the AP running. A following channel switch then
reaches the worker without a beacon period and is discarded.
Disable carrier and the TX queues, retain the channel-switch state, and
request interface shutdown on all four failures. Keeping the state until
the stop path cancels it prevents another switch from starting while
shutdown is pending.
LLM assistance was used to find the bug and review.
Fixes: 73b01e57ed3e ("wifi: nxp: add nxpwifi driver for IW61x")
Cc: <stable+noautosel@kernel.org> # depends on the beacon ownership backport
Signed-off-by: Zhao Li <enderaoelyther@gmail.com>
---
Validation: A W=1 module build with MODPOST passed. Not
reproduced; IW61x hardware was unavailable.
Sparse C=2 passed. Targeted Smatch introduced no new diagnostics.
All four patches apply without changes to v7.3.
Stable pickup is disabled because this patch depends on that backport.
Persistent firmware-command failure recovery was not exercised. The patch
disables TX and requests interface shutdown; it does not prove that cfg80211
and firmware state converge if stop_ap() also fails.
drivers/net/wireless/nxp/nxpwifi/11h.c | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/drivers/net/wireless/nxp/nxpwifi/11h.c b/drivers/net/wireless/nxp/nxpwifi/11h.c
index a6c87dfad9ee..1341c9015ea4 100644
--- a/drivers/net/wireless/nxp/nxpwifi/11h.c
+++ b/drivers/net/wireless/nxp/nxpwifi/11h.c
@@ -300,22 +300,22 @@ void nxpwifi_dfs_chan_sw_work(struct wiphy *wiphy, struct wiphy_work *work)
bss_cfg = &priv->bss_cfg;
if (!bss_cfg->beacon_period) {
nxpwifi_dbg(adapter, ERROR,
- "channel switch: AP already stopped\n");
- goto out;
+ "channel switch: AP configuration is missing\n");
+ goto stop_iface;
}
if (nxpwifi_send_cmd(priv, HOST_CMD_UAP_BSS_STOP,
HOST_ACT_GEN_SET, 0, NULL, true)) {
nxpwifi_dbg(adapter, ERROR,
"channel switch: Failed to stop the BSS\n");
- goto out;
+ goto stop_iface;
}
if (nxpwifi_cfg80211_change_beacon(adapter->wiphy, priv->netdev,
priv->ap_update_info)) {
nxpwifi_dbg(adapter, ERROR,
"channel switch: Failed to set beacon\n");
- goto out;
+ goto stop_iface;
}
nxpwifi_uap_set_channel(priv, bss_cfg, priv->dfs_chandef);
@@ -323,7 +323,7 @@ void nxpwifi_dfs_chan_sw_work(struct wiphy *wiphy, struct wiphy_work *work)
if (nxpwifi_config_start_uap(priv, bss_cfg)) {
nxpwifi_dbg(adapter, ERROR,
"Failed to start AP after channel switch\n");
- goto out;
+ goto stop_iface;
}
nxpwifi_dbg(adapter, MSG,
@@ -337,7 +337,13 @@ void nxpwifi_dfs_chan_sw_work(struct wiphy *wiphy, struct wiphy_work *work)
priv->uap_stop_tx = false;
}
-out:
kfree(priv->ap_update_info);
priv->ap_update_info = NULL;
+ return;
+
+stop_iface:
+ netif_carrier_off(priv->netdev);
+ nxpwifi_stop_net_dev_queue(priv->netdev, adapter);
+ priv->uap_stop_tx = true;
+ cfg80211_stop_iface(wiphy, &priv->wdev, GFP_KERNEL);
}
--
2.56.0
^ permalink raw reply [flat|nested] 5+ messages in thread