Skip to content

Commit 09b1d5d

Browse files
committed
cfg80211: fix management registrations locking
The management registrations locking was broken, the list was locked for each wdev, but cfg80211_mgmt_registrations_update() iterated it without holding all the correct spinlocks, causing list corruption. Rather than trying to fix it with fine-grained locking, just move the lock to the wiphy/rdev (still need the list on each wdev), we already need to hold the wdev lock to change it, so there's no contention on the lock in any case. This trivially fixes the bug since we hold one wdev's lock already, and now will hold the lock that protects all lists. Cc: [email protected] Reported-by: Jouni Malinen <[email protected]> Fixes: 6cd536f ("cfg80211: change internal management frame registration API") Link: https://lore.kernel.org/r/20211025133111.5cf733eab0f4.I7b0abb0494ab712f74e2efcd24bb31ac33f7eee9@changeid Signed-off-by: Johannes Berg <[email protected]>
1 parent 95a359c commit 09b1d5d

File tree

4 files changed

+17
-15
lines changed

4 files changed

+17
-15
lines changed

include/net/cfg80211.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5376,7 +5376,6 @@ static inline void wiphy_unlock(struct wiphy *wiphy)
53765376
* netdev and may otherwise be used by driver read-only, will be update
53775377
* by cfg80211 on change_interface
53785378
* @mgmt_registrations: list of registrations for management frames
5379-
* @mgmt_registrations_lock: lock for the list
53805379
* @mgmt_registrations_need_update: mgmt registrations were updated,
53815380
* need to propagate the update to the driver
53825381
* @mtx: mutex used to lock data in this struct, may be used by drivers
@@ -5423,7 +5422,6 @@ struct wireless_dev {
54235422
u32 identifier;
54245423

54255424
struct list_head mgmt_registrations;
5426-
spinlock_t mgmt_registrations_lock;
54275425
u8 mgmt_registrations_need_update:1;
54285426

54295427
struct mutex mtx;

net/wireless/core.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,7 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
524524
INIT_WORK(&rdev->propagate_cac_done_wk, cfg80211_propagate_cac_done_wk);
525525
INIT_WORK(&rdev->mgmt_registrations_update_wk,
526526
cfg80211_mgmt_registrations_update_wk);
527+
spin_lock_init(&rdev->mgmt_registrations_lock);
527528

528529
#ifdef CONFIG_CFG80211_DEFAULT_PS
529530
rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
@@ -1279,7 +1280,6 @@ void cfg80211_init_wdev(struct wireless_dev *wdev)
12791280
INIT_LIST_HEAD(&wdev->event_list);
12801281
spin_lock_init(&wdev->event_lock);
12811282
INIT_LIST_HEAD(&wdev->mgmt_registrations);
1282-
spin_lock_init(&wdev->mgmt_registrations_lock);
12831283
INIT_LIST_HEAD(&wdev->pmsr_list);
12841284
spin_lock_init(&wdev->pmsr_lock);
12851285
INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);

