cfg80211: avoid freeing last_request while in flight
authorArik Nemtsov <arik@wizery.com>
Tue, 22 Apr 2014 03:39:34 +0000 (20:39 -0700)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 22 Apr 2014 15:12:55 +0000 (17:12 +0200)
Avoid freeing the last request while it is being processed. This can
happen in some cases if reg_work is kicked for some reason while the
currently pending request is in flight.

Cc: Sander Eikelenboom <linux@eikelenboom.it>
Tested-by: Eliad Peller <eliad@wizery.com>
Tested-by: Colleen Twitty <colleen@cozybit.com>
Signed-off-by: Arik Nemtsov <arik@wizery.com>
Signed-off-by: Luis R. Rodriguez <mcgrof@suse.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/wireless/reg.c

index 9d32633f59564c57a6a89404332f17f7f148190f..081c5717e3ec6513e6f465df6be9e75efb11e446 100644 (file)
@@ -263,8 +263,16 @@ static char user_alpha2[2];
 module_param(ieee80211_regdom, charp, 0444);
 MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
 
-static void reg_free_request(struct regulatory_request *lr)
+static void reg_free_request(struct regulatory_request *request)
 {
+       if (request != get_last_request())
+               kfree(request);
+}
+
+static void reg_free_last_request(void)
+{
+       struct regulatory_request *lr = get_last_request();
+
        if (lr != &core_request_world && lr)
                kfree_rcu(lr, rcu_head);
 }
@@ -277,7 +285,7 @@ static void reg_update_last_request(struct regulatory_request *request)
        if (lr == request)
                return;
 
-       reg_free_request(lr);
+       reg_free_last_request();
        rcu_assign_pointer(last_request, request);
 }
 
@@ -1661,7 +1669,7 @@ reg_process_hint_user(struct regulatory_request *user_request)
        if (treatment == REG_REQ_IGNORE ||
            treatment == REG_REQ_ALREADY_SET ||
            treatment == REG_REQ_USER_HINT_HANDLED) {
-               kfree(user_request);
+               reg_free_request(user_request);
                return treatment;
        }
 
@@ -1722,14 +1730,14 @@ reg_process_hint_driver(struct wiphy *wiphy,
                break;
        case REG_REQ_IGNORE:
        case REG_REQ_USER_HINT_HANDLED:
-               kfree(driver_request);
+               reg_free_request(driver_request);
                return treatment;
        case REG_REQ_INTERSECT:
                /* fall through */
        case REG_REQ_ALREADY_SET:
                regd = reg_copy_regd(get_cfg80211_regdom());
                if (IS_ERR(regd)) {
-                       kfree(driver_request);
+                       reg_free_request(driver_request);
                        return REG_REQ_IGNORE;
                }
                rcu_assign_pointer(wiphy->regd, regd);
@@ -1824,10 +1832,10 @@ reg_process_hint_country_ie(struct wiphy *wiphy,
        case REG_REQ_USER_HINT_HANDLED:
                /* fall through */
        case REG_REQ_ALREADY_SET:
-               kfree(country_ie_request);
+               reg_free_request(country_ie_request);
                return treatment;
        case REG_REQ_INTERSECT:
-               kfree(country_ie_request);
+               reg_free_request(country_ie_request);
                /*
                 * This doesn't happen yet, not sure we
                 * ever want to support it for this case.
@@ -1888,7 +1896,7 @@ static void reg_process_hint(struct regulatory_request *reg_request)
        return;
 
 out_free:
-       kfree(reg_request);
+       reg_free_request(reg_request);
 }
 
 /*