[PATCH v2] mac80211: reject station addition if AP or MLO link is inactive

From: Slawomir Stepien

Date: Mon Jul 27 2026 - 04:06:11 EST


Currently, userspace can issue a netlink command to add a station to
an AP or P2P GO interface before the AP has fully started, or before
a specific MLO link has begun beaconing. This will e.g. lead to a
WARN_ON trigger:

WARNING: net/mac80211/rate.c:51 at rate_control_rate_init+0x5a6/0x630
...
Call Trace:
<TASK>
rate_control_rate_init_all_links+0xf4/0x190 net/mac80211/rate.c:84
sta_apply_auth_flags+0x1bc/0x430 net/mac80211/cfg.c:2152
sta_apply_parameters+0x126d/0x1b10 net/mac80211/cfg.c:2618
ieee80211_add_station+0x3de/0x700 net/mac80211/cfg.c:2684
rdev_add_station+0xfc/0x290 net/wireless/rdev-ops.h:201
nl80211_new_station+0x1b4e/0x1fd0 net/wireless/nl80211.c:9505

Fix this by introducing a two-tiered active state check in
ieee80211_add_station():

1. Global AP State: Add an early return if the main wdev is an AP or
P2P GO and `sdata->u.ap.active` is false.

2. MLO Link State: For Wi-Fi 7 MLO setups, links can start asynchronously.
Even if the global AP is active, a specific link might still be down.
If a specific link_id is requested, ensure the underlying physical link
has an allocated beacon (`link->u.ap.beacon`) before proceeding with
sta_info allocation.

Assisted-by: Gemini:3.1-pro
Reported-by: syzbot+9bdc0c5998ab45b05030@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=9bdc0c5998ab45b05030
Signed-off-by: Slawomir Stepien <sst@xxxxxxxxx>
---
v2:
* Move the logic inside ieee80211_add_station()
* Check both the non-MLO AP/P2P state and MLO link state
* Update subject line and commit message

v1:
* https://lore.kernel.org/linux-wireless/373210a9-4365-4cac-823a-0eec391858e7@xxxxxxxxxxxxxxx/
---
net/mac80211/cfg.c | 43 ++++++++++++++++++++++++++++++++++---------
1 file changed, 34 insertions(+), 9 deletions(-)

diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 43f142624d33..c3a580ad97e9 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2637,19 +2637,24 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct wireless_dev *wdev,
{
struct ieee80211_local *local = wiphy_priv(wiphy);
struct sta_info *sta;
- struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
int err;

lockdep_assert_wiphy(local->hw.wiphy);

+ if (sdata->vif.type == NL80211_IFTYPE_AP ||
+ sdata->vif.type == NL80211_IFTYPE_P2P_GO) {
+ if (!sdata->u.ap.active)
+ return -ENETDOWN;
+ }
+
if (params->vlan) {
sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);

if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
sdata->vif.type != NL80211_IFTYPE_AP)
return -EINVAL;
- } else
- sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ }

if (ether_addr_equal(mac, sdata->vif.addr))
return -EINVAL;
@@ -2663,17 +2668,37 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct wireless_dev *wdev,
return -EINVAL;

/*
- * If we have a link ID, it can be a non-MLO station on an AP MLD,
- * but we need to have a link_mac in that case as well, so use the
- * STA's MAC address in that case.
+ * If we have a link ID:
+ * (1) check if it is valid, up and beaconing
+ * (2) it can be a non-MLO station on an AP MLD, but we need to have a
+ * link_mac in that case as well, so use the STA's MAC address in that
+ * case.
*/
- if (params->link_sta_params.link_id >= 0)
+ if (params->link_sta_params.link_id >= 0) {
+ int link_id = params->link_sta_params.link_id;
+ struct ieee80211_link_data *link;
+ struct ieee80211_sub_if_data *wdev_sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+
+ if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS)
+ return -EINVAL;
+
+ link = sdata_dereference(wdev_sdata->link[link_id], wdev_sdata);
+ if (!link)
+ return -ENOLINK;
+
+ if (wdev_sdata->vif.type == NL80211_IFTYPE_AP ||
+ wdev_sdata->vif.type == NL80211_IFTYPE_P2P_GO) {
+ if (!rcu_access_pointer(link->u.ap.beacon))
+ return -ENETDOWN;
+ }
+
sta = sta_info_alloc_with_link(sdata, mac,
- params->link_sta_params.link_id,
+ link_id,
params->link_sta_params.link_mac ?: mac,
GFP_KERNEL);
- else
+ } else {
sta = sta_info_alloc(sdata, mac, GFP_KERNEL);
+ }

if (!sta)
return -ENOMEM;
--
2.55.0