cfg80211: reg: centralize freeing ignored requests

Instead of having a lot of places that free ignored requests
and then return REG_REQ_OK, make reg_process_hint() process
REG_REQ_IGNORE by freeing the request, and let functions it
calls return that instead of freeing.

This also fixes a leak when a second (different) country IE
hint was ignored.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2015-10-15 13:05:55 +02:00
parent 480908a7ec
commit d34265a3ee
1 changed files with 35 additions and 29 deletions

View File

@ -273,6 +273,9 @@ MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
static void reg_free_request(struct regulatory_request *request) static void reg_free_request(struct regulatory_request *request)
{ {
if (request == &core_request_world)
return;
if (request != get_last_request()) if (request != get_last_request())
kfree(request); kfree(request);
} }
@ -1905,13 +1908,17 @@ static void reg_set_request_processed(void)
* The wireless subsystem can use this function to process * The wireless subsystem can use this function to process
* a regulatory request issued by the regulatory core. * a regulatory request issued by the regulatory core.
*/ */
static void reg_process_hint_core(struct regulatory_request *core_request) static enum reg_request_treatment
reg_process_hint_core(struct regulatory_request *core_request)
{ {
if (reg_query_database(core_request)) { if (reg_query_database(core_request)) {
core_request->intersect = false; core_request->intersect = false;
core_request->processed = false; core_request->processed = false;
reg_update_last_request(core_request); reg_update_last_request(core_request);
return REG_REQ_OK;
} }
return REG_REQ_IGNORE;
} }
static enum reg_request_treatment static enum reg_request_treatment
@ -1957,16 +1964,15 @@ __reg_process_hint_user(struct regulatory_request *user_request)
* The wireless subsystem can use this function to process * The wireless subsystem can use this function to process
* a regulatory request initiated by userspace. * a regulatory request initiated by userspace.
*/ */
static void reg_process_hint_user(struct regulatory_request *user_request) static enum reg_request_treatment
reg_process_hint_user(struct regulatory_request *user_request)
{ {
enum reg_request_treatment treatment; enum reg_request_treatment treatment;
treatment = __reg_process_hint_user(user_request); treatment = __reg_process_hint_user(user_request);
if (treatment == REG_REQ_IGNORE || if (treatment == REG_REQ_IGNORE ||
treatment == REG_REQ_ALREADY_SET) { treatment == REG_REQ_ALREADY_SET)
reg_free_request(user_request); return REG_REQ_IGNORE;
return;
}
user_request->intersect = treatment == REG_REQ_INTERSECT; user_request->intersect = treatment == REG_REQ_INTERSECT;
user_request->processed = false; user_request->processed = false;
@ -1975,9 +1981,10 @@ static void reg_process_hint_user(struct regulatory_request *user_request)
reg_update_last_request(user_request); reg_update_last_request(user_request);
user_alpha2[0] = user_request->alpha2[0]; user_alpha2[0] = user_request->alpha2[0];
user_alpha2[1] = user_request->alpha2[1]; user_alpha2[1] = user_request->alpha2[1];
} else { return REG_REQ_OK;
reg_free_request(user_request);
} }
return REG_REQ_IGNORE;
} }
static enum reg_request_treatment static enum reg_request_treatment
@ -2025,15 +2032,12 @@ reg_process_hint_driver(struct wiphy *wiphy,
case REG_REQ_OK: case REG_REQ_OK:
break; break;
case REG_REQ_IGNORE: case REG_REQ_IGNORE:
reg_free_request(driver_request); return REG_REQ_IGNORE;
return REG_REQ_OK;
case REG_REQ_INTERSECT: case REG_REQ_INTERSECT:
case REG_REQ_ALREADY_SET: case REG_REQ_ALREADY_SET:
regd = reg_copy_regd(get_cfg80211_regdom()); regd = reg_copy_regd(get_cfg80211_regdom());
if (IS_ERR(regd)) { if (IS_ERR(regd))
reg_free_request(driver_request); return REG_REQ_IGNORE;
return REG_REQ_OK;
}
tmp = get_wiphy_regdom(wiphy); tmp = get_wiphy_regdom(wiphy);
rcu_assign_pointer(wiphy->regd, regd); rcu_assign_pointer(wiphy->regd, regd);
@ -2056,14 +2060,14 @@ reg_process_hint_driver(struct wiphy *wiphy,
return REG_REQ_ALREADY_SET; return REG_REQ_ALREADY_SET;
} }
if (reg_query_database(driver_request)) if (reg_query_database(driver_request)) {
reg_update_last_request(driver_request); reg_update_last_request(driver_request);
else
reg_free_request(driver_request);
return REG_REQ_OK; return REG_REQ_OK;
} }
return REG_REQ_IGNORE;
}
static enum reg_request_treatment static enum reg_request_treatment
__reg_process_hint_country_ie(struct wiphy *wiphy, __reg_process_hint_country_ie(struct wiphy *wiphy,
struct regulatory_request *country_ie_request) struct regulatory_request *country_ie_request)
@ -2127,31 +2131,30 @@ reg_process_hint_country_ie(struct wiphy *wiphy,
case REG_REQ_OK: case REG_REQ_OK:
break; break;
case REG_REQ_IGNORE: case REG_REQ_IGNORE:
return REG_REQ_OK; return REG_REQ_IGNORE;
case REG_REQ_ALREADY_SET: case REG_REQ_ALREADY_SET:
reg_free_request(country_ie_request); reg_free_request(country_ie_request);
return REG_REQ_ALREADY_SET; return REG_REQ_ALREADY_SET;
case REG_REQ_INTERSECT: case REG_REQ_INTERSECT:
reg_free_request(country_ie_request);
/* /*
* This doesn't happen yet, not sure we * This doesn't happen yet, not sure we
* ever want to support it for this case. * ever want to support it for this case.
*/ */
WARN_ONCE(1, "Unexpected intersection for country IEs"); WARN_ONCE(1, "Unexpected intersection for country IEs");
return REG_REQ_OK; return REG_REQ_IGNORE;
} }
country_ie_request->intersect = false; country_ie_request->intersect = false;
country_ie_request->processed = false; country_ie_request->processed = false;
if (reg_query_database(country_ie_request)) if (reg_query_database(country_ie_request)) {
reg_update_last_request(country_ie_request); reg_update_last_request(country_ie_request);
else
reg_free_request(country_ie_request);
return REG_REQ_OK; return REG_REQ_OK;
} }
return REG_REQ_IGNORE;
}
/* This processes *all* regulatory hints */ /* This processes *all* regulatory hints */
static void reg_process_hint(struct regulatory_request *reg_request) static void reg_process_hint(struct regulatory_request *reg_request)
{ {
@ -2163,11 +2166,11 @@ static void reg_process_hint(struct regulatory_request *reg_request)
switch (reg_request->initiator) { switch (reg_request->initiator) {
case NL80211_REGDOM_SET_BY_CORE: case NL80211_REGDOM_SET_BY_CORE:
reg_process_hint_core(reg_request); treatment = reg_process_hint_core(reg_request);
return; break;
case NL80211_REGDOM_SET_BY_USER: case NL80211_REGDOM_SET_BY_USER:
reg_process_hint_user(reg_request); treatment = reg_process_hint_user(reg_request);
return; break;
case NL80211_REGDOM_SET_BY_DRIVER: case NL80211_REGDOM_SET_BY_DRIVER:
if (!wiphy) if (!wiphy)
goto out_free; goto out_free;
@ -2183,6 +2186,9 @@ static void reg_process_hint(struct regulatory_request *reg_request)
goto out_free; goto out_free;
} }
if (treatment == REG_REQ_IGNORE)
goto out_free;
WARN(treatment != REG_REQ_OK && treatment != REG_REQ_ALREADY_SET, WARN(treatment != REG_REQ_OK && treatment != REG_REQ_ALREADY_SET,
"unexpected treatment value %d\n", treatment); "unexpected treatment value %d\n", treatment);