Johannes Berg says:

====================
Couple more fixes:

 - virt_wifi: remove SET_NETDEV_DEV to avoid UAF on teardown
 - iwlwifi:
   - fix (some) devices that don't have 6 GHz (WiFi6E)
   - fix potential OOB read of firmware notification
   - set WiFi generation for firmware to avoid packet drops
   - fix multi-link scan timing
 - wilc1000: fix integer overflow
 - ath11k/ath12k: fix TID during A-MPDU session teardown
 - wl1251: don't trust firmware TX status response index

* tag 'wireless-2026-03-26' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless:
  wifi: virt_wifi: remove SET_NETDEV_DEV to avoid use-after-free
  wifi: iwlwifi: mvm: fix potential out-of-bounds read in iwl_mvm_nd_match_info_handler()
  wifi: wl1251: validate packet IDs before indexing tx_frames
  wifi: wilc1000: fix u8 overflow in SSID scan buffer size calculation
  wifi: ath12k: Pass the correct value of each TID during a stop AMPDU session
  wifi: ath11k: Pass the correct value of each TID during a stop AMPDU session
  wifi: iwlwifi: mld: correctly set wifi generation data
  wifi: iwlwifi: mvm: don't send a 6E related command when not supported
  wifi: iwlwifi: mld: Fix MLO scan timing
====================

