FIX: [mac80211] CVE-2022-47522 fixes

BugzId: 82740
This commit is contained in:
Patrick Walther 2023-04-06 16:57:53 +02:00
parent 471d605f9e
commit adb94d90b6
1 changed files with 296 additions and 27 deletions

View File

@ -6,6 +6,103 @@ Date: Wed Sep 14 14:25:55 2022 +0200
%% original patch: 0002-backport-of-subsys-patches-from-openwrt.patch
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index 8ff3330..2997959 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -4817,9 +4817,6 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
return;
}
- if (vif->type != NL80211_IFTYPE_STATION)
- return;
-
/* Make sure we're done with the deferred traffic before flushing */
flush_work(&mvm->add_stream_wk);
@@ -4837,9 +4834,6 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
if (mvmsta->vif != vif)
continue;
- /* make sure only TDLS peers or the AP are flushed */
- WARN_ON(i != mvmvif->ap_sta_id && !sta->tdls);
-
if (drop) {
if (iwl_mvm_flush_sta(mvm, mvmsta, false))
IWL_ERR(mvm, "flush request fail\n");
@@ -4859,6 +4853,31 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
iwl_trans_wait_tx_queues_empty(mvm->trans, msk);
}
+static void iwl_mvm_mac_flush_sta(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int i;
+
+ mutex_lock(&mvm->mutex);
+ for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
+ struct iwl_mvm_sta *mvmsta;
+ struct ieee80211_sta *tmp;
+
+ tmp = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+ lockdep_is_held(&mvm->mutex));
+ if (tmp != sta)
+ continue;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+ if (iwl_mvm_flush_sta(mvm, mvmsta, false))
+ IWL_ERR(mvm, "flush request fail\n");
+ }
+ mutex_unlock(&mvm->mutex);
+}
+
static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx,
struct survey_info *survey)
{
@@ -5372,6 +5391,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
.mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
.mgd_protect_tdls_discover = iwl_mvm_mac_mgd_protect_tdls_discover,
.flush = iwl_mvm_mac_flush,
+ .flush_sta = iwl_mvm_mac_flush_sta,
.sched_scan_start = iwl_mvm_mac_sched_scan_start,
.sched_scan_stop = iwl_mvm_mac_sched_scan_stop,
.set_key = iwl_mvm_mac_set_key,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
index 7beb203..d4c7d45 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
@@ -551,8 +551,9 @@ static void iwl_mvm_skb_prepare_status(struct sk_buff *skb,
static int iwl_mvm_get_ctrl_vif_queue(struct iwl_mvm *mvm,
struct ieee80211_tx_info *info,
- struct ieee80211_hdr *hdr)
+ struct sk_buff *skb)
{
+ struct ieee80211_hdr *hdr = (void *)skb->data;
struct iwl_mvm_vif *mvmvif =
iwl_mvm_vif_from_mac80211(info->control.vif);
__le16 fc = hdr->frame_control;
@@ -571,7 +572,7 @@ static int iwl_mvm_get_ctrl_vif_queue(struct iwl_mvm *mvm,
* reason 7 ("Class 3 frame received from nonassociated STA").
*/
if (ieee80211_is_mgmt(fc) &&
- (!ieee80211_is_bufferable_mmpdu(fc) ||
+ (!ieee80211_is_bufferable_mmpdu(skb) ||
ieee80211_is_deauth(fc) || ieee80211_is_disassoc(fc)))
return mvm->probe_queue;
@@ -689,7 +690,7 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
else
sta_id = mvmvif->mcast_sta.sta_id;
- queue = iwl_mvm_get_ctrl_vif_queue(mvm, &info, hdr);
+ queue = iwl_mvm_get_ctrl_vif_queue(mvm, &info, skb);
} else if (info.control.vif->type == NL80211_IFTYPE_MONITOR) {
queue = mvm->snif_queue;
sta_id = mvm->snif_sta.sta_id;
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index bfc2b1f..ea0749c 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
@ -80,6 +177,85 @@ index bfc2b1f..ea0749c 100644
rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
hdr = (void *)skb->data;
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 6942645..637e9fd 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -737,20 +737,6 @@ static inline bool ieee80211_is_any_nullfunc(__le16 fc)
return (ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc));
}
-/**
- * ieee80211_is_bufferable_mmpdu - check if frame is bufferable MMPDU
- * @fc: frame control field in little-endian byteorder
- */
-static inline bool ieee80211_is_bufferable_mmpdu(__le16 fc)
-{
- /* IEEE 802.11-2012, definition of "bufferable management frame";
- * note that this ignores the IBSS special case. */
- return ieee80211_is_mgmt(fc) &&
- (ieee80211_is_action(fc) ||
- ieee80211_is_disassoc(fc) ||
- ieee80211_is_deauth(fc));
-}
-
/**
* ieee80211_is_first_frag - check if IEEE80211_SCTL_FRAG is not set
* @seq_ctrl: frame sequence control bytes in little-endian byteorder
@@ -3188,7 +3174,7 @@ enum ieee80211_pub_actioncode {
WLAN_PUB_ACTION_NETWORK_CHANNEL_CONTROL = 30,
WLAN_PUB_ACTION_WHITE_SPACE_MAP_ANN = 31,
WLAN_PUB_ACTION_FTM_REQUEST = 32,
- WLAN_PUB_ACTION_FTM = 33,
+ WLAN_PUB_ACTION_FTM_RESPONSE = 33,
WLAN_PUB_ACTION_FILS_DISCOVERY = 34,
};
@@ -3667,6 +3653,44 @@ static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr)
return hdr->addr1;
}
+/**
+ * ieee80211_is_bufferable_mmpdu - check if frame is bufferable MMPDU
+ * @skb: the skb to check, starting with the 802.11 header
+ */
+static inline bool ieee80211_is_bufferable_mmpdu(struct sk_buff *skb)
+{
+ struct ieee80211_mgmt *mgmt = (void *)skb->data;
+ __le16 fc = mgmt->frame_control;
+
+ /*
+ * IEEE 802.11 REVme D2.0 definition of bufferable MMPDU;
+ * note that this ignores the IBSS special case.
+ */
+ if (!ieee80211_is_mgmt(fc))
+ return false;
+
+ if (ieee80211_is_disassoc(fc) || ieee80211_is_deauth(fc))
+ return true;
+
+ if (!ieee80211_is_action(fc))
+ return false;
+
+ if (skb->len < offsetofend(typeof(*mgmt), u.action.u.ftm.action_code))
+ return true;
+
+ /* action frame - additionally check for non-bufferable FTM */
+
+ if (mgmt->u.action.category != WLAN_CATEGORY_PUBLIC &&
+ mgmt->u.action.category != WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION)
+ return true;
+
+ if (mgmt->u.action.u.ftm.action_code == WLAN_PUB_ACTION_FTM_REQUEST ||
+ mgmt->u.action.u.ftm.action_code == WLAN_PUB_ACTION_FTM_RESPONSE)
+ return false;
+
+ return true;
+}
+
/**
* _ieee80211_is_robust_mgmt_frame - check if frame is a robust management frame
* @hdr: the frame (buffer must include at least the first octet of payload)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 30c9ebd..ab83553 100644
--- a/include/net/cfg80211.h
@ -271,7 +447,7 @@ index 30c9ebd..ab83553 100644
/**
* cfg80211_gtk_rekey_notify - notify userspace about driver rekeying
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index a331431..4c2760d 100644
index a331431..7293174 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1566,6 +1566,7 @@ enum ieee80211_smps_mode {
@ -325,7 +501,18 @@ index a331431..4c2760d 100644
/* keep last, obviously */
NUM_IEEE80211_HW_FLAGS
@@ -3937,6 +3946,16 @@ struct ieee80211_prep_tx_info {
@@ -3685,6 +3694,10 @@ struct ieee80211_prep_tx_info {
* Note that vif can be NULL.
* The callback can sleep.
*
+ * @flush_sta: Flush or drop all pending frames from the hardware queue(s) for
+ * the given station, as it's about to be removed.
+ * The callback can sleep.
+ *
* @channel_switch: Drivers that need (or want) to offload the channel
* switch operation for CSAs received from the AP may implement this
* callback. They must then call ieee80211_chswitch_done() to indicate
@@ -3937,6 +3950,16 @@ struct ieee80211_prep_tx_info {
* twt structure.
* @twt_teardown_request: Update the hw with TWT teardown request received
* from the peer.
@ -342,7 +529,16 @@ index a331431..4c2760d 100644
*/
struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw,
@@ -4265,6 +4284,15 @@ struct ieee80211_ops {
@@ -4103,6 +4126,8 @@ struct ieee80211_ops {
#endif
void (*flush)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
u32 queues, bool drop);
+ void (*flush_sta)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
void (*channel_switch)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel_switch *ch_switch);
@@ -4265,6 +4290,15 @@ struct ieee80211_ops {
struct ieee80211_twt_setup *twt);
void (*twt_teardown_request)(struct ieee80211_hw *hw,
struct ieee80211_sta *sta, u8 flowid);
@ -358,7 +554,7 @@ index a331431..4c2760d 100644
};
/**
@@ -4916,12 +4944,14 @@ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets);
@@ -4916,12 +4950,14 @@ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets);
* @cntdwn_counter_offs: array of IEEE80211_MAX_CNTDWN_COUNTERS_NUM offsets
* to countdown counters. This array can contain zero values which
* should be ignored.
@ -373,7 +569,7 @@ index a331431..4c2760d 100644
};
/**
@@ -6642,6 +6672,9 @@ static inline void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
@@ -6642,6 +6678,9 @@ static inline void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
{
}
@ -383,7 +579,7 @@ index a331431..4c2760d 100644
/**
* ieee80211_schedule_txq - schedule a TXQ for transmission
*
@@ -6654,7 +6687,11 @@ static inline void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
@@ -6654,7 +6693,11 @@ static inline void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
* The driver may call this function if it has buffered packets for
* this TXQ internally.
*/
@ -396,7 +592,7 @@ index a331431..4c2760d 100644
/**
* ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
@@ -6666,8 +6703,12 @@ void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
@@ -6666,8 +6709,12 @@ void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
* The driver may set force=true if it has buffered packets for this TXQ
* internally.
*/
@ -1275,10 +1471,32 @@ index 8be28cf..afac8d4 100644
}
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 912c75d..bd1e46e 100644
index 912c75d..5e10e24 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -1486,4 +1486,28 @@ static inline void drv_twt_teardown_request(struct ieee80211_local *local,
@@ -639,6 +639,21 @@ static inline void drv_flush(struct ieee80211_local *local,
trace_drv_return_void(local);
}
+static inline void drv_flush_sta(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta)
+{
+ might_sleep();
+
+ if (sdata && !check_sdata_in_driver(sdata))
+ return;
+
+ trace_drv_flush_sta(local, sdata, &sta->sta);
+ if (local->ops->flush_sta)
+ local->ops->flush_sta(&local->hw, &sdata->vif, &sta->sta);
+ trace_drv_return_void(local);
+}
+
static inline void drv_channel_switch(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel_switch *ch_switch)
@@ -1486,4 +1501,28 @@ static inline void drv_twt_teardown_request(struct ieee80211_local *local,
trace_drv_return_void(local);
}
@ -3219,7 +3437,7 @@ index 887f945..e692a24 100644
ieee80211_hw_config(local, 0);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index dccaf4b..d67dfdc 100644
index dccaf4b..24a7d57 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -357,6 +357,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
@ -3247,7 +3465,26 @@ index dccaf4b..d67dfdc 100644
}
for (i = 0; i < IEEE80211_NUM_TIDS; i++)
@@ -1888,59 +1893,29 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
@@ -1065,6 +1070,18 @@ static void __sta_info_destroy_part2(struct sta_info *sta)
WARN_ON_ONCE(ret);
}
+ /* Flush queues before removing keys, as that might remove them
+ * from hardware, and then depending on the offload method, any
+ * frames sitting on hardware queues might be sent out without
+ * any encryption at all.
+ */
+ if (local->ops->set_key) {
+ if (local->ops->flush_sta)
+ drv_flush_sta(local, sta->sdata, sta);
+ else
+ ieee80211_flush_queues(local, sta->sdata, false);
+ }
+
/* now keys can no longer be reached */
ieee80211_free_sta_keys(local, sta);
@@ -1888,59 +1905,29 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
}
EXPORT_SYMBOL(ieee80211_sta_set_buffered);
@ -3279,9 +3516,15 @@ index dccaf4b..d67dfdc 100644
- /* Weights scale so the unit weight is 256 */
- airtime <<= 8;
-
+ spin_lock_bh(&local->active_txq_lock[ac]);
+ sta->airtime[ac].tx_airtime += tx_airtime;
+ sta->airtime[ac].rx_airtime += rx_airtime;
- spin_lock_bh(&air_sched->lock);
-
+ diff = (u32)jiffies - sta->airtime[ac].last_active;
+ if (diff <= AIRTIME_ACTIVE_DURATION)
+ sta->airtime[ac].deficit -= airtime;
- air_info->tx_airtime += tx_airtime;
- air_info->rx_airtime += rx_airtime;
-
@ -3292,20 +3535,14 @@ index dccaf4b..d67dfdc 100644
- weight_sum = air_info->weight;
- weight_sum_reciprocal = air_info->weight_reciprocal;
- }
+ spin_lock_bh(&local->active_txq_lock[ac]);
+ sta->airtime[ac].tx_airtime += tx_airtime;
+ sta->airtime[ac].rx_airtime += rx_airtime;
-
- /* Round the calculation of global vt */
- air_sched->v_t += (u64)((airtime + (weight_sum >> 1)) *
- weight_sum_reciprocal) >> IEEE80211_RECIPROCAL_SHIFT_64;
- air_info->v_t += (u32)((airtime + (air_info->weight >> 1)) *
- air_info->weight_reciprocal) >> IEEE80211_RECIPROCAL_SHIFT_32;
- ieee80211_resort_txq(&local->hw, txq);
+ diff = (u32)jiffies - sta->airtime[ac].last_active;
+ if (diff <= AIRTIME_ACTIVE_DURATION)
+ sta->airtime[ac].deficit -= airtime;
-
- spin_unlock_bh(&air_sched->lock);
-}
-
@ -3322,7 +3559,7 @@ index dccaf4b..d67dfdc 100644
}
EXPORT_SYMBOL(ieee80211_sta_register_airtime);
@@ -1959,6 +1934,7 @@ void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
@@ -1959,6 +1946,7 @@ void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
&sta->airtime[ac].aql_tx_pending);
atomic_add(tx_airtime, &local->aql_total_pending_airtime);
@ -3330,7 +3567,7 @@ index dccaf4b..d67dfdc 100644
return;
}
@@ -1970,14 +1946,17 @@ void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
@@ -1970,14 +1958,17 @@ void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
tx_pending, 0);
}
@ -3351,7 +3588,7 @@ index dccaf4b..d67dfdc 100644
}
int sta_info_move_state(struct sta_info *sta,
@@ -2384,7 +2363,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
@@ -2384,7 +2375,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
}
if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT))) {
@ -3608,10 +3845,24 @@ index 45e532a..137be9e 100644
}
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 9e8381b..ffa1b7b 100644
index 9e8381b..a564f54 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -2892,6 +2892,15 @@ TRACE_EVENT(drv_twt_teardown_request,
@@ -1140,6 +1140,13 @@ TRACE_EVENT(drv_flush,
)
);
+DEFINE_EVENT(sta_event, drv_flush_sta,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta),
+ TP_ARGS(local, sdata, sta)
+);
+
TRACE_EVENT(drv_channel_switch,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
@@ -2892,6 +2899,15 @@ TRACE_EVENT(drv_twt_teardown_request,
)
);
@ -3628,7 +3879,7 @@ index 9e8381b..ffa1b7b 100644
#undef TRACE_INCLUDE_PATH
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 15888e4..50025ff 100644
index 15888e4..9fc4e7b 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -18,7 +18,6 @@
@ -3639,6 +3890,24 @@ index 15888e4..50025ff 100644
#include <net/net_namespace.h>
#include <net/ieee80211_radiotap.h>
#include <net/cfg80211.h>
@@ -488,7 +487,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
int ac = skb_get_queue_mapping(tx->skb);
if (ieee80211_is_mgmt(hdr->frame_control) &&
- !ieee80211_is_bufferable_mmpdu(hdr->frame_control)) {
+ !ieee80211_is_bufferable_mmpdu(tx->skb)) {
info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
return TX_CONTINUE;
}
@@ -1283,7 +1282,7 @@ static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local,
if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) &&
unlikely(!ieee80211_is_data_present(hdr->frame_control))) {
if ((!ieee80211_is_mgmt(hdr->frame_control) ||
- ieee80211_is_bufferable_mmpdu(hdr->frame_control) ||
+ ieee80211_is_bufferable_mmpdu(skb) ||
vif->type == NL80211_IFTYPE_STATION) &&
sta && sta->uploaded) {
/*
@@ -1480,7 +1479,7 @@ void ieee80211_txq_init(struct ieee80211_sub_if_data *sdata,
codel_vars_init(&txqi->def_cvars);
codel_stats_init(&txqi->cstats);