]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
mac80211: always allocate struct ieee802_11_elems
authorJohannes Berg <johannes.berg@intel.com>
Mon, 20 Sep 2021 13:40:10 +0000 (15:40 +0200)
committerStefan Bader <stefan.bader@canonical.com>
Thu, 13 Oct 2022 07:21:46 +0000 (09:21 +0200)
As the 802.11 spec evolves, we need to parse more and more
elements. This is causing the struct to grow, and we can no
longer get away with putting it on the stack.

Change the API to always dynamically allocate and return an
allocated pointer that must be kfree()d later.

As an alternative, I contemplated a scheme whereby we'd say
in the code which elements we needed, e.g.

    DECLARE_ELEMENT_PARSER(elems,
                           SUPPORTED_CHANNELS,
                           CHANNEL_SWITCH,
                           EXT(KEY_DELIVERY));

    ieee802_11_parse_elems(..., &elems, ...);

and while I think this is possible and will save us a lot
since most individual places only care about a small subset
of the elements, it ended up being a bit more work since a
lot of places do the parsing and then pass the struct to
other functions, sometimes with multiple levels.

Link: https://lore.kernel.org/r/20210920154009.26caff6b5998.I05ae58768e990e611aee8eca8abefd9d7bc15e05@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
CVE-2022-41674
(cherry picked from commit 5d24828d05f37ad770599de00b53d5386e35aa61)
Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
Acked-by: Kamal Mostafa <kamal@canonical.com>
Acked-by: Luke Nowakowski-Krijger <luke.nowakowskikrijger@canonical.com>
Signed-off-by: Stefan Bader <stefan.bader@canonical.com>
net/mac80211/agg-rx.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/mesh.c
net/mac80211/mesh_hwmp.c
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/scan.c
net/mac80211/tdls.c
net/mac80211/util.c

index ef729b1e39ea3a46cd0ca3712e429a47141cb383..fc9b8942de77f419bb973bf7b7a31c53f1c42d30 100644 (file)
@@ -478,7 +478,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
                                     size_t len)
 {
        u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num;
-       struct ieee802_11_elems elems = { };
+       struct ieee802_11_elems *elems = NULL;
        u8 dialog_token;
        int ies_len;
 
@@ -496,16 +496,17 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
        ies_len = len - offsetof(struct ieee80211_mgmt,
                                 u.action.u.addba_req.variable);
        if (ies_len) {
-               ieee802_11_parse_elems(mgmt->u.action.u.addba_req.variable,
-                                ies_len, true, &elems, mgmt->bssid, NULL);
-               if (elems.parse_error)
+               elems = ieee802_11_parse_elems(mgmt->u.action.u.addba_req.variable,
+                                              ies_len, true, mgmt->bssid, NULL);
+               if (!elems || elems->parse_error)
                        return;
        }
 
        __ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
                                        start_seq_num, ba_policy, tid,
                                        buf_size, true, false,
-                                       elems.addba_ext_ie);
+                                       elems ? elems->addba_ext_ie : NULL);
+       kfree(elems);
 }
 
 void ieee80211_manage_rx_ba_offl(struct ieee80211_vif *vif,
index 5d6ca4c3e6981942ba5a5afaec0b1f0fd1333f19..66b00046f0c27ac177ef25b24541f414d8bcf51e 100644 (file)
@@ -9,7 +9,7 @@
  * Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright(c) 2016 Intel Deutschland GmbH
- * Copyright(c) 2018-2020 Intel Corporation
+ * Copyright(c) 2018-2021 Intel Corporation
  */
 
 #include <linux/delay.h>
@@ -1589,7 +1589,7 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,
                                    struct ieee80211_rx_status *rx_status)
 {
        size_t baselen;
-       struct ieee802_11_elems elems;
+       struct ieee802_11_elems *elems;
 
        BUILD_BUG_ON(offsetof(typeof(mgmt->u.probe_resp), variable) !=
                     offsetof(typeof(mgmt->u.beacon), variable));
@@ -1602,10 +1602,14 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,
        if (baselen > len)
                return;
 
-       ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
-                              false, &elems, mgmt->bssid, NULL);
+       elems = ieee802_11_parse_elems(mgmt->u.probe_resp.variable,
+                                      len - baselen, false,
+                                      mgmt->bssid, NULL);
 
-       ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
+       if (elems) {
+               ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, elems);
+               kfree(elems);
+       }
 }
 
 void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
@@ -1614,7 +1618,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_rx_status *rx_status;
        struct ieee80211_mgmt *mgmt;
        u16 fc;
-       struct ieee802_11_elems elems;
+       struct ieee802_11_elems *elems;
        int ies_len;
 
        rx_status = IEEE80211_SKB_RXCB(skb);
@@ -1651,15 +1655,16 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                        if (ies_len < 0)
                                break;
 
-                       ieee802_11_parse_elems(
+                       elems = ieee802_11_parse_elems(
                                mgmt->u.action.u.chan_switch.variable,
-                               ies_len, true, &elems, mgmt->bssid, NULL);
+                               ies_len, true, mgmt->bssid, NULL);
 
-                       if (elems.parse_error)
+                       if (!elems || elems->parse_error)
                                break;
 
                        ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt, skb->len,
-                                                       rx_status, &elems);
+                                                       rx_status, elems);
+                       kfree(elems);
                        break;
                }
        }
index c5f0ff80501062f79843b6f0015f5cf52859a805..3633e49239c7864099c19b81b4addc3490abd81c 100644 (file)
@@ -2217,18 +2217,18 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
        ieee80211_tx_skb_tid(sdata, skb, 7);
 }
 