Link: https://patch.msgid.link/20260326093329.77815-3-johannes@sipsolutions.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2026-03-26 17:51:39 -07:00
16 changed files with 163 additions and 56 deletions

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/ieee80211.h>
@@ -1110,9 +1110,8 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar,
struct ath11k_base *ab = ar->ab;
struct ath11k_peer *peer;
struct ath11k_sta *arsta = ath11k_sta_to_arsta(params->sta);
struct dp_rx_tid *rx_tid;
int vdev_id = arsta->arvif->vdev_id;
dma_addr_t paddr;
bool active;
int ret;
spin_lock_bh(&ab->base_lock);
@@ -1124,15 +1123,14 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar,
return -ENOENT;
}
paddr = peer->rx_tid[params->tid].paddr;
active = peer->rx_tid[params->tid].active;
rx_tid = &peer->rx_tid[params->tid];
if (!active) {
if (!rx_tid->active) {
spin_unlock_bh(&ab->base_lock);
return 0;
}
ret = ath11k_peer_rx_tid_reo_update(ar, peer, peer->rx_tid, 1, 0, false);
ret = ath11k_peer_rx_tid_reo_update(ar, peer, rx_tid, 1, 0, false);
spin_unlock_bh(&ab->base_lock);
if (ret) {
ath11k_warn(ab, "failed to update reo for rx tid %d: %d\n",
@@ -1141,7 +1139,8 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar,
}
ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id,
params->sta->addr, paddr,
params->sta->addr,
rx_tid->paddr,
params->tid, 1, 1);
if (ret)
ath11k_warn(ab, "failed to send wmi to delete rx tid %d\n",

View File

@@ -735,6 +735,7 @@ int ath12k_dp_rx_ampdu_stop(struct ath12k *ar,
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
struct ath12k_dp_link_peer *peer;
struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(params->sta);
struct ath12k_dp_rx_tid *rx_tid;
struct ath12k_link_sta *arsta;
int vdev_id;
bool active;
@@ -770,7 +771,8 @@ int ath12k_dp_rx_ampdu_stop(struct ath12k *ar,
return 0;
}
ret = ath12k_dp_arch_peer_rx_tid_reo_update(dp, peer, peer->dp_peer->rx_tid,
rx_tid = &peer->dp_peer->rx_tid[params->tid];
ret = ath12k_dp_arch_peer_rx_tid_reo_update(dp, peer, rx_tid,
1, 0, false);
spin_unlock_bh(&dp->dp_lock);
if (ret) {

View File

@@ -296,6 +296,11 @@ enum iwl_legacy_cmds {
*/
SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E,
/**
* @SCAN_START_NOTIFICATION_UMAC: uses &struct iwl_umac_scan_start
*/
SCAN_START_NOTIFICATION_UMAC = 0xb2,
/**
* @MATCH_FOUND_NOTIFICATION: scan match found
*/

View File

@@ -1156,6 +1156,16 @@ enum iwl_umac_scan_abort_status {
IWL_UMAC_SCAN_ABORT_STATUS_NOT_FOUND,
};
/**
* struct iwl_umac_scan_start - scan start notification
* @uid: scan id, &enum iwl_umac_scan_uid_offsets
* @reserved: for future use
*/
struct iwl_umac_scan_start {
__le32 uid;
__le32 reserved;
} __packed; /* SCAN_START_UMAC_API_S_VER_1 */
/**
* struct iwl_umac_scan_complete - scan complete notification
* @uid: scan id, &enum iwl_umac_scan_uid_offsets

View File

@@ -111,14 +111,75 @@ static bool iwl_mld_is_nic_ack_enabled(struct iwl_mld *mld,
IEEE80211_HE_MAC_CAP2_ACK_EN);
}
static void iwl_mld_set_he_support(struct iwl_mld *mld,
struct ieee80211_vif *vif,
struct iwl_mac_config_cmd *cmd)
struct iwl_mld_mac_wifi_gen_sta_iter_data {
struct ieee80211_vif *vif;
struct iwl_mac_wifi_gen_support *support;
};
static void iwl_mld_mac_wifi_gen_sta_iter(void *_data,
struct ieee80211_sta *sta)
{
if (vif->type == NL80211_IFTYPE_AP)
cmd->wifi_gen.he_ap_support = 1;
else
cmd->wifi_gen.he_support = 1;
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
struct iwl_mld_mac_wifi_gen_sta_iter_data *data = _data;
struct ieee80211_link_sta *link_sta;
unsigned int link_id;
if (mld_sta->vif != data->vif)
return;
for_each_sta_active_link(data->vif, sta, link_sta, link_id) {
if (link_sta->he_cap.has_he)
data->support->he_support = 1;
if (link_sta->eht_cap.has_eht)
data->support->eht_support = 1;
}
}
static void iwl_mld_set_wifi_gen(struct iwl_mld *mld,
struct ieee80211_vif *vif,
struct iwl_mac_wifi_gen_support *support)
{
struct iwl_mld_mac_wifi_gen_sta_iter_data sta_iter_data = {
.vif = vif,
.support = support,
};
struct ieee80211_bss_conf *link_conf;
unsigned int link_id;
switch (vif->type) {
case NL80211_IFTYPE_MONITOR:
/* for sniffer, set to HW capabilities */
support->he_support = 1;
support->eht_support = mld->trans->cfg->eht_supported;
break;
case NL80211_IFTYPE_AP:
/* for AP set according to the link configs */
for_each_vif_active_link(vif, link_conf, link_id) {
support->he_ap_support |= link_conf->he_support;
support->eht_support |= link_conf->eht_support;
}
break;
default:
/*
* If we have MLO enabled, then the firmware needs to enable
* address translation for the station(s) we add. That depends
* on having EHT enabled in firmware, which in turn depends on
* mac80211 in the iteration below.
* However, mac80211 doesn't enable capabilities on the AP STA
* until it has parsed the association response successfully,
* so set EHT (and HE as a pre-requisite for EHT) when the vif
* is an MLD.
*/
if (ieee80211_vif_is_mld(vif)) {
support->he_support = 1;
support->eht_support = 1;
}
ieee80211_iterate_stations_mtx(mld->hw,
iwl_mld_mac_wifi_gen_sta_iter,
&sta_iter_data);
break;
}
}
/* fill the common part for all interface types */
@@ -128,8 +189,6 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld,
u32 action)
{
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
struct ieee80211_bss_conf *link_conf;
unsigned int link_id;
lockdep_assert_wiphy(mld->wiphy);
@@ -147,29 +206,7 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld,
cmd->nic_not_ack_enabled =
cpu_to_le32(!iwl_mld_is_nic_ack_enabled(mld, vif));
/* If we have MLO enabled, then the firmware needs to enable
* address translation for the station(s) we add. That depends
* on having EHT enabled in firmware, which in turn depends on
* mac80211 in the code below.
* However, mac80211 doesn't enable HE/EHT until it has parsed
* the association response successfully, so just skip all that
* and enable both when we have MLO.
*/
if (ieee80211_vif_is_mld(vif)) {
iwl_mld_set_he_support(mld, vif, cmd);
cmd->wifi_gen.eht_support = 1;
return;
}
for_each_vif_active_link(vif, link_conf, link_id) {
if (!link_conf->he_support)
continue;
iwl_mld_set_he_support(mld, vif, cmd);
/* EHT, if supported, was already set above */
break;
}
iwl_mld_set_wifi_gen(mld, vif, &cmd->wifi_gen);
}
static void iwl_mld_fill_mac_cmd_sta(struct iwl_mld *mld,

View File

@@ -1761,6 +1761,16 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld,
if (vif->type == NL80211_IFTYPE_STATION)
iwl_mld_link_set_2mhz_block(mld, vif, sta);
if (sta->tdls) {
/*
* update MAC since wifi generation flags may change,
* we also update MAC on association to the AP via the
* vif assoc change
*/
iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY);
}
/* Now the link_sta's capabilities are set, update the FW */
iwl_mld_config_tlc(mld, vif, sta);
@@ -1873,6 +1883,15 @@ static int iwl_mld_move_sta_state_down(struct iwl_mld *mld,
/* just removed last TDLS STA, so enable PM */
iwl_mld_update_mac_power(mld, vif, false);
}
if (sta->tdls) {
/*
* update MAC since wifi generation flags may change,
* we also update MAC on disassociation to the AP via
* the vif assoc change
*/
iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY);
}
} else {
return -EINVAL;
}

View File

@@ -171,6 +171,7 @@ static const struct iwl_hcmd_names iwl_mld_legacy_names[] = {
HCMD_NAME(MISSED_BEACONS_NOTIFICATION),
HCMD_NAME(MAC_PM_POWER_TABLE),
HCMD_NAME(MFUART_LOAD_NOTIFICATION),
HCMD_NAME(SCAN_START_NOTIFICATION_UMAC),
HCMD_NAME(RSS_CONFIG_CMD),
HCMD_NAME(SCAN_ITERATION_COMPLETE_UMAC),
HCMD_NAME(REPLY_RX_MPDU_CMD),

View File

@@ -739,7 +739,7 @@ iwl_mld_set_link_sel_data(struct iwl_mld *mld,
/* Ignore any BSS that was not seen in the last MLO scan */
if (ktime_before(link_conf->bss->ts_boottime,
mld->scan.last_mlo_scan_time))
mld->scan.last_mlo_scan_start_time))
continue;
data[n_data].link_id = link_id;
@@ -945,7 +945,7 @@ static void _iwl_mld_select_links(struct iwl_mld *mld,
if (!mld_vif->authorized || hweight16(usable_links) <= 1)
return;
if (WARN(ktime_before(mld->scan.last_mlo_scan_time,
if (WARN(ktime_before(mld->scan.last_mlo_scan_start_time,
ktime_sub_ns(ktime_get_boottime_ns(),
5ULL * NSEC_PER_SEC)),
"Last MLO scan was too long ago, can't select links\n"))

View File

@@ -287,6 +287,8 @@ static void iwl_mld_handle_beacon_notification(struct iwl_mld *mld,
* at least enough bytes to cover the structure listed in the CMD_VER_ENTRY.
*/
CMD_VERSIONS(scan_start_notif,
CMD_VER_ENTRY(1, iwl_umac_scan_start))
CMD_VERSIONS(scan_complete_notif,
CMD_VER_ENTRY(1, iwl_umac_scan_complete))
CMD_VERSIONS(scan_iter_complete_notif,
@@ -360,6 +362,7 @@ DEFINE_SIMPLE_CANCELLATION(datapath_monitor, iwl_datapath_monitor_notif,
link_id)
DEFINE_SIMPLE_CANCELLATION(roc, iwl_roc_notif, activity)
DEFINE_SIMPLE_CANCELLATION(scan_complete, iwl_umac_scan_complete, uid)
DEFINE_SIMPLE_CANCELLATION(scan_start, iwl_umac_scan_start, uid)
DEFINE_SIMPLE_CANCELLATION(probe_resp_data, iwl_probe_resp_data_notif,
mac_id)
DEFINE_SIMPLE_CANCELLATION(uapsd_misbehaving_ap, iwl_uapsd_misbehaving_ap_notif,
@@ -402,6 +405,8 @@ const struct iwl_rx_handler iwl_mld_rx_handlers[] = {
RX_HANDLER_SYNC)
RX_HANDLER_NO_OBJECT(LEGACY_GROUP, BA_NOTIF, compressed_ba_notif,
RX_HANDLER_SYNC)
RX_HANDLER_OF_SCAN(LEGACY_GROUP, SCAN_START_NOTIFICATION_UMAC,
scan_start_notif)
RX_HANDLER_OF_SCAN(LEGACY_GROUP, SCAN_COMPLETE_UMAC,
scan_complete_notif)
RX_HANDLER_NO_OBJECT(LEGACY_GROUP, SCAN_ITERATION_COMPLETE_UMAC,

View File

@@ -473,6 +473,9 @@ iwl_mld_scan_get_cmd_gen_flags(struct iwl_mld *mld,
params->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ)
flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_TRIGGER_UHB_SCAN;
if (scan_status == IWL_MLD_SCAN_INT_MLO)
flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_NTF_START;
if (params->enable_6ghz_passive)
flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_6GHZ_PASSIVE_SCAN;
@@ -1817,9 +1820,6 @@ static void iwl_mld_int_mlo_scan_start(struct iwl_mld *mld,
ret = _iwl_mld_single_scan_start(mld, vif, req, &ies,
IWL_MLD_SCAN_INT_MLO);
if (!ret)
mld->scan.last_mlo_scan_time = ktime_get_boottime_ns();
IWL_DEBUG_SCAN(mld, "Internal MLO scan: ret=%d\n", ret);
}
@@ -1904,6 +1904,30 @@ void iwl_mld_handle_match_found_notif(struct iwl_mld *mld,
ieee80211_sched_scan_results(mld->hw);
}
void iwl_mld_handle_scan_start_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt)
{
struct iwl_umac_scan_complete *notif = (void *)pkt->data;
u32 uid = le32_to_cpu(notif->uid);
if (IWL_FW_CHECK(mld, uid >= ARRAY_SIZE(mld->scan.uid_status),
"FW reports out-of-range scan UID %d\n", uid))
return;
if (IWL_FW_CHECK(mld, !(mld->scan.uid_status[uid] & mld->scan.status),
"FW reports scan UID %d we didn't trigger\n", uid))
return;
IWL_DEBUG_SCAN(mld, "Scan started: uid=%u type=%u\n", uid,
mld->scan.uid_status[uid]);
if (IWL_FW_CHECK(mld, mld->scan.uid_status[uid] != IWL_MLD_SCAN_INT_MLO,
"FW reports scan start notification %d we didn't trigger\n",
mld->scan.uid_status[uid]))
return;
mld->scan.last_mlo_scan_start_time = ktime_get_boottime_ns();
}
void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt)
{

View File

@@ -27,6 +27,9 @@ int iwl_mld_sched_scan_start(struct iwl_mld *mld,
void iwl_mld_handle_match_found_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt);
void iwl_mld_handle_scan_start_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt);
void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt);
@@ -114,8 +117,8 @@ enum iwl_mld_traffic_load {
* in jiffies.
* @last_start_time_jiffies: stores the last start time in jiffies
* (interface up/reset/resume).
* @last_mlo_scan_time: start time of the last MLO scan in nanoseconds since
* boot.
* @last_mlo_scan_start_time: start time of the last MLO scan in nanoseconds
* since boot.
*/
struct iwl_mld_scan {
/* Add here fields that need clean up on restart */
@@ -136,7 +139,7 @@ struct iwl_mld_scan {
void *cmd;
unsigned long last_6ghz_passive_jiffies;
unsigned long last_start_time_jiffies;
u64 last_mlo_scan_time;
u64 last_mlo_scan_start_time;
};
/**

View File

@@ -2807,7 +2807,7 @@ static void iwl_mvm_nd_match_info_handler(struct iwl_mvm *mvm,
if (IS_ERR_OR_NULL(vif))
return;
if (len < sizeof(struct iwl_scan_offload_match_info)) {
if (len < sizeof(struct iwl_scan_offload_match_info) + matches_len) {
IWL_ERR(mvm, "Invalid scan match info notification\n");
return;
}

View File

@@ -470,7 +470,8 @@ static void iwl_mvm_uats_init(struct iwl_mvm *mvm)
.dataflags[0] = IWL_HCMD_DFL_NOCOPY,
};
if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210 ||
!mvm->trans->cfg->uhb_supported) {
IWL_DEBUG_RADIO(mvm, "UATS feature is not supported\n");
return;
}

View File

@@ -163,7 +163,7 @@ int wilc_scan(struct wilc_vif *vif, u8 scan_source,
u32 index = 0;
u32 i, scan_timeout;
u8 *buffer;
u8 valuesize = 0;
u32 valuesize = 0;
u8 *search_ssid_vals = NULL;
const u8 ch_list_len = request->n_channels;
struct host_if_drv *hif_drv = vif->hif_drv;

View File

@@ -402,12 +402,14 @@ static void wl1251_tx_packet_cb(struct wl1251 *wl,
int hdrlen;
u8 *frame;
skb = wl->tx_frames[result->id];
if (skb == NULL) {
wl1251_error("SKB for packet %d is NULL", result->id);
if (unlikely(result->id >= ARRAY_SIZE(wl->tx_frames) ||
wl->tx_frames[result->id] == NULL)) {
wl1251_error("invalid packet id %u", result->id);
return;
}
skb = wl->tx_frames[result->id];
info = IEEE80211_SKB_CB(skb);
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) &&

View File

@@ -557,7 +557,6 @@ static int virt_wifi_newlink(struct net_device *dev,
eth_hw_addr_inherit(dev, priv->lowerdev);
netif_stacked_transfer_operstate(priv->lowerdev, dev);
SET_NETDEV_DEV(dev, &priv->lowerdev->dev);
dev->ieee80211_ptr = kzalloc_obj(*dev->ieee80211_ptr);
if (!dev->ieee80211_ptr) {