Commit 6913b49a authored by Johannes Berg's avatar Johannes Berg

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: default avatarLuis R. Rodriguez <mcgrof@do-not-panic.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 540f6f2c
...@@ -4265,21 +4265,12 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) ...@@ -4265,21 +4265,12 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
return -EINVAL; return -EINVAL;
} }
mutex_lock(&cfg80211_mutex);
if (!reg_is_valid_request(alpha2)) {
r = -EINVAL;
goto bad_reg;
}
size_of_regd = sizeof(struct ieee80211_regdomain) + size_of_regd = sizeof(struct ieee80211_regdomain) +
num_rules * sizeof(struct ieee80211_reg_rule); num_rules * sizeof(struct ieee80211_reg_rule);
rd = kzalloc(size_of_regd, GFP_KERNEL); rd = kzalloc(size_of_regd, GFP_KERNEL);
if (!rd) { if (!rd)
r = -ENOMEM; return -ENOMEM;
goto bad_reg;
}
rd->n_reg_rules = num_rules; rd->n_reg_rules = num_rules;
rd->alpha2[0] = alpha2[0]; rd->alpha2[0] = alpha2[0];
...@@ -4309,11 +4300,14 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) ...@@ -4309,11 +4300,14 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
} }
} }
mutex_lock(&cfg80211_mutex);
r = set_regdom(rd); r = set_regdom(rd);
/* set_regdom took ownership */
rd = NULL; rd = NULL;
mutex_unlock(&cfg80211_mutex);
bad_reg: bad_reg:
mutex_unlock(&cfg80211_mutex);
kfree(rd); kfree(rd);
return r; return r;
} }
......
...@@ -425,12 +425,16 @@ static int call_crda(const char *alpha2) ...@@ -425,12 +425,16 @@ static int call_crda(const char *alpha2)
return kobject_uevent(&reg_pdev->dev.kobj, KOBJ_CHANGE); return kobject_uevent(&reg_pdev->dev.kobj, KOBJ_CHANGE);
} }
/* Used by nl80211 before kmalloc'ing our regulatory domain */ static bool reg_is_valid_request(const char *alpha2)
bool reg_is_valid_request(const char *alpha2)
{ {
assert_reg_lock();
if (!last_request) if (!last_request)
return false; return false;
if (last_request->processed)
return false;
return alpha2_equal(last_request->alpha2, alpha2); return alpha2_equal(last_request->alpha2, alpha2);
} }
...@@ -1470,6 +1474,7 @@ __regulatory_hint(struct wiphy *wiphy, ...@@ -1470,6 +1474,7 @@ __regulatory_hint(struct wiphy *wiphy,
last_request = pending_request; last_request = pending_request;
last_request->intersect = intersect; last_request->intersect = intersect;
last_request->processed = false;
pending_request = NULL; pending_request = NULL;
...@@ -2060,11 +2065,13 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) ...@@ -2060,11 +2065,13 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
const struct ieee80211_regdomain *regd; const struct ieee80211_regdomain *regd;
const struct ieee80211_regdomain *intersected_rd = NULL; const struct ieee80211_regdomain *intersected_rd = NULL;
struct wiphy *request_wiphy; struct wiphy *request_wiphy;
/* Some basic sanity checks first */ /* Some basic sanity checks first */
if (is_world_regdom(rd->alpha2)) { if (!reg_is_valid_request(rd->alpha2))
if (WARN_ON(!reg_is_valid_request(rd->alpha2)))
return -EINVAL; return -EINVAL;
if (is_world_regdom(rd->alpha2)) {
update_world_regdomain(rd); update_world_regdomain(rd);
return 0; return 0;
} }
...@@ -2073,9 +2080,6 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) ...@@ -2073,9 +2080,6 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
!is_unknown_alpha2(rd->alpha2)) !is_unknown_alpha2(rd->alpha2))
return -EINVAL; return -EINVAL;
if (!last_request)
return -EINVAL;
/* /*
* Lets only bother proceeding on the same alpha2 if the current * Lets only bother proceeding on the same alpha2 if the current
* rd is non static (it means CRDA was present and was used last) * 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,9 +2101,6 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
* internal EEPROM data * internal EEPROM data
*/ */
if (WARN_ON(!reg_is_valid_request(rd->alpha2)))
return -EINVAL;
if (!is_valid_rd(rd)) { if (!is_valid_rd(rd)) {
pr_err("Invalid regulatory domain detected:\n"); pr_err("Invalid regulatory domain detected:\n");
print_regdomain_info(rd); print_regdomain_info(rd);
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
extern const struct ieee80211_regdomain *cfg80211_regdomain; extern const struct ieee80211_regdomain *cfg80211_regdomain;
bool is_world_regdom(const char *alpha2); bool is_world_regdom(const char *alpha2);
bool reg_is_valid_request(const char *alpha2);
bool reg_supported_dfs_region(u8 dfs_region); bool reg_supported_dfs_region(u8 dfs_region);
int regulatory_hint_user(const char *alpha2, int regulatory_hint_user(const char *alpha2,
......
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