-void ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
-                               struct ieee802_11_elems *elems,
-                               u64 filter, u32 crc, u8 *transmitter_bssid,
-                               u8 *bss_bssid);
-static inline void ieee802_11_parse_elems(const u8 *start, size_t len,
-                                         bool action,
-                                         struct ieee802_11_elems *elems,
-                                         u8 *transmitter_bssid,
-                                         u8 *bss_bssid)
+struct ieee802_11_elems *ieee802_11_parse_elems_crc(const u8 *start, size_t len,
+                                                   bool action,
+                                                   u64 filter, u32 crc,
+                                                   const u8 *transmitter_bssid,
+                                                   const u8 *bss_bssid);
+static inline struct ieee802_11_elems *
+ieee802_11_parse_elems(const u8 *start, size_t len, bool action,
+                      const u8 *transmitter_bssid,
+                      const u8 *bss_bssid)
 {
-       ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0,
-                                  transmitter_bssid, bss_bssid);
+       return ieee802_11_parse_elems_crc(start, len, action, 0, 0,
+                                         transmitter_bssid, bss_bssid);
 }
 
 
index 9f6414a68d71f5ebe3daaab4ea90986d22c79891..6847fdf934392ebbdff323cae065aca5b557002c 100644 (file)
@@ -1247,7 +1247,7 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
        struct sk_buff *presp;
        struct beacon_data *bcn;
        struct ieee80211_mgmt *hdr;
-       struct ieee802_11_elems elems;
+       struct ieee802_11_elems *elems;
        size_t baselen;
        u8 *pos;
 
@@ -1256,22 +1256,24 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
        if (baselen > len)
                return;
 
-       ieee802_11_parse_elems(pos, len - baselen, false, &elems, mgmt->bssid,
-                              NULL);
-
-       if (!elems.mesh_id)
+       elems = ieee802_11_parse_elems(pos, len - baselen, false, mgmt->bssid,
+                                      NULL);
+       if (!elems)
                return;
 
+       if (!elems->mesh_id)
+               goto free;
+
        /* 802.11-2012 10.1.4.3.2 */
        if ((!ether_addr_equal(mgmt->da, sdata->vif.addr) &&
             !is_broadcast_ether_addr(mgmt->da)) ||
-           elems.ssid_len != 0)
-               return;
+           elems->ssid_len != 0)
+               goto free;
 
-       if (elems.mesh_id_len != 0 &&
-           (elems.mesh_id_len != ifmsh->mesh_id_len ||
-            memcmp(elems.mesh_id, ifmsh->mesh_id, ifmsh->mesh_id_len)))
-               return;
+       if (elems->mesh_id_len != 0 &&
+           (elems->mesh_id_len != ifmsh->mesh_id_len ||
+            memcmp(elems->mesh_id, ifmsh->mesh_id, ifmsh->mesh_id_len)))
+               goto free;
 
        rcu_read_lock();
        bcn = rcu_dereference(ifmsh->beacon);
@@ -1295,6 +1297,8 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
        ieee80211_tx_skb(sdata, presp);
 out:
        rcu_read_unlock();
+free:
+       kfree(elems);
 }
 
 static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
@@ -1305,7 +1309,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-       struct ieee802_11_elems elems;
+       struct ieee802_11_elems *elems;
        struct ieee80211_channel *channel;
        size_t baselen;
        int freq;
@@ -1320,42 +1324,47 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
        if (baselen > len)
                return;
 
-       ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
-                              false, &elems, mgmt->bssid, NULL);
+       elems = ieee802_11_parse_elems(mgmt->u.probe_resp.variable,
+                                      len - baselen,
+                                      false, mgmt->bssid, NULL);
+       if (!elems)
+               return;
 
        /* ignore non-mesh or secure / unsecure mismatch */
-       if ((!elems.mesh_id || !elems.mesh_config) ||
-           (elems.rsn && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) ||
-           (!elems.rsn && sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE))
-               return;
+       if ((!elems->mesh_id || !elems->mesh_config) ||
+           (elems->rsn && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) ||
+           (!elems->rsn && sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE))
+               goto free;
 
-       if (elems.ds_params)
-               freq = ieee80211_channel_to_frequency(elems.ds_params[0], band);
+       if (elems->ds_params)
+               freq = ieee80211_channel_to_frequency(elems->ds_params[0], band);
        else
                freq = rx_status->freq;
 
        channel = ieee80211_get_channel(local->hw.wiphy, freq);
 
        if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
-               return;
+               goto free;
 
-       if (mesh_matches_local(sdata, &elems)) {
+       if (mesh_matches_local(sdata, elems)) {
                mpl_dbg(sdata, "rssi_threshold=%d,rx_status->signal=%d\n",
                        sdata->u.mesh.mshcfg.rssi_threshold, rx_status->signal);
                if (!sdata->u.mesh.user_mpm ||
                    sdata->u.mesh.mshcfg.rssi_threshold == 0 ||
                    sdata->u.mesh.mshcfg.rssi_threshold < rx_status->signal)
-                       mesh_neighbour_update(sdata, mgmt->sa, &elems,
+                       mesh_neighbour_update(sdata, mgmt->sa, elems,
                                              rx_status);
 
                if (ifmsh->csa_role != IEEE80211_MESH_CSA_ROLE_INIT &&
                    !sdata->vif.csa_active)
-                       ieee80211_mesh_process_chnswitch(sdata, &elems, true);
+                       ieee80211_mesh_process_chnswitch(sdata, elems, true);
        }
 
        if (ifmsh->sync_ops)
                ifmsh->sync_ops->rx_bcn_presp(sdata, stype, mgmt, len,
-                                             elems.mesh_config, rx_status);
+                                             elems->mesh_config, rx_status);
+free:
+       kfree(elems);
 }
 
 int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
