diff options
author | Jukka Rissanen <jukka.rissanen@linux.intel.com> | 2014-12-15 06:25:38 -0500 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2014-12-18 08:38:09 -0500 |
commit | 31a60ed1e95ab8afbadb65599bef12b195080a0c (patch) | |
tree | 0be2e75a0554a2c1e1c01c6708af80c7388b6e74 /net/wireless | |
parent | 0f8b82456178d558f14011e06ebf9af937c4b197 (diff) |
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>
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/core.c | 10 | ||||
-rw-r--r-- | net/wireless/core.h | 2 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 19 | ||||
-rw-r--r-- | net/wireless/scan.c | 13 |
4 files changed, 27 insertions, 17 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c index b661fcce7e3c..0743449405ca 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -867,6 +867,7 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev, | |||
867 | struct wireless_dev *wdev) | 867 | struct wireless_dev *wdev) |
868 | { | 868 | { |
869 | struct net_device *dev = wdev->netdev; | 869 | struct net_device *dev = wdev->netdev; |
870 | struct cfg80211_sched_scan_request *sched_scan_req; | ||
870 | 871 | ||
871 | ASSERT_RTNL(); | 872 | ASSERT_RTNL(); |
872 | ASSERT_WDEV_LOCK(wdev); | 873 | ASSERT_WDEV_LOCK(wdev); |
@@ -877,7 +878,8 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev, | |||
877 | break; | 878 | break; |
878 | case NL80211_IFTYPE_P2P_CLIENT: | 879 | case NL80211_IFTYPE_P2P_CLIENT: |
879 | case NL80211_IFTYPE_STATION: | 880 | case NL80211_IFTYPE_STATION: |
880 | if (rdev->sched_scan_req && dev == rdev->sched_scan_req->dev) | 881 | sched_scan_req = rtnl_dereference(rdev->sched_scan_req); |
882 | if (sched_scan_req && dev == sched_scan_req->dev) | ||
881 | __cfg80211_stop_sched_scan(rdev, false); | 883 | __cfg80211_stop_sched_scan(rdev, false); |
882 | 884 | ||
883 | #ifdef CONFIG_CFG80211_WEXT | 885 | #ifdef CONFIG_CFG80211_WEXT |
@@ -956,6 +958,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, | |||
956 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); | 958 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
957 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 959 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
958 | struct cfg80211_registered_device *rdev; | 960 | struct cfg80211_registered_device *rdev; |
961 | struct cfg80211_sched_scan_request *sched_scan_req; | ||
959 | 962 | ||
960 | if (!wdev) | 963 | if (!wdev) |
961 | return NOTIFY_DONE; | 964 | return NOTIFY_DONE; |
@@ -1021,8 +1024,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, | |||
1021 | ___cfg80211_scan_done(rdev, false); | 1024 | ___cfg80211_scan_done(rdev, false); |
1022 | } | 1025 | } |
1023 | 1026 | ||
1024 | if (WARN_ON(rdev->sched_scan_req && | 1027 | sched_scan_req = rtnl_dereference(rdev->sched_scan_req); |
1025 | rdev->sched_scan_req->dev == wdev->netdev)) { | 1028 | if (WARN_ON(sched_scan_req && |
1029 | sched_scan_req->dev == wdev->netdev)) { | ||
1026 | __cfg80211_stop_sched_scan(rdev, false); | 1030 | __cfg80211_stop_sched_scan(rdev, false); |
1027 | } | 1031 | } |
1028 | 1032 | ||
diff --git a/net/wireless/core.h b/net/wireless/core.h index e87cae57a83b..e82030c32311 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -70,7 +70,7 @@ struct cfg80211_registered_device { | |||
70 | u32 bss_generation; | 70 | u32 bss_generation; |
71 | struct cfg80211_scan_request *scan_req; /* protected by RTNL */ | 71 | struct cfg80211_scan_request *scan_req; /* protected by RTNL */ |
72 | struct sk_buff *scan_msg; | 72 | struct sk_buff *scan_msg; |
73 | struct cfg80211_sched_scan_request *sched_scan_req; | 73 | struct cfg80211_sched_scan_request __rcu *sched_scan_req; |
74 | unsigned long suspend_at; | 74 | unsigned long suspend_at; |
75 | struct work_struct scan_done_wk; | 75 | struct work_struct scan_done_wk; |
76 | struct work_struct sched_scan_results_wk; | 76 | struct work_struct sched_scan_results_wk; |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5b1907f4c181..bacdf22fa472 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -6190,6 +6190,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, | |||
6190 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 6190 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
6191 | struct net_device *dev = info->user_ptr[1]; | 6191 | struct net_device *dev = info->user_ptr[1]; |
6192 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 6192 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
6193 | struct cfg80211_sched_scan_request *sched_scan_req; | ||
6193 | int err; | 6194 | int err; |
6194 | 6195 | ||
6195 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || | 6196 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || |
@@ -6199,27 +6200,29 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, | |||
6199 | if (rdev->sched_scan_req) | 6200 | if (rdev->sched_scan_req) |
6200 | return -EINPROGRESS; | 6201 | return -EINPROGRESS; |
6201 | 6202 | ||
6202 | rdev->sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev, | 6203 | sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev, |
6203 | info->attrs); | 6204 | info->attrs); |
6204 | err = PTR_ERR_OR_ZERO(rdev->sched_scan_req); | 6205 | |
6206 | err = PTR_ERR_OR_ZERO(sched_scan_req); | ||
6205 | if (err) | 6207 | if (err) |
6206 | goto out_err; | 6208 | goto out_err; |
6207 | 6209 | ||
6208 | err = rdev_sched_scan_start(rdev, dev, rdev->sched_scan_req); | 6210 | err = rdev_sched_scan_start(rdev, dev, sched_scan_req); |
6209 | if (err) | 6211 | if (err) |
6210 | goto out_free; | 6212 | goto out_free; |
6211 | 6213 | ||
6212 | rdev->sched_scan_req->dev = dev; | 6214 | sched_scan_req->dev = dev; |
6213 | rdev->sched_scan_req->wiphy = &rdev->wiphy; | 6215 | sched_scan_req->wiphy = &rdev->wiphy; |
6216 | |||
6217 | rcu_assign_pointer(rdev->sched_scan_req, sched_scan_req); | ||
6214 | 6218 | ||
6215 | nl80211_send_sched_scan(rdev, dev, | 6219 | nl80211_send_sched_scan(rdev, dev, |
6216 | NL80211_CMD_START_SCHED_SCAN); | 6220 | NL80211_CMD_START_SCHED_SCAN); |
6217 | return 0; | 6221 | return 0; |
6218 | 6222 | ||
6219 | out_free: | 6223 | out_free: |
6220 | kfree(rdev->sched_scan_req); | 6224 | kfree(sched_scan_req); |
6221 | out_err: | 6225 | out_err: |
6222 | rdev->sched_scan_req = NULL; | ||
6223 | return err; | 6226 | return err; |
6224 | } | 6227 | } |
6225 | 6228 | ||
diff --git a/net/wireless/scan.c b/net/wireless/scan.c index bda39f149810..c705c3e2b751 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c | |||
@@ -257,7 +257,7 @@ void __cfg80211_sched_scan_results(struct work_struct *wk) | |||
257 | 257 | ||
258 | rtnl_lock(); | 258 | rtnl_lock(); |
259 | 259 | ||
260 | request = rdev->sched_scan_req; | 260 | request = rtnl_dereference(rdev->sched_scan_req); |
261 | 261 | ||
262 | /* we don't have sched_scan_req anymore if the scan is stopping */ | 262 | /* we don't have sched_scan_req anymore if the scan is stopping */ |
263 | if (request) { | 263 | if (request) { |
@@ -279,7 +279,8 @@ void cfg80211_sched_scan_results(struct wiphy *wiphy) | |||
279 | { | 279 | { |
280 | trace_cfg80211_sched_scan_results(wiphy); | 280 | trace_cfg80211_sched_scan_results(wiphy); |
281 | /* ignore if we're not scanning */ | 281 | /* ignore if we're not scanning */ |
282 | if (wiphy_to_rdev(wiphy)->sched_scan_req) | 282 | |
283 | if (rcu_access_pointer(wiphy_to_rdev(wiphy)->sched_scan_req)) | ||
283 | queue_work(cfg80211_wq, | 284 | queue_work(cfg80211_wq, |
284 | &wiphy_to_rdev(wiphy)->sched_scan_results_wk); | 285 | &wiphy_to_rdev(wiphy)->sched_scan_results_wk); |
285 | } | 286 | } |
@@ -308,6 +309,7 @@ EXPORT_SYMBOL(cfg80211_sched_scan_stopped); | |||
308 | int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, | 309 | int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, |
309 | bool driver_initiated) | 310 | bool driver_initiated) |
310 | { | 311 | { |
312 | struct cfg80211_sched_scan_request *sched_scan_req; | ||
311 | struct net_device *dev; | 313 | struct net_device *dev; |
312 | 314 | ||
313 | ASSERT_RTNL(); | 315 | ASSERT_RTNL(); |
@@ -315,7 +317,8 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, | |||
315 | if (!rdev->sched_scan_req) | 317 | if (!rdev->sched_scan_req) |
316 | return -ENOENT; | 318 | return -ENOENT; |
317 | 319 | ||
318 | dev = rdev->sched_scan_req->dev; | 320 | sched_scan_req = rtnl_dereference(rdev->sched_scan_req); |
321 | dev = sched_scan_req->dev; | ||
319 | 322 | ||
320 | if (!driver_initiated) { | 323 | if (!driver_initiated) { |
321 | int err = rdev_sched_scan_stop(rdev, dev); | 324 | int err = rdev_sched_scan_stop(rdev, dev); |
@@ -325,8 +328,8 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, | |||
325 | 328 | ||
326 | nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED); | 329 | nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED); |
327 | 330 | ||
328 | kfree(rdev->sched_scan_req); | 331 | RCU_INIT_POINTER(rdev->sched_scan_req, NULL); |
329 | rdev->sched_scan_req = NULL; | 332 | kfree_rcu(sched_scan_req, rcu_head); |
330 | 333 | ||
331 | return 0; | 334 | return 0; |
332 | } | 335 | } |