aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-12-03 18:48:59 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-01-03 07:01:28 -0500
commit6913b49a5071064f49f7a74b432286fa735f7612 (patch)
tree15b54b8dd9d5eef07e8a9366b3f9e391043499f1
parent540f6f2cc545da9ae2baa9faa3152fc550bedb57 (diff)
regulatory: fix reg_is_valid_request handling
There's a bug with the world regulatory domain, it can be updated any time which is different from all other regdomains that can only be updated once after a request for them. Fix this by adding a check for "processed" to the reg_is_valid_request() function and clear that when doing a request. While looking at this I also found another locking bug, last_request is protected by the reg_mutex not the cfg80211_mutex so the code in nl80211 is racy. Remove that code as it only tries to prevent an allocation in an error case, which isn't necessary. Then the function can also become static and locking in nl80211 can have a smaller scope. Also change __set_regdom() to do the checks earlier and not different for world/other regdomains. Acked-by: Luis R. Rodriguez <mcgrof@do-not-panic.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--net/wireless/nl80211.c18
-rw-r--r--net/wireless/reg.c21
-rw-r--r--net/wireless/reg.h1
3 files changed, 17 insertions, 23 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 9c2c91845be7..b387deaf1132 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -4265,21 +4265,12 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
4265 return -EINVAL; 4265 return -EINVAL;
4266 } 4266 }
4267 4267
4268 mutex_lock(&cfg80211_mutex);
4269
4270 if (!reg_is_valid_request(alpha2)) {
4271 r = -EINVAL;
4272 goto bad_reg;
4273 }
4274
4275 size_of_regd = sizeof(struct ieee80211_regdomain) + 4268 size_of_regd = sizeof(struct ieee80211_regdomain) +
4276 num_rules * sizeof(struct ieee80211_reg_rule); 4269 num_rules * sizeof(struct ieee80211_reg_rule);
4277 4270
4278 rd = kzalloc(size_of_regd, GFP_KERNEL); 4271 rd = kzalloc(size_of_regd, GFP_KERNEL);
4279 if (!rd) { 4272 if (!rd)
4280 r = -ENOMEM; 4273 return -ENOMEM;
4281 goto bad_reg;
4282 }
4283 4274
4284 rd->n_reg_rules = num_rules; 4275 rd->n_reg_rules = num_rules;
4285 rd->alpha2[0] = alpha2[0]; 4276 rd->alpha2[0] = alpha2[0];
@@ -4309,11 +4300,14 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
4309 } 4300 }
4310 } 4301 }
4311 4302
4303 mutex_lock(&cfg80211_mutex);
4304
4312 r = set_regdom(rd); 4305 r = set_regdom(rd);
4306 /* set_regdom took ownership */
4313 rd = NULL; 4307 rd = NULL;
4308 mutex_unlock(&cfg80211_mutex);
4314 4309
4315 bad_reg: 4310 bad_reg:
4316 mutex_unlock(&cfg80211_mutex);
4317 kfree(rd); 4311 kfree(rd);
4318 return r; 4312 return r;
4319} 4313}
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 752729ecd701..b3f94c957d1d 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -425,12 +425,16 @@ static int call_crda(const char *alpha2)
425 return kobject_uevent(&reg_pdev->dev.kobj, KOBJ_CHANGE); 425 return kobject_uevent(&reg_pdev->dev.kobj, KOBJ_CHANGE);
426} 426}
427 427
428/* Used by nl80211 before kmalloc'ing our regulatory domain */ 428static bool reg_is_valid_request(const char *alpha2)
429bool reg_is_valid_request(const char *alpha2)
430{ 429{
430 assert_reg_lock();
431
431 if (!last_request) 432 if (!last_request)
432 return false; 433 return false;
433 434
435 if (last_request->processed)
436 return false;
437
434 return alpha2_equal(last_request->alpha2, alpha2); 438 return alpha2_equal(last_request->alpha2, alpha2);
435} 439}
436 440
@@ -1470,6 +1474,7 @@ new_request:
1470 1474
1471 last_request = pending_request; 1475 last_request = pending_request;
1472 last_request->intersect = intersect; 1476 last_request->intersect = intersect;
1477 last_request->processed = false;
1473 1478
1474 pending_request = NULL; 1479 pending_request = NULL;
1475 1480
@@ -2060,11 +2065,13 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
2060 const struct ieee80211_regdomain *regd; 2065 const struct ieee80211_regdomain *regd;
2061 const struct ieee80211_regdomain *intersected_rd = NULL; 2066 const struct ieee80211_regdomain *intersected_rd = NULL;
2062 struct wiphy *request_wiphy; 2067 struct wiphy *request_wiphy;
2068
2063 /* Some basic sanity checks first */ 2069 /* Some basic sanity checks first */
2064 2070
2071 if (!reg_is_valid_request(rd->alpha2))
2072 return -EINVAL;
2073
2065 if (is_world_regdom(rd->alpha2)) { 2074 if (is_world_regdom(rd->alpha2)) {
2066 if (WARN_ON(!reg_is_valid_request(rd->alpha2)))
2067 return -EINVAL;
2068 update_world_regdomain(rd); 2075 update_world_regdomain(rd);
2069 return 0; 2076 return 0;
2070 } 2077 }
@@ -2073,9 +2080,6 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
2073 !is_unknown_alpha2(rd->alpha2)) 2080 !is_unknown_alpha2(rd->alpha2))
2074 return -EINVAL; 2081 return -EINVAL;
2075 2082
2076 if (!last_request)
2077 return -EINVAL;
2078
2079 /* 2083 /*
2080 * Lets only bother proceeding on the same alpha2 if the current 2084 * Lets only bother proceeding on the same alpha2 if the current
2081 * rd is non static (it means CRDA was present and was used last) 2085 * rd is non static (it means CRDA was present and was used last)
@@ -2097,9 +2101,6 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
2097 * internal EEPROM data 2101 * internal EEPROM data
2098 */ 2102 */
2099 2103
2100 if (WARN_ON(!reg_is_valid_request(rd->alpha2)))
2101 return -EINVAL;
2102
2103 if (!is_valid_rd(rd)) { 2104 if (!is_valid_rd(rd)) {
2104 pr_err("Invalid regulatory domain detected:\n"); 2105 pr_err("Invalid regulatory domain detected:\n");
2105 print_regdomain_info(rd); 2106 print_regdomain_info(rd);
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index 37891e813a74..d391b50d2829 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -19,7 +19,6 @@
19extern const struct ieee80211_regdomain *cfg80211_regdomain; 19extern const struct ieee80211_regdomain *cfg80211_regdomain;
20 20
21bool is_world_regdom(const char *alpha2); 21bool is_world_regdom(const char *alpha2);
22bool reg_is_valid_request(const char *alpha2);
23bool reg_supported_dfs_region(u8 dfs_region); 22bool reg_supported_dfs_region(u8 dfs_region);
24 23
25int regulatory_hint_user(const char *alpha2, 24int regulatory_hint_user(const char *alpha2,