net/wireless/core.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ struct cfg80211_registered_device {
100100
struct work_struct propagate_cac_done_wk;
101101

102102
struct work_struct mgmt_registrations_update_wk;
103+
/* lock for all wdev lists */
104+
spinlock_t mgmt_registrations_lock;
103105

104106
/* must be last because of the way we do wiphy_priv(),
105107
* and it should at least be aligned to NETDEV_ALIGN */

net/wireless/mlme.c

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -452,9 +452,9 @@ static void cfg80211_mgmt_registrations_update(struct wireless_dev *wdev)
452452

453453
lockdep_assert_held(&rdev->wiphy.mtx);
454454

455-
spin_lock_bh(&wdev->mgmt_registrations_lock);
455+
spin_lock_bh(&rdev->mgmt_registrations_lock);
456456
if (!wdev->mgmt_registrations_need_update) {
457-
spin_unlock_bh(&wdev->mgmt_registrations_lock);
457+
spin_unlock_bh(&rdev->mgmt_registrations_lock);
458458
return;
459459
}
460460

@@ -479,7 +479,7 @@ static void cfg80211_mgmt_registrations_update(struct wireless_dev *wdev)
479479
rcu_read_unlock();
480480

481481
wdev->mgmt_registrations_need_update = 0;
482-
spin_unlock_bh(&wdev->mgmt_registrations_lock);
482+
spin_unlock_bh(&rdev->mgmt_registrations_lock);
483483

484484
rdev_update_mgmt_frame_registrations(rdev, wdev, &upd);
485485
}
@@ -503,6 +503,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
503503
int match_len, bool multicast_rx,
504504
struct netlink_ext_ack *extack)
505505
{
506+
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
506507
struct cfg80211_mgmt_registration *reg, *nreg;
507508
int err = 0;
508509
u16 mgmt_type;
@@ -548,7 +549,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
548549
if (!nreg)
549550
return -ENOMEM;
550551

551-
spin_lock_bh(&wdev->mgmt_registrations_lock);
552+
spin_lock_bh(&rdev->mgmt_registrations_lock);
552553

553554
list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
554555
int mlen = min(match_len, reg->match_len);
@@ -583,15 +584,15 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
583584
list_add(&nreg->list, &wdev->mgmt_registrations);
584585
}
585586
wdev->mgmt_registrations_need_update = 1;
586-
spin_unlock_bh(&wdev->mgmt_registrations_lock);
587+
spin_unlock_bh(&rdev->mgmt_registrations_lock);
587588

588589
cfg80211_mgmt_registrations_update(wdev);
589590

590591
return 0;
591592

592593
out:
593594
kfree(nreg);
594-
spin_unlock_bh(&wdev->mgmt_registrations_lock);
595+
spin_unlock_bh(&rdev->mgmt_registrations_lock);
595596

596597
return err;
597598
}
@@ -602,7 +603,7 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
602603
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
603604
struct cfg80211_mgmt_registration *reg, *tmp;
604605

605-
spin_lock_bh(&wdev->mgmt_registrations_lock);
606+
spin_lock_bh(&rdev->mgmt_registrations_lock);
606607

607608
list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
608609
if (reg->nlportid != nlportid)
@@ -615,7 +616,7 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
615616
schedule_work(&rdev->mgmt_registrations_update_wk);
616617
}
617618

618-
spin_unlock_bh(&wdev->mgmt_registrations_lock);
619+
spin_unlock_bh(&rdev->mgmt_registrations_lock);
619620

620621
if (nlportid && rdev->crit_proto_nlportid == nlportid) {
621622
rdev->crit_proto_nlportid = 0;
@@ -628,15 +629,16 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
628629

629630
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
630631
{
632+
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
631633
struct cfg80211_mgmt_registration *reg, *tmp;
632634

633-
spin_lock_bh(&wdev->mgmt_registrations_lock);
635+
spin_lock_bh(&rdev->mgmt_registrations_lock);
634636
list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
635637
list_del(&reg->list);
636638
kfree(reg);
637639
}
638640
wdev->mgmt_registrations_need_update = 1;
639-
spin_unlock_bh(&wdev->mgmt_registrations_lock);
641+
spin_unlock_bh(&rdev->mgmt_registrations_lock);
640642

641643
cfg80211_mgmt_registrations_update(wdev);
642644
}
@@ -784,7 +786,7 @@ bool cfg80211_rx_mgmt_khz(struct wireless_dev *wdev, int freq, int sig_dbm,
784786
data = buf + ieee80211_hdrlen(mgmt->frame_control);
785787
data_len = len - ieee80211_hdrlen(mgmt->frame_control);
786788

787-
spin_lock_bh(&wdev->mgmt_registrations_lock);
789+
spin_lock_bh(&rdev->mgmt_registrations_lock);
788790

789791
list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
790792
if (reg->frame_type != ftype)
@@ -808,7 +810,7 @@ bool cfg80211_rx_mgmt_khz(struct wireless_dev *wdev, int freq, int sig_dbm,
808810
break;
809811
}
810812

811-
spin_unlock_bh(&wdev->mgmt_registrations_lock);
813+
spin_unlock_bh(&rdev->mgmt_registrations_lock);
812814

813815
trace_cfg80211_return_bool(result);
814816
return result;

0 commit comments

Comments
 (0)