]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.15-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 14 Oct 2022 08:20:24 +0000 (10:20 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 14 Oct 2022 08:20:24 +0000 (10:20 +0200)
added patches:
mac80211-always-allocate-struct-ieee802_11_elems.patch
mac80211-fix-memory-leaks-with-element-parsing.patch
mac80211-mesh-clean-up-rx_bcn_presp-api.patch
mac80211-mlme-find-auth-challenge-directly.patch
mac80211-move-crc-into-struct-ieee802_11_elems.patch
wifi-mac80211-fix-mbssid-parsing-use-after-free.patch

queue-5.15/mac80211-always-allocate-struct-ieee802_11_elems.patch [new file with mode: 0644]
queue-5.15/mac80211-fix-memory-leaks-with-element-parsing.patch [new file with mode: 0644]
queue-5.15/mac80211-mesh-clean-up-rx_bcn_presp-api.patch [new file with mode: 0644]
queue-5.15/mac80211-mlme-find-auth-challenge-directly.patch [new file with mode: 0644]
queue-5.15/mac80211-move-crc-into-struct-ieee802_11_elems.patch [new file with mode: 0644]
queue-5.15/series
queue-5.15/wifi-mac80211-fix-mbssid-parsing-use-after-free.patch [new file with mode: 0644]

diff --git a/queue-5.15/mac80211-always-allocate-struct-ieee802_11_elems.patch b/queue-5.15/mac80211-always-allocate-struct-ieee802_11_elems.patch
new file mode 100644 (file)
index 0000000..d0edb77
--- /dev/null
@@ -0,0 +1,1162 @@
+From foo@baz Fri Oct 14 10:18:27 AM CEST 2022
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 13 Oct 2022 20:15:59 +0200
+Subject: mac80211: always allocate struct ieee802_11_elems
+To: stable@vger.kernel.org
+Cc: johannes@sipsolutions.net
+Message-ID: <20221013181601.5712-4-nbd@nbd.name>
+
+From: Johannes Berg <johannes.berg@intel.com>
+
+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>
+Cc: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/mac80211/agg-rx.c      |   11 +-
+ net/mac80211/ibss.c        |   27 ++++--
+ net/mac80211/ieee80211_i.h |   22 ++---
+ net/mac80211/mesh.c        |   85 ++++++++++++---------
+ net/mac80211/mesh_hwmp.c   |   44 ++++++-----
+ net/mac80211/mesh_plink.c  |   11 +-
+ net/mac80211/mlme.c        |  176 +++++++++++++++++++++++++--------------------
+ net/mac80211/scan.c        |   16 ++--
+ net/mac80211/tdls.c        |   63 +++++++++-------
+ net/mac80211/util.c        |   20 +++--
+ 10 files changed, 273 insertions(+), 202 deletions(-)
+
+--- a/net/mac80211/agg-rx.c
++++ b/net/mac80211/agg-rx.c
+@@ -478,7 +478,7 @@ void ieee80211_process_addba_request(str
+                                    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(str
+       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,
+--- a/net/mac80211/ibss.c
++++ b/net/mac80211/ibss.c
+@@ -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>
+@@ -1593,7 +1593,7 @@ void ieee80211_rx_mgmt_probe_beacon(stru
+                                   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));
+@@ -1606,10 +1606,14 @@ void ieee80211_rx_mgmt_probe_beacon(stru
+       if (baselen > len)
+               return;
+-      ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
+-                             false, &elems, mgmt->bssid, NULL);
+-
+-      ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
++      elems = ieee802_11_parse_elems(mgmt->u.probe_resp.variable,
++                                     len - baselen, false,
++                                     mgmt->bssid, NULL);
++
++      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,
+@@ -1618,7 +1622,7 @@ void ieee80211_ibss_rx_queued_mgmt(struc
+       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);
+@@ -1655,15 +1659,16 @@ void ieee80211_ibss_rx_queued_mgmt(struc
+                       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;
+               }
+       }
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -2217,18 +2217,18 @@ static inline void ieee80211_tx_skb(stru
+       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);
+ }
+--- a/net/mac80211/mesh.c
++++ b/net/mac80211/mesh.c
+@@ -1247,7 +1247,7 @@ ieee80211_mesh_rx_probe_req(struct ieee8
+       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 ieee8
+       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 ieee8
+       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_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(
+       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 iee
+                             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 iee
+       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,
+--- a/net/mac80211/mesh_hwmp.c
++++ b/net/mac80211/mesh_hwmp.c
+@@ -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(stru
+ 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 ieee8
+       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);
+ }
+ /**
+--- a/net/mac80211/mesh_plink.c
++++ b/net/mac80211/mesh_plink.c
+@@ -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 ieee8021
+                        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 ieee8021
+               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);
+ }
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -3317,8 +3317,11 @@ static bool ieee80211_assoc_success(stru
+               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(stru
+       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(stru
+            (!(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(stru
+               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(stru
+                * 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(stru
+       ret = true;
+  out:
++      kfree(elems);
+       kfree(bss_ies);
+       return ret;
+ }
+@@ -3673,7 +3684,7 @@ static void ieee80211_rx_mgmt_assoc_resp
+       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
+           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
+               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
+                              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(str
+       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(str
+       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(str
+                               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(str
+               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(str
+        */
+       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(str
+                       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(str
+       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(str
+        */
+       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(str
+               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(str
+       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(str
+                                           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
+       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
+               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
+                               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
+                        * 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;
+       }
+--- a/net/mac80211/scan.c
++++ b/net/mac80211/scan.c
+@@ -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 ieee802
+       };
+       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 ieee802
+       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 ieee802
+               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;
+ }
+--- a/net/mac80211/tdls.c
++++ b/net/mac80211/tdls.c
+@@ -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_re
+                                          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_re
+               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_re
+       /* 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_re
+                                         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_re
+               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_re
+       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_re
+       /* 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_re
+       }
+       /* 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_re
+ out:
+       mutex_unlock(&local->sta_mtx);
+       dev_kfree_skb_any(params.tmpl_skb);
++free:
++      kfree(elems);
+       return ret;
+ }
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -1396,8 +1396,8 @@ _ieee802_11_parse_elems_crc(const u8 *st
+ 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;
+@@ -1464,16 +1464,20 @@ static size_t ieee802_11_find_bssid_prof
+       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;
+@@ -1520,6 +1524,8 @@ void ieee802_11_parse_elems_crc(const u8
+       kfree(nontransmitted_profile);
+       elems->crc = crc;
++
++      return elems;
+ }
+ void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata,
diff --git a/queue-5.15/mac80211-fix-memory-leaks-with-element-parsing.patch b/queue-5.15/mac80211-fix-memory-leaks-with-element-parsing.patch
new file mode 100644 (file)
index 0000000..25a3056
--- /dev/null
@@ -0,0 +1,127 @@
+From foo@baz Fri Oct 14 10:18:27 AM CEST 2022
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 13 Oct 2022 20:16:00 +0200
+Subject: mac80211: fix memory leaks with element parsing
+To: stable@vger.kernel.org
+Cc: johannes@sipsolutions.net
+Message-ID: <20221013181601.5712-5-nbd@nbd.name>
+
+From: Johannes Berg <johannes.berg@intel.com>
+
+commit 8223ac199a3849257e86ec27865dc63f034b1cf1 upstream.
+
+My previous commit 5d24828d05f3 ("mac80211: always allocate
+struct ieee802_11_elems") had a few bugs and leaked the new
+allocated struct in a few error cases, fix that.
+
+Fixes: 5d24828d05f3 ("mac80211: always allocate struct ieee802_11_elems")
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Link: https://lore.kernel.org/r/20211001211108.9839928e42e0.Ib81ca187d3d3af7ed1bfeac2e00d08a4637c8025@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Cc: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/mac80211/agg-rx.c |    3 ++-
+ net/mac80211/ibss.c   |   10 +++++-----
+ net/mac80211/mlme.c   |   38 +++++++++++++++++++-------------------
+ 3 files changed, 26 insertions(+), 25 deletions(-)
+
+--- a/net/mac80211/agg-rx.c
++++ b/net/mac80211/agg-rx.c
+@@ -499,13 +499,14 @@ void ieee80211_process_addba_request(str
+               elems = ieee802_11_parse_elems(mgmt->u.action.u.addba_req.variable,
+                                              ies_len, true, mgmt->bssid, NULL);
+               if (!elems || elems->parse_error)
+-                      return;
++                      goto free;
+       }
+       __ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
+                                       start_seq_num, ba_policy, tid,
+                                       buf_size, true, false,
+                                       elems ? elems->addba_ext_ie : NULL);
++free:
+       kfree(elems);
+ }
+--- a/net/mac80211/ibss.c
++++ b/net/mac80211/ibss.c
+@@ -1663,11 +1663,11 @@ void ieee80211_ibss_rx_queued_mgmt(struc
+                               mgmt->u.action.u.chan_switch.variable,
+                               ies_len, true, mgmt->bssid, NULL);
+-                      if (!elems || elems->parse_error)
+-                              break;
+-
+-                      ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt, skb->len,
+-                                                      rx_status, elems);
++                      if (elems && !elems->parse_error)
++                              ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt,
++                                                              skb->len,
++                                                              rx_status,
++                                                              elems);
+                       kfree(elems);
+                       break;
+               }
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -3374,8 +3374,10 @@ static bool ieee80211_assoc_success(stru
+                       bss_ies = kmemdup(ies, sizeof(*ies) + ies->len,
+                                         GFP_ATOMIC);
+               rcu_read_unlock();
+-              if (!bss_ies)
+-                      return false;
++              if (!bss_ies) {
++                      ret = false;
++                      goto out;
++              }
+               bss_elems = ieee802_11_parse_elems(bss_ies->data, bss_ies->len,
+                                                  false, mgmt->bssid,
+@@ -4358,13 +4360,11 @@ void ieee80211_sta_rx_queued_mgmt(struct
+                                       mgmt->u.action.u.chan_switch.variable,
+                                       ies_len, true, mgmt->bssid, NULL);
+-                      if (!elems || elems->parse_error)
+-                              break;
+-
+-                      ieee80211_sta_process_chanswitch(sdata,
+-                                               rx_status->mactime,
+-                                               rx_status->device_timestamp,
+-                                               elems, false);
++                      if (elems && !elems->parse_error)
++                              ieee80211_sta_process_chanswitch(sdata,
++                                                               rx_status->mactime,
++                                                               rx_status->device_timestamp,
++                                                               elems, false);
+                       kfree(elems);
+               } else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
+                       struct ieee802_11_elems *elems;
+@@ -4384,17 +4384,17 @@ void ieee80211_sta_rx_queued_mgmt(struct
+                                       mgmt->u.action.u.ext_chan_switch.variable,
+                                       ies_len, true, mgmt->bssid, NULL);
+-                      if (!elems || elems->parse_error)
+-                              break;
++                      if (elems && !elems->parse_error) {
++                              /* for the handling code pretend it was an 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);
++                      }
+-                      /* for the handling code pretend this was also an 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);
+                       kfree(elems);
+               }
+               break;
diff --git a/queue-5.15/mac80211-mesh-clean-up-rx_bcn_presp-api.patch b/queue-5.15/mac80211-mesh-clean-up-rx_bcn_presp-api.patch
new file mode 100644 (file)
index 0000000..8aa1d62
--- /dev/null
@@ -0,0 +1,122 @@
+From foo@baz Fri Oct 14 10:18:27 AM CEST 2022
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 13 Oct 2022 20:15:56 +0200
+Subject: mac80211: mesh: clean up rx_bcn_presp API
+To: stable@vger.kernel.org
+Cc: johannes@sipsolutions.net
+Message-ID: <20221013181601.5712-1-nbd@nbd.name>
+
+From: Johannes Berg <johannes.berg@intel.com>
+
+commit a5b983c6073140b624f64e79fea6d33c3e4315a0 upstream.
+
+We currently pass the entire elements to the rx_bcn_presp()
+method, but only need mesh_config. Additionally, we use the
+length of the elements to calculate back the entire frame's
+length, but that's confusing - just pass the length of the
+frame instead.
+
+Link: https://lore.kernel.org/r/20210920154009.a18ed3d2da6c.I1824b773a0fbae4453e1433c184678ca14e8df45@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Cc: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/mac80211/ieee80211_i.h |    7 +++----
+ net/mac80211/mesh.c        |    4 ++--
+ net/mac80211/mesh_sync.c   |   26 ++++++++++++--------------
+ 3 files changed, 17 insertions(+), 20 deletions(-)
+
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -631,10 +631,9 @@ struct ieee80211_if_ocb {
+  */
+ struct ieee802_11_elems;
+ struct ieee80211_mesh_sync_ops {
+-      void (*rx_bcn_presp)(struct ieee80211_sub_if_data *sdata,
+-                           u16 stype,
+-                           struct ieee80211_mgmt *mgmt,
+-                           struct ieee802_11_elems *elems,
++      void (*rx_bcn_presp)(struct ieee80211_sub_if_data *sdata, u16 stype,
++                           struct ieee80211_mgmt *mgmt, unsigned int len,
++                           const struct ieee80211_meshconf_ie *mesh_cfg,
+                            struct ieee80211_rx_status *rx_status);
+       /* should be called with beacon_data under RCU read lock */
+--- a/net/mac80211/mesh.c
++++ b/net/mac80211/mesh.c
+@@ -1354,8 +1354,8 @@ static void ieee80211_mesh_rx_bcn_presp(
+       }
+       if (ifmsh->sync_ops)
+-              ifmsh->sync_ops->rx_bcn_presp(sdata,
+-                      stype, mgmt, &elems, rx_status);
++              ifmsh->sync_ops->rx_bcn_presp(sdata, stype, mgmt, len,
++                                            elems.mesh_config, rx_status);
+ }
+ int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
+--- a/net/mac80211/mesh_sync.c
++++ b/net/mac80211/mesh_sync.c
+@@ -3,6 +3,7 @@
+  * Copyright 2011-2012, Pavel Zubarev <pavel.zubarev@gmail.com>
+  * Copyright 2011-2012, Marco Porsch <marco.porsch@s2005.tu-chemnitz.de>
+  * Copyright 2011-2012, cozybit Inc.
++ * Copyright (C) 2021 Intel Corporation
+  */
+ #include "ieee80211_i.h"
+@@ -35,12 +36,12 @@ struct sync_method {
+ /**
+  * mesh_peer_tbtt_adjusting - check if an mp is currently adjusting its TBTT
+  *
+- * @ie: information elements of a management frame from the mesh peer
++ * @cfg: mesh config element from the mesh peer (or %NULL)
+  */
+-static bool mesh_peer_tbtt_adjusting(struct ieee802_11_elems *ie)
++static bool mesh_peer_tbtt_adjusting(const struct ieee80211_meshconf_ie *cfg)
+ {
+-      return (ie->mesh_config->meshconf_cap &
+-                      IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING) != 0;
++      return cfg &&
++             (cfg->meshconf_cap & IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING);
+ }
+ void mesh_sync_adjust_tsf(struct ieee80211_sub_if_data *sdata)
+@@ -76,11 +77,11 @@ void mesh_sync_adjust_tsf(struct ieee802
+       }
+ }
+-static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
+-                                 u16 stype,
+-                                 struct ieee80211_mgmt *mgmt,
+-                                 struct ieee802_11_elems *elems,
+-                                 struct ieee80211_rx_status *rx_status)
++static void
++mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, u16 stype,
++                            struct ieee80211_mgmt *mgmt, unsigned int len,
++                            const struct ieee80211_meshconf_ie *mesh_cfg,
++                            struct ieee80211_rx_status *rx_status)
+ {
+       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+       struct ieee80211_local *local = sdata->local;
+@@ -101,10 +102,7 @@ static void mesh_sync_offset_rx_bcn_pres
+        */
+       if (ieee80211_have_rx_timestamp(rx_status))
+               t_r = ieee80211_calculate_rx_timestamp(local, rx_status,
+-                                                     24 + 12 +
+-                                                     elems->total_len +
+-                                                     FCS_LEN,
+-                                                     24);
++                                                     len + FCS_LEN, 24);
+       else
+               t_r = drv_get_tsf(local, sdata);
+@@ -119,7 +117,7 @@ static void mesh_sync_offset_rx_bcn_pres
+        * dot11MeshNbrOffsetMaxNeighbor non-peer non-MBSS neighbors
+        */
+-      if (elems->mesh_config && mesh_peer_tbtt_adjusting(elems)) {
++      if (mesh_peer_tbtt_adjusting(mesh_cfg)) {
+               msync_dbg(sdata, "STA %pM : is adjusting TBTT\n",
+                         sta->sta.addr);
+               goto no_sync;
diff --git a/queue-5.15/mac80211-mlme-find-auth-challenge-directly.patch b/queue-5.15/mac80211-mlme-find-auth-challenge-directly.patch
new file mode 100644 (file)
index 0000000..41c804d
--- /dev/null
@@ -0,0 +1,92 @@
+From foo@baz Fri Oct 14 10:18:27 AM CEST 2022
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 13 Oct 2022 20:15:58 +0200
+Subject: mac80211: mlme: find auth challenge directly
+To: stable@vger.kernel.org
+Cc: johannes@sipsolutions.net
+Message-ID: <20221013181601.5712-3-nbd@nbd.name>
+
+From: Johannes Berg <johannes.berg@intel.com>
+
+commit 49a765d6785e99157ff5091cc37485732496864e upstream.
+
+There's no need to parse all elements etc. just to find the
+authentication challenge - use cfg80211_find_elem() instead.
+This also allows us to remove WLAN_EID_CHALLENGE handling
+from the element parsing entirely.
+
+Link: https://lore.kernel.org/r/20210920154009.45f9b3a15722.Ice3159ffad03a007d6154cbf1fb3a8c48489e86f@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Cc: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/mac80211/ieee80211_i.h |    2 --
+ net/mac80211/mlme.c        |   11 ++++++-----
+ net/mac80211/util.c        |    4 ----
+ 3 files changed, 6 insertions(+), 11 deletions(-)
+
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -1542,7 +1542,6 @@ struct ieee802_11_elems {
+       const u8 *supp_rates;
+       const u8 *ds_params;
+       const struct ieee80211_tim_ie *tim;
+-      const u8 *challenge;
+       const u8 *rsn;
+       const u8 *rsnx;
+       const u8 *erp_info;
+@@ -1596,7 +1595,6 @@ struct ieee802_11_elems {
+       u8 ssid_len;
+       u8 supp_rates_len;
+       u8 tim_len;
+-      u8 challenge_len;
+       u8 rsn_len;
+       u8 rsnx_len;
+       u8 ext_supp_rates_len;
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -2889,17 +2889,17 @@ static void ieee80211_auth_challenge(str
+ {
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data;
++      const struct element *challenge;
+       u8 *pos;
+-      struct ieee802_11_elems elems;
+       u32 tx_flags = 0;
+       struct ieee80211_prep_tx_info info = {
+               .subtype = IEEE80211_STYPE_AUTH,
+       };
+       pos = mgmt->u.auth.variable;
+-      ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, &elems,
+-                             mgmt->bssid, auth_data->bss->bssid);
+-      if (!elems.challenge)
++      challenge = cfg80211_find_elem(WLAN_EID_CHALLENGE, pos,
++                                     len - (pos - (u8 *)mgmt));
++      if (!challenge)
+               return;
+       auth_data->expected_transaction = 4;
+       drv_mgd_prepare_tx(sdata->local, sdata, &info);
+@@ -2907,7 +2907,8 @@ static void ieee80211_auth_challenge(str
+               tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
+                          IEEE80211_TX_INTFL_MLME_CONN_TX;
+       ieee80211_send_auth(sdata, 3, auth_data->algorithm, 0,
+-                          elems.challenge - 2, elems.challenge_len + 2,
++                          (void *)challenge,
++                          challenge->datalen + sizeof(*challenge),
+                           auth_data->bss->bssid, auth_data->bss->bssid,
+                           auth_data->key, auth_data->key_len,
+                           auth_data->key_idx, tx_flags);
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -1117,10 +1117,6 @@ _ieee802_11_parse_elems_crc(const u8 *st
+                       } else
+                               elem_parse_failed = true;
+                       break;
+-              case WLAN_EID_CHALLENGE:
+-                      elems->challenge = pos;
+-                      elems->challenge_len = elen;
+-                      break;
+               case WLAN_EID_VENDOR_SPECIFIC:
+                       if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 &&
+                           pos[2] == 0xf2) {
diff --git a/queue-5.15/mac80211-move-crc-into-struct-ieee802_11_elems.patch b/queue-5.15/mac80211-move-crc-into-struct-ieee802_11_elems.patch
new file mode 100644 (file)
index 0000000..b1d2301
--- /dev/null
@@ -0,0 +1,94 @@
+From foo@baz Fri Oct 14 10:18:27 AM CEST 2022
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 13 Oct 2022 20:15:57 +0200
+Subject: mac80211: move CRC into struct ieee802_11_elems
+To: stable@vger.kernel.org
+Cc: johannes@sipsolutions.net
+Message-ID: <20221013181601.5712-2-nbd@nbd.name>
+
+From: Johannes Berg <johannes.berg@intel.com>
+
+commit c6e37ed498f958254b5459253199e816b6bfc52f upstream.
+
+We're currently returning this value, but to prepare for
+returning the allocated structure, move it into there.
+
+Link: https://lore.kernel.org/r/20210920154009.479b8ebf999d.If0d4ba75ee38998dc3eeae25058aa748efcb2fc9@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Cc: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/mac80211/ieee80211_i.h |    9 +++++----
+ net/mac80211/mlme.c        |    9 +++++----
+ net/mac80211/util.c        |   10 +++++-----
+ 3 files changed, 15 insertions(+), 13 deletions(-)
+
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -1532,6 +1532,7 @@ struct ieee80211_csa_ie {
+ struct ieee802_11_elems {
+       const u8 *ie_start;
+       size_t total_len;
++      u32 crc;
+       /* pointers to IEs */
+       const struct ieee80211_tdls_lnkie *lnk_id;
+@@ -2218,10 +2219,10 @@ static inline void ieee80211_tx_skb(stru
+       ieee80211_tx_skb_tid(sdata, skb, 7);
+ }
+-u32 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);
++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,
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -4102,10 +4102,11 @@ static void ieee80211_rx_mgmt_beacon(str
+        */
+       if (!ieee80211_is_s1g_beacon(hdr->frame_control))
+               ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
+-      ncrc = ieee802_11_parse_elems_crc(variable,
+-                                        len - baselen, false, &elems,
+-                                        care_about_ies, ncrc,
+-                                        mgmt->bssid, bssid);
++      ieee802_11_parse_elems_crc(variable,
++                                 len - baselen, false, &elems,
++                                 care_about_ies, ncrc,
++                                 mgmt->bssid, bssid);
++      ncrc = elems.crc;
+       if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) &&
+           ieee80211_check_tim(elems.tim, elems.tim_len, bss_conf->aid)) {
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -1468,10 +1468,10 @@ static size_t ieee802_11_find_bssid_prof
+       return found ? profile_len : 0;
+ }
+-u32 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)
++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)
+ {
+       const struct element *non_inherit = NULL;
+       u8 *nontransmitted_profile;
+@@ -1523,7 +1523,7 @@ u32 ieee802_11_parse_elems_crc(const u8
+       kfree(nontransmitted_profile);
+-      return crc;
++      elems->crc = crc;
+ }
+ void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata,
index e0564928560bf3e6a3533bf6c829725699fba006..aa00b42dbb242dfc77e46671b05078784e3024dc 100644 (file)
@@ -25,3 +25,9 @@ input-xpad-add-supported-devices-as-contributed-on-github.patch
 input-xpad-fix-wireless-360-controller-breaking-after-suspend.patch
 misc-pci_endpoint_test-aggregate-params-checking-for-xfer.patch
 misc-pci_endpoint_test-fix-pci_endpoint_test_-copy-write-read-panic.patch
+mac80211-mesh-clean-up-rx_bcn_presp-api.patch
+mac80211-move-crc-into-struct-ieee802_11_elems.patch
+mac80211-mlme-find-auth-challenge-directly.patch
+mac80211-always-allocate-struct-ieee802_11_elems.patch
+mac80211-fix-memory-leaks-with-element-parsing.patch
+wifi-mac80211-fix-mbssid-parsing-use-after-free.patch
diff --git a/queue-5.15/wifi-mac80211-fix-mbssid-parsing-use-after-free.patch b/queue-5.15/wifi-mac80211-fix-mbssid-parsing-use-after-free.patch
new file mode 100644 (file)
index 0000000..69fe9d8
--- /dev/null
@@ -0,0 +1,105 @@
+From foo@baz Fri Oct 14 10:18:27 AM CEST 2022
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 13 Oct 2022 20:16:01 +0200
+Subject: wifi: mac80211: fix MBSSID parsing use-after-free
+To: stable@vger.kernel.org
+Cc: johannes@sipsolutions.net
+Message-ID: <20221013181601.5712-6-nbd@nbd.name>
+
+From: Johannes Berg <johannes.berg@intel.com>
+
+commit ff05d4b45dd89b922578dac497dcabf57cf771c6
+
+When we parse a multi-BSSID element, we might point some
+element pointers into the allocated nontransmitted_profile.
+However, we free this before returning, causing UAF when the
+relevant pointers in the parsed elements are accessed.
+
+Fix this by not allocating the scratch buffer separately but
+as part of the returned structure instead, that way, there
+are no lifetime issues with it.
+
+The scratch buffer introduction as part of the returned data
+here is taken from MLO feature work done by Ilan.
+
+This fixes CVE-2022-42719.
+
+Fixes: 5023b14cf4df ("mac80211: support profile split between elements")
+Co-developed-by: Ilan Peer <ilan.peer@intel.com>
+Signed-off-by: Ilan Peer <ilan.peer@intel.com>
+Reviewed-by: Kees Cook <keescook@chromium.org>
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Cc: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/mac80211/ieee80211_i.h |    8 ++++++++
+ net/mac80211/util.c        |   29 ++++++++++++++---------------
+ 2 files changed, 22 insertions(+), 15 deletions(-)
+
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -1613,6 +1613,14 @@ struct ieee802_11_elems {
+       /* whether a parse error occurred while retrieving these elements */
+       bool parse_error;
++
++      /*
++       * scratch buffer that can be used for various element parsing related
++       * tasks, e.g., element de-fragmentation etc.
++       */
++      size_t scratch_len;
++      u8 *scratch_pos;
++      u8 scratch[];
+ };
+ static inline struct ieee80211_local *hw_to_local(
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -1475,24 +1475,25 @@ struct ieee802_11_elems *ieee802_11_pars
+       u8 *nontransmitted_profile;
+       int nontransmitted_profile_len = 0;
+-      elems = kzalloc(sizeof(*elems), GFP_ATOMIC);
++      elems = kzalloc(sizeof(*elems) + len, GFP_ATOMIC);
+       if (!elems)
+               return NULL;
+       elems->ie_start = start;
+       elems->total_len = len;
+-      nontransmitted_profile = kmalloc(len, GFP_ATOMIC);
+-      if (nontransmitted_profile) {
+-              nontransmitted_profile_len =
+-                      ieee802_11_find_bssid_profile(start, len, elems,
+-                                                    transmitter_bssid,
+-                                                    bss_bssid,
+-                                                    nontransmitted_profile);
+-              non_inherit =
+-                      cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
+-                                             nontransmitted_profile,
+-                                             nontransmitted_profile_len);
+-      }
++      elems->scratch_len = len;
++      elems->scratch_pos = elems->scratch;
++
++      nontransmitted_profile = elems->scratch_pos;
++      nontransmitted_profile_len =
++              ieee802_11_find_bssid_profile(start, len, elems,
++                                            transmitter_bssid,
++                                            bss_bssid,
++                                            nontransmitted_profile);
++      non_inherit =
++              cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
++                                     nontransmitted_profile,
++                                     nontransmitted_profile_len);
+       crc = _ieee802_11_parse_elems_crc(start, len, action, elems, filter,
+                                         crc, non_inherit);
+@@ -1521,8 +1522,6 @@ struct ieee802_11_elems *ieee802_11_pars
+           offsetofend(struct ieee80211_bssid_index, dtim_count))
+               elems->dtim_count = elems->bssid_index->dtim_count;
+-      kfree(nontransmitted_profile);
+-
+       elems->crc = crc;
+       return elems;