wifi: cfg80211: preserve hidden-group beacon IE ownership

A hidden-group probe-response entry borrows its beacon IEs from the
group's beacon entry. cfg80211_update_assoc_bss_entry() can pass such
an entry to cfg80211_update_known_bss() when merging scan entries
after a channel switch.

Copying the borrowed beacon_ies into the associated entry gives the
same allocation two owners. Releasing the group's beacon entry can
then leave the associated entry pointing to freed IEs, which are
freed again when that entry is released.

Skip beacon IE replacement when the source entry belongs to a hidden
group. Preserve probe-response processing, the subsequent metadata
updates, and the existing rejection path for conflicting hidden
beacon updates.

Fixes: 0afd425b1b ("cfg80211: fix duplicated scan entries after channel switch")
Reported-by: Yilin Zhang <yilinzhang@moonshot.ai>
Closes: https://lore.kernel.org/linux-wireless/20260901080427.1983015-1-yilinzhang@moonshot.ai/
Assisted-by: Codex:gpt-6-astra
Signed-off-by: Zhao Li <enderaoelyther@gmail.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Zhao Li 2026-09-14 23:28:25 +08:00 committed by Johannes Berg
parent a10a2f8047
commit cc45f313d8

View File

@ -1992,20 +1992,22 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,
return false;
}
old = rcu_access_pointer(known->pub.beacon_ies);
if (!new->pub.hidden_beacon_bss) {
old = rcu_access_pointer(known->pub.beacon_ies);
rcu_assign_pointer(known->pub.beacon_ies, new->pub.beacon_ies);
rcu_assign_pointer(known->pub.beacon_ies, new->pub.beacon_ies);
/* Override IEs if they were from a beacon before */
if (old == rcu_access_pointer(known->pub.ies))
rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies);
/* Override IEs if they were from a beacon before */
if (old == rcu_access_pointer(known->pub.ies))
rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies);
cfg80211_update_hidden_bsses(known,
rcu_access_pointer(new->pub.beacon_ies),
old);
cfg80211_update_hidden_bsses(known,
rcu_access_pointer(new->pub.beacon_ies),
old);
if (old)
kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
if (old)
kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
}
}
known->pub.beacon_interval = new->pub.beacon_interval;