@@ -1447,7 +1456,7 @@ static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
                              struct ieee80211_mgmt *mgmt, size_t len)
 {
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-       struct ieee802_11_elems elems;
+       struct ieee802_11_elems *elems;
        u16 pre_value;
        bool fwd_csa = true;
        size_t baselen;
@@ -1460,33 +1469,37 @@ static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
        pos = mgmt->u.action.u.chan_switch.variable;
        baselen = offsetof(struct ieee80211_mgmt,
                           u.action.u.chan_switch.variable);
-       ieee802_11_parse_elems(pos, len - baselen, true, &elems,
-                              mgmt->bssid, NULL);
-
-       if (!mesh_matches_local(sdata, &elems))
+       elems = ieee802_11_parse_elems(pos, len - baselen, true,
+                                      mgmt->bssid, NULL);
+       if (!elems)
                return;
 
-       ifmsh->chsw_ttl = elems.mesh_chansw_params_ie->mesh_ttl;
+       if (!mesh_matches_local(sdata, elems))
+               goto free;
+
+       ifmsh->chsw_ttl = elems->mesh_chansw_params_ie->mesh_ttl;
        if (!--ifmsh->chsw_ttl)
                fwd_csa = false;
 
-       pre_value = le16_to_cpu(elems.mesh_chansw_params_ie->mesh_pre_value);
+       pre_value = le16_to_cpu(elems->mesh_chansw_params_ie->mesh_pre_value);
        if (ifmsh->pre_value >= pre_value)
-               return;
+               goto free;
 
        ifmsh->pre_value = pre_value;
 
        if (!sdata->vif.csa_active &&
-           !ieee80211_mesh_process_chnswitch(sdata, &elems, false)) {
+           !ieee80211_mesh_process_chnswitch(sdata, elems, false)) {
                mcsa_dbg(sdata, "Failed to process CSA action frame");
-               return;
+               goto free;
        }
 
        /* forward or re-broadcast the CSA frame */
        if (fwd_csa) {
-               if (mesh_fwd_csa_frame(sdata, mgmt, len, &elems) < 0)
+               if (mesh_fwd_csa_frame(sdata, mgmt, len, elems) < 0)
                        mcsa_dbg(sdata, "Failed to forward the CSA frame");
        }
+free:
+       kfree(elems);
 }
 
 static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
index a05b615deb517740b39fa86e52484f890d77e41a..44a6fdb6efbd44f55904b96d17136507a0b11963 100644 (file)
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2008, 2009 open80211s Ltd.
- * Copyright (C) 2019 Intel Corporation
+ * Copyright (C) 2019, 2021 Intel Corporation
  * Author:     Luis Carlos Cobo <luisca@cozybit.com>
  */
 
@@ -908,7 +908,7 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,
 void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
                            struct ieee80211_mgmt *mgmt, size_t len)
 {
-       struct ieee802_11_elems elems;
+       struct ieee802_11_elems *elems;
        size_t baselen;
        u32 path_metric;
        struct sta_info *sta;
@@ -926,37 +926,41 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
        rcu_read_unlock();
 
        baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt;
-       ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable,
-                              len - baselen, false, &elems, mgmt->bssid, NULL);
+       elems = ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable,
+                                      len - baselen, false, mgmt->bssid, NULL);
+       if (!elems)
+               return;
 
-       if (elems.preq) {
-               if (elems.preq_len != 37)
+       if (elems->preq) {
+               if (elems->preq_len != 37)
                        /* Right now we support just 1 destination and no AE */
-                       return;
-               path_metric = hwmp_route_info_get(sdata, mgmt, elems.preq,
+                       goto free;
+               path_metric = hwmp_route_info_get(sdata, mgmt, elems->preq,
                                                  MPATH_PREQ);
                if (path_metric)
-                       hwmp_preq_frame_process(sdata, mgmt, elems.preq,
+                       hwmp_preq_frame_process(sdata, mgmt, elems->preq,
                                                path_metric);
        }
-       if (elems.prep) {
-               if (elems.prep_len != 31)
+       if (elems->prep) {
+               if (elems->prep_len != 31)
                        /* Right now we support no AE */
-                       return;
-               path_metric = hwmp_route_info_get(sdata, mgmt, elems.prep,
+                       goto free;
+               path_metric = hwmp_route_info_get(sdata, mgmt, elems->prep,
                                                  MPATH_PREP);
                if (path_metric)
-                       hwmp_prep_frame_process(sdata, mgmt, elems.prep,
+                       hwmp_prep_frame_process(sdata, mgmt, elems->prep,
                                                path_metric);
        }
-       if (elems.perr) {
-               if (elems.perr_len != 15)
+       if (elems->perr) {
+               if (elems->perr_len != 15)
                        /* Right now we support only one destination per PERR */
-                       return;
-               hwmp_perr_frame_process(sdata, mgmt, elems.perr);
+                       goto free;
+               hwmp_perr_frame_process(sdata, mgmt, elems->perr);
        }
-       if (elems.rann)
-               hwmp_rann_frame_process(sdata, mgmt, elems.rann);
+       if (elems->rann)
+               hwmp_rann_frame_process(sdata, mgmt, elems->rann);
+free:
+       kfree(elems);
 }
 
 /**
index a6915847d78aed15af40a565917a0dabc272bc41..a829470dd59ede2a24e9b4ee6aadff672ebc2f13 100644 (file)
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2008, 2009 open80211s Ltd.
- * Copyright (C) 2019 Intel Corporation
+ * Copyright (C) 2019, 2021 Intel Corporation
  * Author:     Luis Carlos Cobo <luisca@cozybit.com>
  */
 #include <linux/gfp.h>
@@ -1200,7 +1200,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
                         struct ieee80211_mgmt *mgmt, size_t len,
                         struct ieee80211_rx_status *rx_status)
 {
-       struct ieee802_11_elems elems;
+       struct ieee802_11_elems *elems;
        size_t baselen;
        u8 *baseaddr;
 
@@ -1228,7 +1228,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
                if (baselen > len)
                        return;
        }
-       ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems,
-                              mgmt->bssid, NULL);
-       mesh_process_plink_frame(sdata, mgmt, &elems, rx_status);
+       elems = ieee802_11_parse_elems(baseaddr, len - baselen, true,
+                                      mgmt->bssid, NULL);
+       mesh_process_plink_frame(sdata, mgmt, elems, rx_status);
+       kfree(elems);
 }
index 548cd14c55033323978615c2bf23b69c59ec39e7..45efa1d1c550c7b33d5ddf3f55f299c05bea1357 100644 (file)
@@ -3317,8 +3317,11 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
                aid = 0; /* TODO */
        }
        capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
-       ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, elems,
-                              mgmt->bssid, assoc_data->bss->bssid);
+       elems = ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false,
+                                      mgmt->bssid, assoc_data->bss->bssid);
+
+       if (!elems)
+               return false;
 
        if (elems->aid_resp)
                aid = le16_to_cpu(elems->aid_resp->aid);
@@ -3340,7 +3343,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
 
        if (!is_s1g && !elems->supp_rates) {
                sdata_info(sdata, "no SuppRates element in AssocResp\n");
-               return false;
+               ret = false;
+               goto out;
        }
 
        sdata->vif.bss_conf.aid = aid;
@@ -3362,7 +3366,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
             (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
              (!elems->vht_cap_elem || !elems->vht_operation)))) {
                const struct cfg80211_bss_ies *ies;
-               struct ieee802_11_elems bss_elems;
+               struct ieee802_11_elems *bss_elems;
 
                rcu_read_lock();
                ies = rcu_dereference(cbss->ies);
@@ -3373,13 +3377,17 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
                if (!bss_ies)
                        return false;
 
-               ieee802_11_parse_elems(bss_ies->data, bss_ies->len,
-                                      false, &bss_elems,
-                                      mgmt->bssid,
-                                      assoc_data->bss->bssid);
+               bss_elems = ieee802_11_parse_elems(bss_ies->data, bss_ies->len,
+                                                  false, mgmt->bssid,
+                                                  assoc_data->bss->bssid);
+               if (!bss_elems) {
+                       ret = false;
+                       goto out;
+               }
+
                if (assoc_data->wmm &&
-                   !elems->wmm_param && bss_elems.wmm_param) {
-                       elems->wmm_param = bss_elems.wmm_param;
+                   !elems->wmm_param && bss_elems->wmm_param) {
+                       elems->wmm_param = bss_elems->wmm_param;
                        sdata_info(sdata,
                                   "AP bug: WMM param missing from AssocResp\n");
                }
@@ -3388,30 +3396,32 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
                 * Also check if we requested HT/VHT, otherwise the AP doesn't
                 * have to include the IEs in the (re)association response.
                 */
-               if (!elems->ht_cap_elem && bss_elems.ht_cap_elem &&
+               if (!elems->ht_cap_elem && bss_elems->ht_cap_elem &&
                    !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
-                       elems->ht_cap_elem = bss_elems.ht_cap_elem;
+                       elems->ht_cap_elem = bss_elems->ht_cap_elem;
                        sdata_info(sdata,
                                   "AP bug: HT capability missing from AssocResp\n");
                }
-               if (!elems->ht_operation && bss_elems.ht_operation &&
+               if (!elems->ht_operation && bss_elems->ht_operation &&
                    !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
-                       elems->ht_operation = bss_elems.ht_operation;
+                       elems->ht_operation = bss_elems->ht_operation;
                        sdata_info(sdata,
                                   "AP bug: HT operation missing from AssocResp\n");
                }
-               if (!elems->vht_cap_elem && bss_elems.vht_cap_elem &&
+               if (!elems->vht_cap_elem && bss_elems->vht_cap_elem &&
                    !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) {
-                       elems->vht_cap_elem = bss_elems.vht_cap_elem;
+                       elems->vht_cap_elem = bss_elems->vht_cap_elem;
                        sdata_info(sdata,
                                   "AP bug: VHT capa missing from AssocResp\n");
                }
-               if (!elems->vht_operation && bss_elems.vht_operation &&
+               if (!elems->vht_operation && bss_elems->vht_operation &&
                    !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) {
-                       elems->vht_operation = bss_elems.vht_operation;
+                       elems->vht_operation = bss_elems->vht_operation;
                        sdata_info(sdata,
                                   "AP bug: VHT operation missing from AssocResp\n");
                }
+
+               kfree(bss_elems);
        }
 
        /*
@@ -3662,6 +3672,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
 
        ret = true;
  out:
+       kfree(elems);
        kfree(bss_ies);
        return ret;
 }
@@ -3673,7 +3684,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
        u16 capab_info, status_code, aid;
-       struct ieee802_11_elems elems;
+       struct ieee802_11_elems *elems;
        int ac, uapsd_queues = -1;
        u8 *pos;
        bool reassoc;
@@ -3730,14 +3741,16 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
            fils_decrypt_assoc_resp(sdata, (u8 *)mgmt, &len, assoc_data) < 0)
                return;
 
-       ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, &elems,
-                              mgmt->bssid, assoc_data->bss->bssid);
+       elems = ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false,
+                                      mgmt->bssid, assoc_data->bss->bssid);
+       if (!elems)
+               goto notify_driver;
 
        if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
-           elems.timeout_int &&
-           elems.timeout_int->type == WLAN_TIMEOUT_ASSOC_COMEBACK) {
+           elems->timeout_int &&
+           elems->timeout_int->type == WLAN_TIMEOUT_ASSOC_COMEBACK) {
                u32 tu, ms;
-               tu = le32_to_cpu(elems.timeout_int->value);
+               tu = le32_to_cpu(elems->timeout_int->value);
                ms = tu * 1024 / 1000;
                sdata_info(sdata,
                           "%pM rejected association temporarily; comeback duration %u TU (%u ms)\n",
@@ -3757,7 +3770,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                event.u.mlme.reason = status_code;
                drv_event_callback(sdata->local, sdata, &event);
        } else {
-               if (!ieee80211_assoc_success(sdata, cbss, mgmt, len, &elems)) {
+               if (!ieee80211_assoc_success(sdata, cbss, mgmt, len, elems)) {
                        /* oops -- internal error -- send timeout for now */
                        ieee80211_destroy_assoc_data(sdata, false, false);
                        cfg80211_assoc_timeout(sdata->dev, cbss);
@@ -3787,6 +3800,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                               ifmgd->assoc_req_ies, ifmgd->assoc_req_ies_len);
 notify_driver:
        drv_mgd_complete_tx(sdata->local, sdata, &info);
+       kfree(elems);
 }
 
 static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
@@ -3991,7 +4005,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
        struct ieee80211_mgmt *mgmt = (void *) hdr;
        size_t baselen;
-       struct ieee802_11_elems elems;
+       struct ieee802_11_elems *elems;
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_channel *chan;
@@ -4037,15 +4051,16 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 
        if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&
            ieee80211_rx_our_beacon(bssid, ifmgd->assoc_data->bss)) {
-               ieee802_11_parse_elems(variable,
-                                      len - baselen, false, &elems,
-                                      bssid,
-                                      ifmgd->assoc_data->bss->bssid);
+               elems = ieee802_11_parse_elems(variable, len - baselen, false,
+                                              bssid,
+                                              ifmgd->assoc_data->bss->bssid);
+               if (!elems)
+                       return;
 
                ieee80211_rx_bss_info(sdata, mgmt, len, rx_status);
 
-               if (elems.dtim_period)
-                       ifmgd->dtim_period = elems.dtim_period;
+               if (elems->dtim_period)
+                       ifmgd->dtim_period = elems->dtim_period;
                ifmgd->have_beacon = true;
                ifmgd->assoc_data->need_beacon = false;
                if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) {
@@ -4053,17 +4068,17 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                                le64_to_cpu(mgmt->u.beacon.timestamp);
                        sdata->vif.bss_conf.sync_device_ts =
                                rx_status->device_timestamp;
-                       sdata->vif.bss_conf.sync_dtim_count = elems.dtim_count;
+                       sdata->vif.bss_conf.sync_dtim_count = elems->dtim_count;
                }
 
-               if (elems.mbssid_config_ie)
+               if (elems->mbssid_config_ie)
                        bss_conf->profile_periodicity =
-                               elems.mbssid_config_ie->profile_periodicity;
+                               elems->mbssid_config_ie->profile_periodicity;
                else
                        bss_conf->profile_periodicity = 0;
 
-               if (elems.ext_capab_len >= 11 &&
-                   (elems.ext_capab[10] & WLAN_EXT_CAPA11_EMA_SUPPORT))
+               if (elems->ext_capab_len >= 11 &&
+                   (elems->ext_capab[10] & WLAN_EXT_CAPA11_EMA_SUPPORT))
                        bss_conf->ema_ap = true;
                else
                        bss_conf->ema_ap = false;
@@ -4072,6 +4087,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                ifmgd->assoc_data->timeout = jiffies;
                ifmgd->assoc_data->timeout_started = true;
                run_again(sdata, ifmgd->assoc_data->timeout);
+               kfree(elems);
                return;
        }
 
@@ -4103,14 +4119,15 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
         */
        if (!ieee80211_is_s1g_beacon(hdr->frame_control))
                ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
-       ieee802_11_parse_elems_crc(variable,
-                                  len - baselen, false, &elems,
-                                  care_about_ies, ncrc,
-                                  mgmt->bssid, bssid);
-       ncrc = elems.crc;
+       elems = ieee802_11_parse_elems_crc(variable, len - baselen,
+                                          false, care_about_ies, ncrc,
+                                          mgmt->bssid, bssid);
+       if (!elems)
+               return;
+       ncrc = elems->crc;
 
        if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) &&
-           ieee80211_check_tim(elems.tim, elems.tim_len, bss_conf->aid)) {
+           ieee80211_check_tim(elems->tim, elems->tim_len, bss_conf->aid)) {
                if (local->hw.conf.dynamic_ps_timeout > 0) {
                        if (local->hw.conf.flags & IEEE80211_CONF_PS) {
                                local->hw.conf.flags &= ~IEEE80211_CONF_PS;
@@ -4180,12 +4197,12 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                        le64_to_cpu(mgmt->u.beacon.timestamp);
                sdata->vif.bss_conf.sync_device_ts =
                        rx_status->device_timestamp;
-               sdata->vif.bss_conf.sync_dtim_count = elems.dtim_count;
+               sdata->vif.bss_conf.sync_dtim_count = elems->dtim_count;
        }
 
        if ((ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid) ||
            ieee80211_is_s1g_short_beacon(mgmt->frame_control))
-               return;
+               goto free;
        ifmgd->beacon_crc = ncrc;
        ifmgd->beacon_crc_valid = true;
 
@@ -4193,12 +4210,12 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
                                         rx_status->device_timestamp,
-                                        &elems, true);
+                                        elems, true);
 
        if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) &&
-           ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
-                                    elems.wmm_param_len,
-                                    elems.mu_edca_param_set))
+           ieee80211_sta_wmm_params(local, sdata, elems->wmm_param,
+                                    elems->wmm_param_len,
+                                    elems->mu_edca_param_set))
                changed |= BSS_CHANGED_QOS;
 
        /*
@@ -4207,7 +4224,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
         */
        if (!ifmgd->have_beacon) {
                /* a few bogus AP send dtim_period = 0 or no TIM IE */
-               bss_conf->dtim_period = elems.dtim_period ?: 1;
+               bss_conf->dtim_period = elems->dtim_period ?: 1;
 
                changed |= BSS_CHANGED_BEACON_INFO;
                ifmgd->have_beacon = true;
@@ -4219,9 +4236,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                ieee80211_recalc_ps_vif(sdata);
        }
 
-       if (elems.erp_info) {
+       if (elems->erp_info) {
                erp_valid = true;
-               erp_value = elems.erp_info[0];
+               erp_value = elems->erp_info[0];
        } else {
                erp_valid = false;
        }
@@ -4234,12 +4251,12 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        mutex_lock(&local->sta_mtx);
        sta = sta_info_get(sdata, bssid);
 
-       changed |= ieee80211_recalc_twt_req(sdata, sta, &elems);
+       changed |= ieee80211_recalc_twt_req(sdata, sta, elems);
 
-       if (ieee80211_config_bw(sdata, sta, elems.ht_cap_elem,
-                               elems.vht_cap_elem, elems.ht_operation,
-                               elems.vht_operation, elems.he_operation,
-                               elems.s1g_oper, bssid, &changed)) {
+       if (ieee80211_config_bw(sdata, sta, elems->ht_cap_elem,
+                               elems->vht_cap_elem, elems->ht_operation,
+                               elems->vht_operation, elems->he_operation,
+                               elems->s1g_oper, bssid, &changed)) {
                mutex_unlock(&local->sta_mtx);
                sdata_info(sdata,
                           "failed to follow AP %pM bandwidth change, disconnect\n",
@@ -4251,21 +4268,23 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                                            sizeof(deauth_buf), true,
                                            WLAN_REASON_DEAUTH_LEAVING,
                                            false);
-               return;
+               goto free;
        }
 
-       if (sta && elems.opmode_notif)
-               ieee80211_vht_handle_opmode(sdata, sta, *elems.opmode_notif,
+       if (sta && elems->opmode_notif)
+               ieee80211_vht_handle_opmode(sdata, sta, *elems->opmode_notif,
                                            rx_status->band);
        mutex_unlock(&local->sta_mtx);
 
        changed |= ieee80211_handle_pwr_constr(sdata, chan, mgmt,
-                                              elems.country_elem,
-                                              elems.country_elem_len,
-                                              elems.pwr_constr_elem,
-                                              elems.cisco_dtpc_elem);
+                                              elems->country_elem,
+                                              elems->country_elem_len,
+                                              elems->pwr_constr_elem,
+                                              elems->cisco_dtpc_elem);
 
        ieee80211_bss_info_change_notify(sdata, changed);
+free:
+       kfree(elems);
 }
 
 void ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata,
@@ -4294,7 +4313,6 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_rx_status *rx_status;
        struct ieee80211_mgmt *mgmt;
        u16 fc;
-       struct ieee802_11_elems elems;
        int ies_len;
 
        rx_status = (struct ieee80211_rx_status *) skb->cb;
@@ -4326,6 +4344,8 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                break;
        case IEEE80211_STYPE_ACTION:
                if (mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) {
+                       struct ieee802_11_elems *elems;
+
                        ies_len = skb->len -
                                  offsetof(struct ieee80211_mgmt,
                                           u.action.u.chan_switch.variable);
@@ -4334,18 +4354,21 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                                break;
 
                        /* CSA IE cannot be overridden, no need for BSSID */
-                       ieee802_11_parse_elems(
-                               mgmt->u.action.u.chan_switch.variable,
-                               ies_len, true, &elems, mgmt->bssid, NULL);
+                       elems = ieee802_11_parse_elems(
+                                       mgmt->u.action.u.chan_switch.variable,
+                                       ies_len, true, mgmt->bssid, NULL);
 
-                       if (elems.parse_error)
+                       if (!elems || elems->parse_error)
                                break;
 
                        ieee80211_sta_process_chanswitch(sdata,
                                                 rx_status->mactime,
                                                 rx_status->device_timestamp,
-                                                &elems, false);
+                                                elems, false);
+                       kfree(elems);
                } else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
+                       struct ieee802_11_elems *elems;
+
                        ies_len = skb->len -
                                  offsetof(struct ieee80211_mgmt,
                                           u.action.u.ext_chan_switch.variable);
@@ -4357,21 +4380,22 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                         * extended CSA IE can't be overridden, no need for
                         * BSSID
                         */
-                       ieee802_11_parse_elems(
-                               mgmt->u.action.u.ext_chan_switch.variable,
-                               ies_len, true, &elems, mgmt->bssid, NULL);
+                       elems = ieee802_11_parse_elems(
+                                       mgmt->u.action.u.ext_chan_switch.variable,
+                                       ies_len, true, mgmt->bssid, NULL);
 
-                       if (elems.parse_error)
+                       if (!elems || elems->parse_error)
                                break;
 
                        /* for the handling code pretend this was also an IE */
-                       elems.ext_chansw_ie =
+                       elems->ext_chansw_ie =
                                &mgmt->u.action.u.ext_chan_switch.data;
 
                        ieee80211_sta_process_chanswitch(sdata,
                                                 rx_status->mactime,
                                                 rx_status->device_timestamp,
-                                                &elems, false);
+                                                elems, false);
+                       kfree(elems);
                }
                break;
        }
index 887f945bb12d46f8cae52f92eea983c709c5d344..b698756887eb5704dc1acb0d43a3808a280951ce 100644 (file)
@@ -9,7 +9,7 @@
  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
  * Copyright 2013-2015  Intel Mobile Communications GmbH
  * Copyright 2016-2017  Intel Deutschland GmbH
- * Copyright (C) 2018-2020 Intel Corporation
+ * Copyright (C) 2018-2021 Intel Corporation
  */
 
 #include <linux/if_arp.h>
@@ -155,7 +155,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
        };
        bool signal_valid;
        struct ieee80211_sub_if_data *scan_sdata;
-       struct ieee802_11_elems elems;
+       struct ieee802_11_elems *elems;
        size_t baselen;
        u8 *elements;
 
@@ -209,8 +209,10 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
        if (baselen > len)
                return NULL;
 
-       ieee802_11_parse_elems(elements, len - baselen, false, &elems,
-                              mgmt->bssid, cbss->bssid);
+       elems = ieee802_11_parse_elems(elements, len - baselen, false,
+                                      mgmt->bssid, cbss->bssid);
+       if (!elems)
+               return NULL;
 
        /* In case the signal is invalid update the status */
        signal_valid = channel == cbss->channel;
@@ -218,15 +220,17 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
                rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
 
        bss = (void *)cbss->priv;
-       ieee80211_update_bss_from_elems(local, bss, &elems, rx_status, beacon);
+       ieee80211_update_bss_from_elems(local, bss, elems, rx_status, beacon);
 
        list_for_each_entry(non_tx_cbss, &cbss->nontrans_list, nontrans_list) {
                non_tx_bss = (void *)non_tx_cbss->priv;
 
-               ieee80211_update_bss_from_elems(local, non_tx_bss, &elems,
+               ieee80211_update_bss_from_elems(local, non_tx_bss, elems,
                                                rx_status, beacon);
        }
 
+       kfree(elems);
+
        return bss;
 }
 
index 45e532ad1215baaf5e26b84c4006045a62f8f6a9..137be9ec94af14f041e2881cbe39a9fbd3e11ddc 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright 2014, Intel Corporation
  * Copyright 2014  Intel Mobile Communications GmbH
  * Copyright 2015 - 2016 Intel Deutschland GmbH
- * Copyright (C) 2019 Intel Corporation
+ * Copyright (C) 2019, 2021 Intel Corporation
  */
 
 #include <linux/ieee80211.h>
@@ -1684,7 +1684,7 @@ ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata,
                                           struct sk_buff *skb)
 {
        struct ieee80211_local *local = sdata->local;
-       struct ieee802_11_elems elems;
+       struct ieee802_11_elems *elems = NULL;
        struct sta_info *sta;
        struct ieee80211_tdls_data *tf = (void *)skb->data;
        bool local_initiator;
@@ -1718,16 +1718,20 @@ ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata,
                goto call_drv;
        }
 
-       ieee802_11_parse_elems(tf->u.chan_switch_resp.variable,
-                              skb->len - baselen, false, &elems,
-                              NULL, NULL);
-       if (elems.parse_error) {
+       elems = ieee802_11_parse_elems(tf->u.chan_switch_resp.variable,
+                                      skb->len - baselen, false, NULL, NULL);
+       if (!elems) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (elems->parse_error) {
                tdls_dbg(sdata, "Invalid IEs in TDLS channel switch resp\n");
                ret = -EINVAL;
                goto out;
        }
 
-       if (!elems.ch_sw_timing || !elems.lnk_id) {
+       if (!elems->ch_sw_timing || !elems->lnk_id) {
                tdls_dbg(sdata, "TDLS channel switch resp - missing IEs\n");
                ret = -EINVAL;
                goto out;
@@ -1735,15 +1739,15 @@ ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata,
 
        /* validate the initiator is set correctly */
        local_initiator =
-               !memcmp(elems.lnk_id->init_sta, sdata->vif.addr, ETH_ALEN);
+               !memcmp(elems->lnk_id->init_sta, sdata->vif.addr, ETH_ALEN);
        if (local_initiator == sta->sta.tdls_initiator) {
                tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n");
                ret = -EINVAL;
                goto out;
        }
 
-       params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time);
-       params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout);
+       params.switch_time = le16_to_cpu(elems->ch_sw_timing->switch_time);
+       params.switch_timeout = le16_to_cpu(elems->ch_sw_timing->switch_timeout);
 
        params.tmpl_skb =
                ieee80211_tdls_ch_sw_resp_tmpl_get(sta, &params.ch_sw_tm_ie);
@@ -1763,6 +1767,7 @@ call_drv:
 out:
        mutex_unlock(&local->sta_mtx);
        dev_kfree_skb_any(params.tmpl_skb);
+       kfree(elems);
        return ret;
 }
 
