[PATCH 3/4] wifi: nxpwifi: own beacon data used by channel switch work
From: Zhao Li
Date: Wed Oct 28 2026 - 10:20:53 EST
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@xxxxxxxxxx> # needs a smaller backport
Signed-off-by: Zhao Li <enderaoelyther@xxxxxxxxx>
---
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