mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	nl80211: Convert sched_scan_req pointer to RCU pointer
Because of possible races when accessing sched_scan_req pointer in rdev, the sched_scan_req is converted to RCU pointer. Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
		
							parent
							
								
									0f8b824561
								
							
						
					
					
						commit
						31a60ed1e9
					
				
					 5 changed files with 29 additions and 17 deletions
				
			
		| 
						 | 
					@ -1516,6 +1516,7 @@ struct cfg80211_match_set {
 | 
				
			||||||
 * @mac_addr_mask: MAC address mask used with randomisation, bits that
 | 
					 * @mac_addr_mask: MAC address mask used with randomisation, bits that
 | 
				
			||||||
 *	are 0 in the mask should be randomised, bits that are 1 should
 | 
					 *	are 0 in the mask should be randomised, bits that are 1 should
 | 
				
			||||||
 *	be taken from the @mac_addr
 | 
					 *	be taken from the @mac_addr
 | 
				
			||||||
 | 
					 * @rcu_head: RCU callback used to free the struct
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct cfg80211_sched_scan_request {
 | 
					struct cfg80211_sched_scan_request {
 | 
				
			||||||
	struct cfg80211_ssid *ssids;
 | 
						struct cfg80211_ssid *ssids;
 | 
				
			||||||
| 
						 | 
					@ -1537,6 +1538,7 @@ struct cfg80211_sched_scan_request {
 | 
				
			||||||
	struct wiphy *wiphy;
 | 
						struct wiphy *wiphy;
 | 
				
			||||||
	struct net_device *dev;
 | 
						struct net_device *dev;
 | 
				
			||||||
	unsigned long scan_start;
 | 
						unsigned long scan_start;
 | 
				
			||||||
 | 
						struct rcu_head rcu_head;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* keep last */
 | 
						/* keep last */
 | 
				
			||||||
	struct ieee80211_channel *channels[0];
 | 
						struct ieee80211_channel *channels[0];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -867,6 +867,7 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,
 | 
				
			||||||
		      struct wireless_dev *wdev)
 | 
							      struct wireless_dev *wdev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct net_device *dev = wdev->netdev;
 | 
						struct net_device *dev = wdev->netdev;
 | 
				
			||||||
 | 
						struct cfg80211_sched_scan_request *sched_scan_req;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RTNL();
 | 
						ASSERT_RTNL();
 | 
				
			||||||
	ASSERT_WDEV_LOCK(wdev);
 | 
						ASSERT_WDEV_LOCK(wdev);
 | 
				
			||||||
| 
						 | 
					@ -877,7 +878,8 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case NL80211_IFTYPE_P2P_CLIENT:
 | 
						case NL80211_IFTYPE_P2P_CLIENT:
 | 
				
			||||||
	case NL80211_IFTYPE_STATION:
 | 
						case NL80211_IFTYPE_STATION:
 | 
				
			||||||
		if (rdev->sched_scan_req && dev == rdev->sched_scan_req->dev)
 | 
							sched_scan_req = rtnl_dereference(rdev->sched_scan_req);
 | 
				
			||||||
 | 
							if (sched_scan_req && dev == sched_scan_req->dev)
 | 
				
			||||||
			__cfg80211_stop_sched_scan(rdev, false);
 | 
								__cfg80211_stop_sched_scan(rdev, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_CFG80211_WEXT
 | 
					#ifdef CONFIG_CFG80211_WEXT
 | 
				
			||||||
| 
						 | 
					@ -956,6 +958,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
 | 
				
			||||||
	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 | 
						struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 | 
				
			||||||
	struct wireless_dev *wdev = dev->ieee80211_ptr;
 | 
						struct wireless_dev *wdev = dev->ieee80211_ptr;
 | 
				
			||||||
	struct cfg80211_registered_device *rdev;
 | 
						struct cfg80211_registered_device *rdev;
 | 
				
			||||||
 | 
						struct cfg80211_sched_scan_request *sched_scan_req;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!wdev)
 | 
						if (!wdev)
 | 
				
			||||||
		return NOTIFY_DONE;
 | 
							return NOTIFY_DONE;
 | 
				
			||||||
| 
						 | 
					@ -1021,8 +1024,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
 | 
				
			||||||
			___cfg80211_scan_done(rdev, false);
 | 
								___cfg80211_scan_done(rdev, false);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (WARN_ON(rdev->sched_scan_req &&
 | 
							sched_scan_req = rtnl_dereference(rdev->sched_scan_req);
 | 
				
			||||||
			    rdev->sched_scan_req->dev == wdev->netdev)) {
 | 
							if (WARN_ON(sched_scan_req &&
 | 
				
			||||||
 | 
								    sched_scan_req->dev == wdev->netdev)) {
 | 
				
			||||||
			__cfg80211_stop_sched_scan(rdev, false);
 | 
								__cfg80211_stop_sched_scan(rdev, false);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,7 +70,7 @@ struct cfg80211_registered_device {
 | 
				
			||||||
	u32 bss_generation;
 | 
						u32 bss_generation;
 | 
				
			||||||
	struct cfg80211_scan_request *scan_req; /* protected by RTNL */
 | 
						struct cfg80211_scan_request *scan_req; /* protected by RTNL */
 | 
				
			||||||
	struct sk_buff *scan_msg;
 | 
						struct sk_buff *scan_msg;
 | 
				
			||||||
	struct cfg80211_sched_scan_request *sched_scan_req;
 | 
						struct cfg80211_sched_scan_request __rcu *sched_scan_req;
 | 
				
			||||||
	unsigned long suspend_at;
 | 
						unsigned long suspend_at;
 | 
				
			||||||
	struct work_struct scan_done_wk;
 | 
						struct work_struct scan_done_wk;
 | 
				
			||||||
	struct work_struct sched_scan_results_wk;
 | 
						struct work_struct sched_scan_results_wk;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6190,6 +6190,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
 | 
				
			||||||
	struct cfg80211_registered_device *rdev = info->user_ptr[0];
 | 
						struct cfg80211_registered_device *rdev = info->user_ptr[0];
 | 
				
			||||||
	struct net_device *dev = info->user_ptr[1];
 | 
						struct net_device *dev = info->user_ptr[1];
 | 
				
			||||||
	struct wireless_dev *wdev = dev->ieee80211_ptr;
 | 
						struct wireless_dev *wdev = dev->ieee80211_ptr;
 | 
				
			||||||
 | 
						struct cfg80211_sched_scan_request *sched_scan_req;
 | 
				
			||||||
	int err;
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
 | 
						if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
 | 
				
			||||||
| 
						 | 
					@ -6199,27 +6200,29 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
 | 
				
			||||||
	if (rdev->sched_scan_req)
 | 
						if (rdev->sched_scan_req)
 | 
				
			||||||
		return -EINPROGRESS;
 | 
							return -EINPROGRESS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rdev->sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
 | 
						sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
 | 
				
			||||||
							info->attrs);
 | 
											  info->attrs);
 | 
				
			||||||
	err = PTR_ERR_OR_ZERO(rdev->sched_scan_req);
 | 
					
 | 
				
			||||||
 | 
						err = PTR_ERR_OR_ZERO(sched_scan_req);
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
		goto out_err;
 | 
							goto out_err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = rdev_sched_scan_start(rdev, dev, rdev->sched_scan_req);
 | 
						err = rdev_sched_scan_start(rdev, dev, sched_scan_req);
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
		goto out_free;
 | 
							goto out_free;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rdev->sched_scan_req->dev = dev;
 | 
						sched_scan_req->dev = dev;
 | 
				
			||||||
	rdev->sched_scan_req->wiphy = &rdev->wiphy;
 | 
						sched_scan_req->wiphy = &rdev->wiphy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rcu_assign_pointer(rdev->sched_scan_req, sched_scan_req);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nl80211_send_sched_scan(rdev, dev,
 | 
						nl80211_send_sched_scan(rdev, dev,
 | 
				
			||||||
				NL80211_CMD_START_SCHED_SCAN);
 | 
									NL80211_CMD_START_SCHED_SCAN);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out_free:
 | 
					out_free:
 | 
				
			||||||
	kfree(rdev->sched_scan_req);
 | 
						kfree(sched_scan_req);
 | 
				
			||||||
out_err:
 | 
					out_err:
 | 
				
			||||||
	rdev->sched_scan_req = NULL;
 | 
					 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -257,7 +257,7 @@ void __cfg80211_sched_scan_results(struct work_struct *wk)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rtnl_lock();
 | 
						rtnl_lock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	request = rdev->sched_scan_req;
 | 
						request = rtnl_dereference(rdev->sched_scan_req);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* we don't have sched_scan_req anymore if the scan is stopping */
 | 
						/* we don't have sched_scan_req anymore if the scan is stopping */
 | 
				
			||||||
	if (request) {
 | 
						if (request) {
 | 
				
			||||||
| 
						 | 
					@ -279,7 +279,8 @@ void cfg80211_sched_scan_results(struct wiphy *wiphy)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	trace_cfg80211_sched_scan_results(wiphy);
 | 
						trace_cfg80211_sched_scan_results(wiphy);
 | 
				
			||||||
	/* ignore if we're not scanning */
 | 
						/* ignore if we're not scanning */
 | 
				
			||||||
	if (wiphy_to_rdev(wiphy)->sched_scan_req)
 | 
					
 | 
				
			||||||
 | 
						if (rcu_access_pointer(wiphy_to_rdev(wiphy)->sched_scan_req))
 | 
				
			||||||
		queue_work(cfg80211_wq,
 | 
							queue_work(cfg80211_wq,
 | 
				
			||||||
			   &wiphy_to_rdev(wiphy)->sched_scan_results_wk);
 | 
								   &wiphy_to_rdev(wiphy)->sched_scan_results_wk);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -308,6 +309,7 @@ EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
 | 
				
			||||||
int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
 | 
					int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
 | 
				
			||||||
			       bool driver_initiated)
 | 
								       bool driver_initiated)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct cfg80211_sched_scan_request *sched_scan_req;
 | 
				
			||||||
	struct net_device *dev;
 | 
						struct net_device *dev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RTNL();
 | 
						ASSERT_RTNL();
 | 
				
			||||||
| 
						 | 
					@ -315,7 +317,8 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
 | 
				
			||||||
	if (!rdev->sched_scan_req)
 | 
						if (!rdev->sched_scan_req)
 | 
				
			||||||
		return -ENOENT;
 | 
							return -ENOENT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dev = rdev->sched_scan_req->dev;
 | 
						sched_scan_req = rtnl_dereference(rdev->sched_scan_req);
 | 
				
			||||||
 | 
						dev = sched_scan_req->dev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!driver_initiated) {
 | 
						if (!driver_initiated) {
 | 
				
			||||||
		int err = rdev_sched_scan_stop(rdev, dev);
 | 
							int err = rdev_sched_scan_stop(rdev, dev);
 | 
				
			||||||
| 
						 | 
					@ -325,8 +328,8 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED);
 | 
						nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kfree(rdev->sched_scan_req);
 | 
						RCU_INIT_POINTER(rdev->sched_scan_req, NULL);
 | 
				
			||||||
	rdev->sched_scan_req = NULL;
 | 
						kfree_rcu(sched_scan_req, rcu_head);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue