Commit afe8ca11 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge tag 'mac80211-for-net-2021-10-27' of...

Merge tag 'mac80211-for-net-2021-10-27' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211

Johannes Berg says:

====================
Two fixes:
 * bridge vs. 4-addr mode check was wrong
 * management frame registrations locking was
   wrong, causing list corruption/crashes
====================

Link: https://lore.kernel.org/r/20211027143756.91711-1-johannes@sipsolutions.netSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 424a4f52 689a0a9f
...@@ -5376,7 +5376,6 @@ static inline void wiphy_unlock(struct wiphy *wiphy) ...@@ -5376,7 +5376,6 @@ static inline void wiphy_unlock(struct wiphy *wiphy)
* netdev and may otherwise be used by driver read-only, will be update * netdev and may otherwise be used by driver read-only, will be update
* by cfg80211 on change_interface * by cfg80211 on change_interface
* @mgmt_registrations: list of registrations for management frames * @mgmt_registrations: list of registrations for management frames
* @mgmt_registrations_lock: lock for the list
* @mgmt_registrations_need_update: mgmt registrations were updated, * @mgmt_registrations_need_update: mgmt registrations were updated,
* need to propagate the update to the driver * need to propagate the update to the driver
* @mtx: mutex used to lock data in this struct, may be used by drivers * @mtx: mutex used to lock data in this struct, may be used by drivers
...@@ -5423,7 +5422,6 @@ struct wireless_dev { ...@@ -5423,7 +5422,6 @@ struct wireless_dev {
u32 identifier; u32 identifier;
struct list_head mgmt_registrations; struct list_head mgmt_registrations;
spinlock_t mgmt_registrations_lock;
u8 mgmt_registrations_need_update:1; u8 mgmt_registrations_need_update:1;
struct mutex mtx; struct mutex mtx;
......
...@@ -524,6 +524,7 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, ...@@ -524,6 +524,7 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
INIT_WORK(&rdev->propagate_cac_done_wk, cfg80211_propagate_cac_done_wk); INIT_WORK(&rdev->propagate_cac_done_wk, cfg80211_propagate_cac_done_wk);
INIT_WORK(&rdev->mgmt_registrations_update_wk, INIT_WORK(&rdev->mgmt_registrations_update_wk,
cfg80211_mgmt_registrations_update_wk); cfg80211_mgmt_registrations_update_wk);
spin_lock_init(&rdev->mgmt_registrations_lock);
#ifdef CONFIG_CFG80211_DEFAULT_PS #ifdef CONFIG_CFG80211_DEFAULT_PS
rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
...@@ -1279,7 +1280,6 @@ void cfg80211_init_wdev(struct wireless_dev *wdev) ...@@ -1279,7 +1280,6 @@ void cfg80211_init_wdev(struct wireless_dev *wdev)
INIT_LIST_HEAD(&wdev->event_list); INIT_LIST_HEAD(&wdev->event_list);
spin_lock_init(&wdev->event_lock); spin_lock_init(&wdev->event_lock);
INIT_LIST_HEAD(&wdev->mgmt_registrations); INIT_LIST_HEAD(&wdev->mgmt_registrations);
spin_lock_init(&wdev->mgmt_registrations_lock);
INIT_LIST_HEAD(&wdev->pmsr_list); INIT_LIST_HEAD(&wdev->pmsr_list);
spin_lock_init(&wdev->pmsr_lock); spin_lock_init(&wdev->pmsr_lock);
INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk); INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);
......
...@@ -100,6 +100,8 @@ struct cfg80211_registered_device { ...@@ -100,6 +100,8 @@ struct cfg80211_registered_device {
struct work_struct propagate_cac_done_wk; struct work_struct propagate_cac_done_wk;
struct work_struct mgmt_registrations_update_wk; struct work_struct mgmt_registrations_update_wk;
/* lock for all wdev lists */
spinlock_t mgmt_registrations_lock;
/* must be last because of the way we do wiphy_priv(), /* must be last because of the way we do wiphy_priv(),
* and it should at least be aligned to NETDEV_ALIGN */ * and it should at least be aligned to NETDEV_ALIGN */
......
...@@ -452,9 +452,9 @@ static void cfg80211_mgmt_registrations_update(struct wireless_dev *wdev) ...@@ -452,9 +452,9 @@ static void cfg80211_mgmt_registrations_update(struct wireless_dev *wdev)
lockdep_assert_held(&rdev->wiphy.mtx); lockdep_assert_held(&rdev->wiphy.mtx);
spin_lock_bh(&wdev->mgmt_registrations_lock); spin_lock_bh(&rdev->mgmt_registrations_lock);
if (!wdev->mgmt_registrations_need_update) { if (!wdev->mgmt_registrations_need_update) {
spin_unlock_bh(&wdev->mgmt_registrations_lock); spin_unlock_bh(&rdev->mgmt_registrations_lock);
return; return;
} }
...@@ -479,7 +479,7 @@ static void cfg80211_mgmt_registrations_update(struct wireless_dev *wdev) ...@@ -479,7 +479,7 @@ static void cfg80211_mgmt_registrations_update(struct wireless_dev *wdev)
rcu_read_unlock(); rcu_read_unlock();
wdev->mgmt_registrations_need_update = 0; wdev->mgmt_registrations_need_update = 0;
spin_unlock_bh(&wdev->mgmt_registrations_lock); spin_unlock_bh(&rdev->mgmt_registrations_lock);
rdev_update_mgmt_frame_registrations(rdev, wdev, &upd); rdev_update_mgmt_frame_registrations(rdev, wdev, &upd);
} }
...@@ -503,6 +503,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid, ...@@ -503,6 +503,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
int match_len, bool multicast_rx, int match_len, bool multicast_rx,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
struct cfg80211_mgmt_registration *reg, *nreg; struct cfg80211_mgmt_registration *reg, *nreg;
int err = 0; int err = 0;
u16 mgmt_type; u16 mgmt_type;
...@@ -548,7 +549,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid, ...@@ -548,7 +549,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
if (!nreg) if (!nreg)
return -ENOMEM; return -ENOMEM;
spin_lock_bh(&wdev->mgmt_registrations_lock); spin_lock_bh(&rdev->mgmt_registrations_lock);
list_for_each_entry(reg, &wdev->mgmt_registrations, list) { list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
int mlen = min(match_len, reg->match_len); int mlen = min(match_len, reg->match_len);
...@@ -583,7 +584,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid, ...@@ -583,7 +584,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
list_add(&nreg->list, &wdev->mgmt_registrations); list_add(&nreg->list, &wdev->mgmt_registrations);
} }
wdev->mgmt_registrations_need_update = 1; wdev->mgmt_registrations_need_update = 1;
spin_unlock_bh(&wdev->mgmt_registrations_lock); spin_unlock_bh(&rdev->mgmt_registrations_lock);
cfg80211_mgmt_registrations_update(wdev); cfg80211_mgmt_registrations_update(wdev);
...@@ -591,7 +592,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid, ...@@ -591,7 +592,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
out: out:
kfree(nreg); kfree(nreg);
spin_unlock_bh(&wdev->mgmt_registrations_lock); spin_unlock_bh(&rdev->mgmt_registrations_lock);
return err; return err;
} }
...@@ -602,7 +603,7 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid) ...@@ -602,7 +603,7 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
struct cfg80211_mgmt_registration *reg, *tmp; struct cfg80211_mgmt_registration *reg, *tmp;
spin_lock_bh(&wdev->mgmt_registrations_lock); spin_lock_bh(&rdev->mgmt_registrations_lock);
list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
if (reg->nlportid != nlportid) if (reg->nlportid != nlportid)
...@@ -615,7 +616,7 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid) ...@@ -615,7 +616,7 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
schedule_work(&rdev->mgmt_registrations_update_wk); schedule_work(&rdev->mgmt_registrations_update_wk);
} }
spin_unlock_bh(&wdev->mgmt_registrations_lock); spin_unlock_bh(&rdev->mgmt_registrations_lock);
if (nlportid && rdev->crit_proto_nlportid == nlportid) { if (nlportid && rdev->crit_proto_nlportid == nlportid) {
rdev->crit_proto_nlportid = 0; rdev->crit_proto_nlportid = 0;
...@@ -628,15 +629,16 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid) ...@@ -628,15 +629,16 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
{ {
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
struct cfg80211_mgmt_registration *reg, *tmp; struct cfg80211_mgmt_registration *reg, *tmp;
spin_lock_bh(&wdev->mgmt_registrations_lock); spin_lock_bh(&rdev->mgmt_registrations_lock);
list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
list_del(&reg->list); list_del(&reg->list);
kfree(reg); kfree(reg);
} }
wdev->mgmt_registrations_need_update = 1; wdev->mgmt_registrations_need_update = 1;
spin_unlock_bh(&wdev->mgmt_registrations_lock); spin_unlock_bh(&rdev->mgmt_registrations_lock);
cfg80211_mgmt_registrations_update(wdev); cfg80211_mgmt_registrations_update(wdev);
} }
...@@ -784,7 +786,7 @@ bool cfg80211_rx_mgmt_khz(struct wireless_dev *wdev, int freq, int sig_dbm, ...@@ -784,7 +786,7 @@ bool cfg80211_rx_mgmt_khz(struct wireless_dev *wdev, int freq, int sig_dbm,
data = buf + ieee80211_hdrlen(mgmt->frame_control); data = buf + ieee80211_hdrlen(mgmt->frame_control);
data_len = len - ieee80211_hdrlen(mgmt->frame_control); data_len = len - ieee80211_hdrlen(mgmt->frame_control);
spin_lock_bh(&wdev->mgmt_registrations_lock); spin_lock_bh(&rdev->mgmt_registrations_lock);
list_for_each_entry(reg, &wdev->mgmt_registrations, list) { list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
if (reg->frame_type != ftype) if (reg->frame_type != ftype)
...@@ -808,7 +810,7 @@ bool cfg80211_rx_mgmt_khz(struct wireless_dev *wdev, int freq, int sig_dbm, ...@@ -808,7 +810,7 @@ bool cfg80211_rx_mgmt_khz(struct wireless_dev *wdev, int freq, int sig_dbm,
break; break;
} }
spin_unlock_bh(&wdev->mgmt_registrations_lock); spin_unlock_bh(&rdev->mgmt_registrations_lock);
trace_cfg80211_return_bool(result); trace_cfg80211_return_bool(result);
return result; return result;
......
...@@ -1028,14 +1028,14 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, ...@@ -1028,14 +1028,14 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
!(rdev->wiphy.interface_modes & (1 << ntype))) !(rdev->wiphy.interface_modes & (1 << ntype)))
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* if it's part of a bridge, reject changing type to station/ibss */
if (netif_is_bridge_port(dev) &&
(ntype == NL80211_IFTYPE_ADHOC ||
ntype == NL80211_IFTYPE_STATION ||
ntype == NL80211_IFTYPE_P2P_CLIENT))
return -EBUSY;
if (ntype != otype) { if (ntype != otype) {
/* if it's part of a bridge, reject changing type to station/ibss */
if (netif_is_bridge_port(dev) &&
(ntype == NL80211_IFTYPE_ADHOC ||
ntype == NL80211_IFTYPE_STATION ||
ntype == NL80211_IFTYPE_P2P_CLIENT))
return -EBUSY;
dev->ieee80211_ptr->use_4addr = false; dev->ieee80211_ptr->use_4addr = false;
dev->ieee80211_ptr->mesh_id_up_len = 0; dev->ieee80211_ptr->mesh_id_up_len = 0;
wdev_lock(dev->ieee80211_ptr); wdev_lock(dev->ieee80211_ptr);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment