Commit a042994d authored by Luis R. Rodriguez's avatar Luis R. Rodriguez Committed by John W. Linville

cfg80211: fix race on init and driver registration

There is a theoretical race that if hit will trigger
a crash. The race is between when we issue the first
regulatory hint, regulatory_hint_core(), gets processed
by the workqueue and between when the first device
gets registered to the wireless core. This is not easy
to reproduce but it was easy to do so through the
regulatory simulator I have been working on. This
is a port of the fix I implemented there [1].

[1] https://github.com/mcgrof/regsim/commit/a246ccf81f059cb662eee288aa13100f631e4cc8

Cc: stable@vger.kernel.org
Cc: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarLuis R. Rodriguez <mcgrof@qca.qualcomm.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 2a1e0fd1
...@@ -55,8 +55,17 @@ ...@@ -55,8 +55,17 @@
#define REG_DBG_PRINT(args...) #define REG_DBG_PRINT(args...)
#endif #endif
static struct regulatory_request core_request_world = {
.initiator = NL80211_REGDOM_SET_BY_CORE,
.alpha2[0] = '0',
.alpha2[1] = '0',
.intersect = false,
.processed = true,
.country_ie_env = ENVIRON_ANY,
};
/* Receipt of information from last regulatory request */ /* Receipt of information from last regulatory request */
static struct regulatory_request *last_request; static struct regulatory_request *last_request = &core_request_world;
/* To trigger userspace events */ /* To trigger userspace events */
static struct platform_device *reg_pdev; static struct platform_device *reg_pdev;
...@@ -148,7 +157,7 @@ static char user_alpha2[2]; ...@@ -148,7 +157,7 @@ static char user_alpha2[2];
module_param(ieee80211_regdom, charp, 0444); module_param(ieee80211_regdom, charp, 0444);
MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
static void reset_regdomains(void) static void reset_regdomains(bool full_reset)
{ {
/* avoid freeing static information or freeing something twice */ /* avoid freeing static information or freeing something twice */
if (cfg80211_regdomain == cfg80211_world_regdom) if (cfg80211_regdomain == cfg80211_world_regdom)
...@@ -163,6 +172,13 @@ static void reset_regdomains(void) ...@@ -163,6 +172,13 @@ static void reset_regdomains(void)
cfg80211_world_regdom = &world_regdom; cfg80211_world_regdom = &world_regdom;
cfg80211_regdomain = NULL; cfg80211_regdomain = NULL;
if (!full_reset)
return;
if (last_request != &core_request_world)
kfree(last_request);
last_request = &core_request_world;
} }
/* /*
...@@ -173,7 +189,7 @@ static void update_world_regdomain(const struct ieee80211_regdomain *rd) ...@@ -173,7 +189,7 @@ static void update_world_regdomain(const struct ieee80211_regdomain *rd)
{ {
BUG_ON(!last_request); BUG_ON(!last_request);
reset_regdomains(); reset_regdomains(false);
cfg80211_world_regdom = rd; cfg80211_world_regdom = rd;
cfg80211_regdomain = rd; cfg80211_regdomain = rd;
...@@ -1405,7 +1421,8 @@ static int __regulatory_hint(struct wiphy *wiphy, ...@@ -1405,7 +1421,8 @@ static int __regulatory_hint(struct wiphy *wiphy,
} }
new_request: new_request:
kfree(last_request); if (last_request != &core_request_world)
kfree(last_request);
last_request = pending_request; last_request = pending_request;
last_request->intersect = intersect; last_request->intersect = intersect;
...@@ -1575,9 +1592,6 @@ static int regulatory_hint_core(const char *alpha2) ...@@ -1575,9 +1592,6 @@ static int regulatory_hint_core(const char *alpha2)
{ {
struct regulatory_request *request; struct regulatory_request *request;
kfree(last_request);
last_request = NULL;
request = kzalloc(sizeof(struct regulatory_request), request = kzalloc(sizeof(struct regulatory_request),
GFP_KERNEL); GFP_KERNEL);
if (!request) if (!request)
...@@ -1775,7 +1789,7 @@ static void restore_regulatory_settings(bool reset_user) ...@@ -1775,7 +1789,7 @@ static void restore_regulatory_settings(bool reset_user)
mutex_lock(&cfg80211_mutex); mutex_lock(&cfg80211_mutex);
mutex_lock(&reg_mutex); mutex_lock(&reg_mutex);
reset_regdomains(); reset_regdomains(true);
restore_alpha2(alpha2, reset_user); restore_alpha2(alpha2, reset_user);
/* /*
...@@ -2044,7 +2058,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) ...@@ -2044,7 +2058,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
int r; int r;
if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) {
reset_regdomains(); reset_regdomains(false);
cfg80211_regdomain = rd; cfg80211_regdomain = rd;
return 0; return 0;
} }
...@@ -2065,7 +2079,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) ...@@ -2065,7 +2079,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
if (r) if (r)
return r; return r;
reset_regdomains(); reset_regdomains(false);
cfg80211_regdomain = rd; cfg80211_regdomain = rd;
return 0; return 0;
} }
...@@ -2090,7 +2104,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) ...@@ -2090,7 +2104,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
rd = NULL; rd = NULL;
reset_regdomains(); reset_regdomains(false);
cfg80211_regdomain = intersected_rd; cfg80211_regdomain = intersected_rd;
return 0; return 0;
...@@ -2110,7 +2124,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) ...@@ -2110,7 +2124,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
kfree(rd); kfree(rd);
rd = NULL; rd = NULL;
reset_regdomains(); reset_regdomains(false);
cfg80211_regdomain = intersected_rd; cfg80211_regdomain = intersected_rd;
return 0; return 0;
...@@ -2263,11 +2277,8 @@ void /* __init_or_exit */ regulatory_exit(void) ...@@ -2263,11 +2277,8 @@ void /* __init_or_exit */ regulatory_exit(void)
mutex_lock(&cfg80211_mutex); mutex_lock(&cfg80211_mutex);
mutex_lock(&reg_mutex); mutex_lock(&reg_mutex);
reset_regdomains(); reset_regdomains(true);
kfree(last_request);
last_request = NULL;
dev_set_uevent_suppress(&reg_pdev->dev, true); dev_set_uevent_suppress(&reg_pdev->dev, true);
platform_device_unregister(reg_pdev); platform_device_unregister(reg_pdev);
......
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