Commit afe8ca11 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

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.net


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 424a4f52 689a0a9f
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -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
 *	by cfg80211 on change_interface
 * @mgmt_registrations: list of registrations for management frames
 * @mgmt_registrations_lock: lock for the list
 * @mgmt_registrations_need_update: mgmt registrations were updated,
 *	need to propagate the update to the driver
 * @mtx: mutex used to lock data in this struct, may be used by drivers
@@ -5423,7 +5422,6 @@ struct wireless_dev {
	u32 identifier;

	struct list_head mgmt_registrations;
	spinlock_t mgmt_registrations_lock;
	u8 mgmt_registrations_need_update:1;

	struct mutex mtx;
+1 −1
Original line number Diff line number Diff line
@@ -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->mgmt_registrations_update_wk,
		  cfg80211_mgmt_registrations_update_wk);
	spin_lock_init(&rdev->mgmt_registrations_lock);

#ifdef CONFIG_CFG80211_DEFAULT_PS
	rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
@@ -1279,7 +1280,6 @@ void cfg80211_init_wdev(struct wireless_dev *wdev)
	INIT_LIST_HEAD(&wdev->event_list);
	spin_lock_init(&wdev->event_lock);
	INIT_LIST_HEAD(&wdev->mgmt_registrations);
	spin_lock_init(&wdev->mgmt_registrations_lock);
	INIT_LIST_HEAD(&wdev->pmsr_list);
	spin_lock_init(&wdev->pmsr_lock);
	INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);
+2 −0
Original line number Diff line number Diff line
@@ -100,6 +100,8 @@ struct cfg80211_registered_device {
	struct work_struct propagate_cac_done_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(),
	 * and it should at least be aligned to NETDEV_ALIGN */
+14 −12
Original line number Diff line number Diff line
@@ -452,9 +452,9 @@ static void cfg80211_mgmt_registrations_update(struct wireless_dev *wdev)

	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) {
		spin_unlock_bh(&wdev->mgmt_registrations_lock);
		spin_unlock_bh(&rdev->mgmt_registrations_lock);
		return;
	}

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

	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);
}
@@ -503,6 +503,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
				int match_len, bool multicast_rx,
				struct netlink_ext_ack *extack)
{
	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
	struct cfg80211_mgmt_registration *reg, *nreg;
	int err = 0;
	u16 mgmt_type;
@@ -548,7 +549,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
	if (!nreg)
		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) {
		int mlen = min(match_len, reg->match_len);
@@ -583,7 +584,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
		list_add(&nreg->list, &wdev->mgmt_registrations);
	}
	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);

@@ -591,7 +592,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,

 out:
	kfree(nreg);
	spin_unlock_bh(&wdev->mgmt_registrations_lock);
	spin_unlock_bh(&rdev->mgmt_registrations_lock);

	return err;
}
@@ -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_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) {
		if (reg->nlportid != nlportid)
@@ -615,7 +616,7 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
		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) {
		rdev->crit_proto_nlportid = 0;
@@ -628,15 +629,16 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)

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;

	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_del(&reg->list);
		kfree(reg);
	}
	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);
}
@@ -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_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) {
		if (reg->frame_type != ftype)
@@ -808,7 +810,7 @@ bool cfg80211_rx_mgmt_khz(struct wireless_dev *wdev, int freq, int sig_dbm,
		break;
	}

	spin_unlock_bh(&wdev->mgmt_registrations_lock);
	spin_unlock_bh(&rdev->mgmt_registrations_lock);

	trace_cfg80211_return_bool(result);
	return result;
+7 −7
Original line number Diff line number Diff line
@@ -1028,6 +1028,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
	    !(rdev->wiphy.interface_modes & (1 << ntype)))
		return -EOPNOTSUPP;

	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 ||
@@ -1035,7 +1036,6 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
		     ntype == NL80211_IFTYPE_P2P_CLIENT))
			return -EBUSY;

	if (ntype != otype) {
		dev->ieee80211_ptr->use_4addr = false;
		dev->ieee80211_ptr->mesh_id_up_len = 0;
		wdev_lock(dev->ieee80211_ptr);