CVE-2026-46125
Description
In the Linux kernel, the following vulnerability has been resolved:
wifi: mac80211: remove station if connection prep fails
If connection preparation fails for MLO connections, then the interface is completely reset to non-MLD. In this case, we must not keep the station since it's related to the link of the vif being removed. Delete an existing station. Any "new_sta" is already being removed, so that doesn't need changes.
This fixes a use-after-free/double-free in debugfs if that's enabled, because a vif going from MLD (and to MLD, but that's not relevant here) recreates its entire debugfs.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
In the Linux kernel's mac80211, failing MLO connection preparation can cause a use-after-free/double-free in debugfs due to an uncleaned station pointer.
Vulnerability
In the Linux kernel's mac80211 subsystem, when a multi-link operation (MLO) connection fails during preparation, the interface is completely reset to non-MLD. The relevant code path fails to delete the existing station associated with the link of the VIF being removed. This oversight leads to a use-after-free or double-free condition in debugfs (when enabled), because the VIF transitioning from MLD to non-MLD recreates its entire debugfs directory while the stale station pointer remains. The vulnerability is present in affected kernel versions before the fix commit 283fc9e44ff5b5ac967439b4951b80bd4299f4e4 [1].
Exploitation
An attacker would need the ability to trigger MLO connection preparation failures on a system running a vulnerable kernel. This typically requires local access and the ability to initiate Wi-Fi MLO connections that fail during preparation. The exact sequence involves starting an MLO connection setup that then fails, causing the interface reset without proper station cleanup.
Impact
On systems with debugfs enabled, a successful exploit results in a use-after-free or double-free kernel memory corruption. The privilege level is local kernel context; an attacker could potentially leverage this to escalate privileges or cause a denial of service (system crash). The scope of compromise is limited to the kernel's mac80211 subsystem state.
Mitigation
The fix was committed to the Linux kernel stable tree as commit 283fc9e44ff5b5ac967439b4951b80bd4299f4e4 [1]. Users should apply the patch or update to a kernel version that includes this commit. No workaround is documented; systems with debugfs disabled are not affected by the debugfs issue, but the station cleanup should still be performed for correctness.
AI Insight generated on May 28, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
10283fc9e44ff5wifi: mac80211: remove station if connection prep fails
2 files changed · +8 −12
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 298ebff6bbf84b..0a0f27836d5706 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -9149,7 +9149,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; u16 new_links; @@ -9168,11 +9168,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, mlo = false; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (mlo && !have_sta && WARN_ON(sdata->vif.valid_links || sdata->vif.active_links)) @@ -9336,6 +9333,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 298ebff6bbf84b..0a0f27836d5706 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -9149,7 +9149,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; u16 new_links; @@ -9168,11 +9168,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, mlo = false; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (mlo && !have_sta && WARN_ON(sdata->vif.valid_links || sdata->vif.active_links)) @@ -9336,6 +9333,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
afcbaed89cdcwifi: mac80211: remove station if connection prep fails
2 files changed · +8 −12
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 61e7935926380f..835316fd3cd76f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -8177,7 +8177,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; @@ -8215,11 +8215,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, goto out_err; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (!have_sta) { if (mlo) @@ -8352,6 +8349,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 61e7935926380f..835316fd3cd76f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -8177,7 +8177,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; @@ -8215,11 +8215,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, goto out_err; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (!have_sta) { if (mlo) @@ -8352,6 +8349,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
fe75fa1ac9a9wifi: mac80211: remove station if connection prep fails
2 files changed · +8 −12
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 77da0bd5891efa..496f2b36a0bed4 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -6940,7 +6940,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; @@ -6978,11 +6978,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, goto out_err; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (!have_sta) { if (mlo) @@ -7106,6 +7103,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 77da0bd5891efa..496f2b36a0bed4 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -6940,7 +6940,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; @@ -6978,11 +6978,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, goto out_err; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (!have_sta) { if (mlo) @@ -7106,6 +7103,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
1c2b72ea8988wifi: mac80211: remove station if connection prep fails
2 files changed · +8 −12
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 810bea1aacc5e5..6a0e2896b54c74 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -9053,7 +9053,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; u16 new_links; @@ -9072,11 +9072,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, mlo = false; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (mlo && !have_sta && WARN_ON(sdata->vif.valid_links || sdata->vif.active_links)) @@ -9239,6 +9236,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 810bea1aacc5e5..6a0e2896b54c74 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -9053,7 +9053,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; u16 new_links; @@ -9072,11 +9072,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, mlo = false; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (mlo && !have_sta && WARN_ON(sdata->vif.valid_links || sdata->vif.active_links)) @@ -9239,6 +9236,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
9e28654f79f4wifi: mac80211: remove station if connection prep fails
2 files changed · +8 −12
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index f119149bcc1c11..deef15c074c832 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -8926,7 +8926,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; u16 new_links; @@ -8945,11 +8945,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, mlo = false; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (mlo && !have_sta && WARN_ON(sdata->vif.valid_links || sdata->vif.active_links)) @@ -9108,6 +9105,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index f119149bcc1c11..deef15c074c832 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -8926,7 +8926,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; u16 new_links; @@ -8945,11 +8945,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, mlo = false; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (mlo && !have_sta && WARN_ON(sdata->vif.valid_links || sdata->vif.active_links)) @@ -9108,6 +9105,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
9e28654f79f4wifi: mac80211: remove station if connection prep fails
2 files changed · +8 −12
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index f119149bcc1c11..deef15c074c832 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -8926,7 +8926,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; u16 new_links; @@ -8945,11 +8945,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, mlo = false; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (mlo && !have_sta && WARN_ON(sdata->vif.valid_links || sdata->vif.active_links)) @@ -9108,6 +9105,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index f119149bcc1c11..deef15c074c832 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -8926,7 +8926,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; u16 new_links; @@ -8945,11 +8945,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, mlo = false; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (mlo && !have_sta && WARN_ON(sdata->vif.valid_links || sdata->vif.active_links)) @@ -9108,6 +9105,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
283fc9e44ff5wifi: mac80211: remove station if connection prep fails
2 files changed · +8 −12
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 298ebff6bbf84b..0a0f27836d5706 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -9149,7 +9149,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; u16 new_links; @@ -9168,11 +9168,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, mlo = false; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (mlo && !have_sta && WARN_ON(sdata->vif.valid_links || sdata->vif.active_links)) @@ -9336,6 +9333,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 298ebff6bbf84b..0a0f27836d5706 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -9149,7 +9149,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; u16 new_links; @@ -9168,11 +9168,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, mlo = false; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (mlo && !have_sta && WARN_ON(sdata->vif.valid_links || sdata->vif.active_links)) @@ -9336,6 +9333,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
afcbaed89cdcwifi: mac80211: remove station if connection prep fails
2 files changed · +8 −12
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 61e7935926380f..835316fd3cd76f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -8177,7 +8177,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; @@ -8215,11 +8215,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, goto out_err; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (!have_sta) { if (mlo) @@ -8352,6 +8349,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 61e7935926380f..835316fd3cd76f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -8177,7 +8177,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; @@ -8215,11 +8215,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, goto out_err; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (!have_sta) { if (mlo) @@ -8352,6 +8349,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
1c2b72ea8988wifi: mac80211: remove station if connection prep fails
2 files changed · +8 −12
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 810bea1aacc5e5..6a0e2896b54c74 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -9053,7 +9053,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; u16 new_links; @@ -9072,11 +9072,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, mlo = false; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (mlo && !have_sta && WARN_ON(sdata->vif.valid_links || sdata->vif.active_links)) @@ -9239,6 +9236,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 810bea1aacc5e5..6a0e2896b54c74 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -9053,7 +9053,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; u16 new_links; @@ -9072,11 +9072,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, mlo = false; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (mlo && !have_sta && WARN_ON(sdata->vif.valid_links || sdata->vif.active_links)) @@ -9239,6 +9236,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
fe75fa1ac9a9wifi: mac80211: remove station if connection prep fails
2 files changed · +8 −12
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 77da0bd5891efa..496f2b36a0bed4 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -6940,7 +6940,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; @@ -6978,11 +6978,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, goto out_err; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (!have_sta) { if (mlo) @@ -7106,6 +7103,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
net/mac80211/mlme.c+4 −6 modifieddiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 77da0bd5891efa..496f2b36a0bed4 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -6940,7 +6940,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; @@ -6978,11 +6978,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, goto out_err; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (!have_sta) { if (mlo) @@ -7106,6 +7103,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Missing station cleanup in the error path of ieee80211_prep_connection for MLO connections, causing a use-after-free/double-free when the vif is reset to non-MLD."
Attack vector
An attacker on the same Wi-Fi network can trigger a failed MLO connection preparation (e.g., by causing an authentication or association failure) against a station that already has an existing STA entry from a prior association attempt. When `ieee80211_prep_connection` fails and takes the `out_err` path, the interface is reset to non-MLD, but the existing station (`have_sta`) tied to the removed link was not freed. This leaves a stale `sta_info` pointer that can later be used-after-free or double-freed when debugfs is recreated for the vif [patch_id=2898488].
Affected code
The vulnerability is in the `ieee80211_prep_connection` function in `net/mac80211/mlme.c`. The error path (`out_err`) resets the interface to non-MLD via `ieee80211_vif_set_links(sdata, 0, 0)` but previously did not destroy the existing station (`have_sta`) that was associated with the link being removed. The patch changes `have_sta` from a `bool` to a `struct sta_info *` pointer and adds a call to `__sta_info_destroy(have_sta)` before the vif links reset.
What the fix does
The patch makes two changes in `ieee80211_prep_connection`. First, `have_sta` is changed from a `bool` to a `struct sta_info *` pointer so the actual station object can be tracked. The `rcu_read_lock`/`rcu_read_unlock` around `sta_info_get` is removed because the pointer is now used outside the RCU critical section. Second, in the `out_err` error path, a new conditional calls `__sta_info_destroy(have_sta)` when `mlo` is true and a station exists, ensuring the stale station is freed before `ieee80211_vif_set_links(sdata, 0, 0)` resets the interface. This prevents the use-after-free/double-free in debugfs [patch_id=2898488].
Preconditions
- configThe system must have CONFIG_MAC80211 enabled and be using MLO (Multi-Link Operation) connections.
- inputAn existing STA entry must already be present for the AP MLD address (from a prior association attempt).
- inputThe connection preparation (ieee80211_prep_connection) must fail after the station lookup but before successful completion, hitting the out_err path.
- configdebugfs must be enabled (CONFIG_DEBUG_FS) for the use-after-free/double-free to manifest.
Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- git.kernel.org/stable/c/1c2b72ea89882aeb948340498391e69c58d466f1nvd
- git.kernel.org/stable/c/283fc9e44ff5b5ac967439b4951b80bd4299f4e4nvd
- git.kernel.org/stable/c/9e28654f79f443bca9b29ff3ae7cf18abfba58a0nvd
- git.kernel.org/stable/c/afcbaed89cdc1a001b43270cbf5394bb4804270anvd
- git.kernel.org/stable/c/fe75fa1ac9a92990f7fc3d34b17808fd933071b2nvd
News mentions
0No linked articles in our index yet.