@@ -1771,7 +1776,7 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
                                          struct sk_buff *skb)
 {
        struct ieee80211_local *local = sdata->local;
-       struct ieee802_11_elems elems;
+       struct ieee802_11_elems *elems;
        struct cfg80211_chan_def chandef;
        struct ieee80211_channel *chan;
        enum nl80211_channel_type chan_type;
@@ -1831,22 +1836,27 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
                return -EINVAL;
        }
 
-       ieee802_11_parse_elems(tf->u.chan_switch_req.variable,
-                              skb->len - baselen, false, &elems, NULL, NULL);
-       if (elems.parse_error) {
+       elems = ieee802_11_parse_elems(tf->u.chan_switch_req.variable,
+                                      skb->len - baselen, false, NULL, NULL);
+       if (!elems)
+               return -ENOMEM;
+
+       if (elems->parse_error) {
                tdls_dbg(sdata, "Invalid IEs in TDLS channel switch req\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto free;
        }
 
-       if (!elems.ch_sw_timing || !elems.lnk_id) {
+       if (!elems->ch_sw_timing || !elems->lnk_id) {
                tdls_dbg(sdata, "TDLS channel switch req - missing IEs\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto free;
        }
 
-       if (!elems.sec_chan_offs) {
+       if (!elems->sec_chan_offs) {
                chan_type = NL80211_CHAN_HT20;
        } else {
-               switch (elems.sec_chan_offs->sec_chan_offs) {
+               switch (elems->sec_chan_offs->sec_chan_offs) {
                case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
                        chan_type = NL80211_CHAN_HT40PLUS;
                        break;
@@ -1865,7 +1875,8 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
        if (!cfg80211_reg_can_beacon_relax(sdata->local->hw.wiphy, &chandef,
                                           sdata->wdev.iftype)) {
                tdls_dbg(sdata, "TDLS chan switch to forbidden channel\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto free;
        }
 
        mutex_lock(&local->sta_mtx);
@@ -1881,7 +1892,7 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
 
        /* validate the initiator is set correctly */
        local_initiator =
-               !memcmp(elems.lnk_id->init_sta, sdata->vif.addr, ETH_ALEN);
+               !memcmp(elems->lnk_id->init_sta, sdata->vif.addr, ETH_ALEN);
        if (local_initiator == sta->sta.tdls_initiator) {
                tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n");
                ret = -EINVAL;
@@ -1889,16 +1900,16 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
        }
 
        /* peer should have known better */
-       if (!sta->sta.ht_cap.ht_supported && elems.sec_chan_offs &&
-           elems.sec_chan_offs->sec_chan_offs) {
+       if (!sta->sta.ht_cap.ht_supported && elems->sec_chan_offs &&
+           elems->sec_chan_offs->sec_chan_offs) {
                tdls_dbg(sdata, "TDLS chan switch - wide chan unsupported\n");
                ret = -ENOTSUPP;
                goto out;
        }
 
        params.chandef = &chandef;
-       params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time);
-       params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout);
+       params.switch_time = le16_to_cpu(elems->ch_sw_timing->switch_time);
+       params.switch_timeout = le16_to_cpu(elems->ch_sw_timing->switch_timeout);
 
        params.tmpl_skb =
                ieee80211_tdls_ch_sw_resp_tmpl_get(sta,
@@ -1917,6 +1928,8 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
 out:
        mutex_unlock(&local->sta_mtx);
        dev_kfree_skb_any(params.tmpl_skb);
+free:
+       kfree(elems);
        return ret;
 }
 
index 19a7e6fc68ac38d56da1d7c398047f57147d960c..b0d6829b4f3e8f8e9c66779aff4882d6541cecc7 100644 (file)
@@ -1396,8 +1396,8 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
 
 static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len,
                                            struct ieee802_11_elems *elems,
-                                           u8 *transmitter_bssid,
-                                           u8 *bss_bssid,
+                                           const u8 *transmitter_bssid,
+                                           const u8 *bss_bssid,
                                            u8 *nontransmitted_profile)
 {
        const struct element *elem, *sub;
@@ -1462,16 +1462,20 @@ static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len,
        return found ? profile_len : 0;
 }
 
-void ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
-                               struct ieee802_11_elems *elems,
-                               u64 filter, u32 crc, u8 *transmitter_bssid,
-                               u8 *bss_bssid)
+struct ieee802_11_elems *ieee802_11_parse_elems_crc(const u8 *start, size_t len,
+                                                   bool action, u64 filter,
+                                                   u32 crc,
+                                                   const u8 *transmitter_bssid,
+                                                   const u8 *bss_bssid)
 {
+       struct ieee802_11_elems *elems;
        const struct element *non_inherit = NULL;
        u8 *nontransmitted_profile;
        int nontransmitted_profile_len = 0;
 
-       memset(elems, 0, sizeof(*elems));
+       elems = kzalloc(sizeof(*elems), GFP_ATOMIC);
+       if (!elems)
+               return NULL;
        elems->ie_start = start;
        elems->total_len = len;
 
@@ -1518,6 +1522,8 @@ void ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
        kfree(nontransmitted_profile);
 
        elems->crc = crc;
+
+       return elems;
 }
 
 void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata,