]> git.karo-electronics.de Git - linux-beck.git/commitdiff
cfg80211/mac80211: fix BSS leaks when abandoning assoc attempts
authorJohannes Berg <johannes.berg@intel.com>
Thu, 8 Dec 2016 16:22:09 +0000 (17:22 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 9 Jan 2017 07:32:17 +0000 (08:32 +0100)
commit e6f462df9acd2a3295e5d34eb29e2823220cf129 upstream.

When mac80211 abandons an association attempt, it may free
all the data structures, but inform cfg80211 and userspace
about it only by sending the deauth frame it received, in
which case cfg80211 has no link to the BSS struct that was
used and will not cfg80211_unhold_bss() it.

Fix this by providing a way to inform cfg80211 of this with
the BSS entry passed, so that it can clean up properly, and
use this ability in the appropriate places in mac80211.

This isn't ideal: some code is more or less duplicated and
tracing is missing. However, it's a fairly small change and
it's thus easier to backport - cleanups can come later.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/net/cfg80211.h
net/mac80211/mlme.c
net/wireless/core.h
net/wireless/mlme.c
net/wireless/sme.c

index 14b51d739c3b39d5d9b8f0b934d3926841cdce69..66167138120a2d2108e88ec23e5184ac55ed37a1 100644 (file)
@@ -4583,6 +4583,17 @@ void cfg80211_rx_assoc_resp(struct net_device *dev,
  */
 void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss);
 
+/**
+ * cfg80211_abandon_assoc - notify cfg80211 of abandoned association attempt
+ * @dev: network device
+ * @bss: The BSS entry with which association was abandoned.
+ *
+ * Call this whenever - for reasons reported through other API, like deauth RX,
+ * an association attempt was abandoned.
+ * This function may sleep. The caller must hold the corresponding wdev's mutex.
+ */
+void cfg80211_abandon_assoc(struct net_device *dev, struct cfg80211_bss *bss);
+
 /**
  * cfg80211_tx_mlme_mgmt - notification of transmitted deauth/disassoc frame
  * @dev: network device
index 7486f2dab4ba70ade0b2faf48ffa7f3880ac2ad4..1118c61f835dde97b801531163f22ede2177592d 100644 (file)
@@ -2510,7 +2510,7 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
 }
 
 static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
-                                        bool assoc)
+                                        bool assoc, bool abandon)
 {
        struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
 
@@ -2533,6 +2533,9 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
                mutex_lock(&sdata->local->mtx);
                ieee80211_vif_release_channel(sdata);
                mutex_unlock(&sdata->local->mtx);
+
+               if (abandon)
+                       cfg80211_abandon_assoc(sdata->dev, assoc_data->bss);
        }
 
        kfree(assoc_data);
@@ -2762,7 +2765,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
                           bssid, reason_code,
                           ieee80211_get_reason_code_string(reason_code));
 
-               ieee80211_destroy_assoc_data(sdata, false);
+               ieee80211_destroy_assoc_data(sdata, false, true);
 
                cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
                return;
@@ -3167,14 +3170,14 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
        if (status_code != WLAN_STATUS_SUCCESS) {
                sdata_info(sdata, "%pM denied association (code=%d)\n",
                           mgmt->sa, status_code);
-               ieee80211_destroy_assoc_data(sdata, false);
+               ieee80211_destroy_assoc_data(sdata, false, false);
                event.u.mlme.status = MLME_DENIED;
                event.u.mlme.reason = status_code;
                drv_event_callback(sdata->local, sdata, &event);
        } else {
                if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) {
                        /* oops -- internal error -- send timeout for now */
-                       ieee80211_destroy_assoc_data(sdata, false);
+                       ieee80211_destroy_assoc_data(sdata, false, false);
                        cfg80211_assoc_timeout(sdata->dev, bss);
                        return;
                }
@@ -3187,7 +3190,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                 * recalc after assoc_data is NULL but before associated
                 * is set can cause the interface to go idle
                 */
-               ieee80211_destroy_assoc_data(sdata, true);
+               ieee80211_destroy_assoc_data(sdata, true, false);
 
                /* get uapsd queues configuration */
                uapsd_queues = 0;
@@ -3886,7 +3889,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                                .u.mlme.status = MLME_TIMEOUT,
                        };
 
-                       ieee80211_destroy_assoc_data(sdata, false);
+                       ieee80211_destroy_assoc_data(sdata, false, false);
                        cfg80211_assoc_timeout(sdata->dev, bss);
                        drv_event_callback(sdata->local, sdata, &event);
                }
@@ -4025,7 +4028,7 @@ void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata)
                                               WLAN_REASON_DEAUTH_LEAVING,
                                               false, frame_buf);
                if (ifmgd->assoc_data)
-                       ieee80211_destroy_assoc_data(sdata, false);
+                       ieee80211_destroy_assoc_data(sdata, false, true);
                if (ifmgd->auth_data)
                        ieee80211_destroy_auth_data(sdata, false);
                cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
@@ -4907,7 +4910,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
                                               IEEE80211_STYPE_DEAUTH,
                                               req->reason_code, tx,
                                               frame_buf);
-               ieee80211_destroy_assoc_data(sdata, false);
+               ieee80211_destroy_assoc_data(sdata, false, true);
                ieee80211_report_disconnect(sdata, frame_buf,
                                            sizeof(frame_buf), true,
                                            req->reason_code);
@@ -4982,7 +4985,7 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
        sdata_lock(sdata);
        if (ifmgd->assoc_data) {
                struct cfg80211_bss *bss = ifmgd->assoc_data->bss;
-               ieee80211_destroy_assoc_data(sdata, false);
+               ieee80211_destroy_assoc_data(sdata, false, false);
                cfg80211_assoc_timeout(sdata->dev, bss);
        }
        if (ifmgd->auth_data)
index f0c0c8a48c920887cadbc714f3e55015e9b9761d..5f5867f90fed5e61992baf06fbf330333889a390 100644 (file)
@@ -410,6 +410,7 @@ void cfg80211_sme_disassoc(struct wireless_dev *wdev);
 void cfg80211_sme_deauth(struct wireless_dev *wdev);
 void cfg80211_sme_auth_timeout(struct wireless_dev *wdev);
 void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev);
+void cfg80211_sme_abandon_assoc(struct wireless_dev *wdev);
 
 /* internal helpers */
 bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher);
index cbb48e26a8715b8964333bf9b69c52010289bafb..76775a2b421db31c33a323f4a671f470099051db 100644 (file)
@@ -149,6 +149,18 @@ void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss)
 }
 EXPORT_SYMBOL(cfg80211_assoc_timeout);
 
+void cfg80211_abandon_assoc(struct net_device *dev, struct cfg80211_bss *bss)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+
+       cfg80211_sme_abandon_assoc(wdev);
+
+       cfg80211_unhold_bss(bss_from_pub(bss));
+       cfg80211_put_bss(wiphy, bss);
+}
+EXPORT_SYMBOL(cfg80211_abandon_assoc);
+
 void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
index a77db333927ec1bc8ab6efa4ee26b9fc7e711f5e..35cc1de85dcc53ea18817a5a23a4745718277910 100644 (file)
@@ -39,6 +39,7 @@ struct cfg80211_conn {
                CFG80211_CONN_ASSOCIATING,
                CFG80211_CONN_ASSOC_FAILED,
                CFG80211_CONN_DEAUTH,
+               CFG80211_CONN_ABANDON,
                CFG80211_CONN_CONNECTED,
        } state;
        u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
@@ -206,6 +207,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
                cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
                                     NULL, 0,
                                     WLAN_REASON_DEAUTH_LEAVING, false);
+               /* fall through */
+       case CFG80211_CONN_ABANDON:
                /* free directly, disconnected event already sent */
                cfg80211_sme_free(wdev);
                return 0;
@@ -423,6 +426,17 @@ void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev)
        schedule_work(&rdev->conn_work);
 }
 
+void cfg80211_sme_abandon_assoc(struct wireless_dev *wdev)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+
+       if (!wdev->conn)
+               return;
+
+       wdev->conn->state = CFG80211_CONN_ABANDON;
+       schedule_work(&rdev->conn_work);
+}
+
 static int cfg80211_sme_get_conn_ies(struct wireless_dev *wdev,
                                     const u8 *ies, size_t ies_len,
                                     const u8 **out_ies, size_t *out_ies_len)