AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/15 18:14 flow patch-triage
5m Args:
null

Results:
{
  "EnableConfigs": [
    "CFG80211",
    "MAC80211",
    "MAC80211_HWSIM"
  ],
  "FocusSymbols": [
    "nl80211_tx_mgmt",
    "nl80211_send_mgmt",
    "ieee80211_mgmt_tx",
    "ieee80211_tx_skb_tid",
    "__ieee80211_tx_skb_tid_band",
    "ieee80211_rx_list",
    "ieee80211_rx_napi",
    "__ieee80211_rx_handle_packet",
    "__ieee80211_rx_handle_8023",
    "ieee80211_rx_handlers",
    "ieee80211_rx_data_set_link_sta"
  ],
  "KMSANReasoning": "The patch series updates IEEE 802.11 Multi-Link Operation (MLO) handling across wireless drivers (ath11k, ath12k, iwlwifi, mt76, rtw89, mac80211_hwsim) and mac80211/cfg80211. Specifically:\n1. It replaces passing `struct ieee80211_sta *pubsta` to `ieee80211_rx_napi`/`ieee80211_rx_list` with `struct ieee80211_link_sta *link_pubsta`.\n2. It removes the bitfield `link_valid:1` from `struct ieee80211_rx_status`, keeping `link_id:4`, and determines link IDs internally based on frequency or resolved link stations.\n3. It introduces the netlink attribute `NL80211_ATTR_FRAME_NO_STA` and corresponding `bool no_sta` flags in `struct cfg80211_mgmt_tx_params` and `struct cfg80211_rx_info`.\n4. It refactors frame resolution in `__ieee80211_rx_handle_packet` and link handling across A-MPDU reorder and fast-rx.\n\nEvaluation regarding KMSAN vs. KASAN:\n- No uninitialized memory leaks to userspace: `NL80211_ATTR_FRAME_NO_STA` is a flag attribute (`NLA_FLAG`) added via `nla_put_flag()` which carries zero payload bytes. The instances of `struct cfg80211_rx_info` populated in mac80211 and cfg80211 use designated C initializers (`struct cfg80211_rx_info info = { ... }`), ensuring all fields and padding are zero-initialized.\n- No uninitialized variable or memory reads: `struct ieee80211_rx_data` is explicitly zeroed via `memset(\u0026rx, 0, sizeof(rx))` in `__ieee80211_rx_handle_8023` and `__ieee80211_rx_handle_packet`. In skb receive paths, `skb-\u003ecb` / `rx_status` is either zeroed upon allocation or explicitly cleared via `memset(status, 0, ...)` by drivers, and `status-\u003elink_id` is assigned before handlers run (`status-\u003elink_id = rx-\u003elink_id \u003c 0 ? 0 : rx-\u003elink_id`).\n- Potential issues introduced by this patch involve pointer resolution logic, RCU dereferencing (`rcu_dereference(sta-\u003elink[...])`), array bounds, or NULL pointer dereferencing, all of which fall squarely within standard bug detectors (KASAN, UBSAN, NULL checks, LOCKDEP).\n\nTherefore, there is no risk of uninitialized memory usage or kernel information leaks, and a dedicated KMSAN fuzzing session is not warranted.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch series refactors Multi-Link Operation (MLO) frame transmission and reception in the core mac80211 networking stack and the nl80211 netlink interface, as well as the virtual mac80211_hwsim driver. Key modifications include removing link_valid from rx_status, passing link_sta instead of sta to RX functions, restructuring station and link lookups in __ieee80211_rx_handle_packet and __ieee80211_rx_handle_8023, updating cross-link A-MPDU reordering in ieee80211_rx_handlers, and introducing NL80211_ATTR_FRAME_NO_STA handling in nl80211_tx_mgmt. These code paths are fully reachable in virtualized QEMU environments using mac80211_hwsim and unprivileged user namespaces via nl80211 netlink commands.",
  "WorthFuzzing": true
}

1/1 2026/09/15 18:14 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 05665aae504027f5bfd53117ebba1243a0c0f83c\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Tue Sep 15 18:14:34 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c\nindex 33425707c0842..fbb3d18f55ddd 100644\n--- a/drivers/net/wireless/ath/ath11k/dp_rx.c\n+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c\n@@ -2500,7 +2500,7 @@ static void ath11k_dp_rx_deliver_msdu(struct ath11k *ar, struct napi_struct *nap\n \t    !(is_mcbc \u0026\u0026 rx_status-\u003eflag \u0026 RX_FLAG_DECRYPTED))\n \t\trx_status-\u003eflag |= RX_FLAG_8023;\n \n-\tieee80211_rx_napi(ar-\u003ehw, pubsta, msdu, napi);\n+\tieee80211_rx_napi(ar-\u003ehw, pubsta ? \u0026pubsta-\u003edeflink : NULL, msdu, napi);\n }\n \n static bool ath11k_dp_rx_check_nwifi_hdr_len_valid(struct ath11k_base *ab,\ndiff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c\nindex 7d5be77b081f8..1faf1896d0ab1 100644\n--- a/drivers/net/wireless/ath/ath12k/dp_mon.c\n+++ b/drivers/net/wireless/ath/ath12k/dp_mon.c\n@@ -503,8 +503,6 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,\n \tstruct ieee80211_rx_status *rx_status;\n \tstruct ieee80211_radiotap_he *he = NULL;\n \n-\tstatus-\u003elink_valid = 0;\n-\n \tif ((status-\u003eencoding == RX_ENC_HE) \u0026\u0026 !(status-\u003eflag \u0026 RX_FLAG_RADIOTAP_HE) \u0026\u0026\n \t    !(status-\u003eflag \u0026 RX_FLAG_SKIP_MONITOR)) {\n \t\the = skb_push(msdu, sizeof(known));\ndiff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c\nindex 8fa0e90b45316..41ac0a4072aeb 100644\n--- a/drivers/net/wireless/ath/ath12k/dp_rx.c\n+++ b/drivers/net/wireless/ath/ath12k/dp_rx.c\n@@ -1375,6 +1375,7 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc\n \tstruct ath12k_dp *dp = dp_pdev-\u003edp;\n \tstruct ieee80211_rx_status *rx_status;\n \tstruct ieee80211_sta *pubsta;\n+\tstruct ieee80211_link_sta *link_pubsta = NULL;\n \tstruct ath12k_dp_peer *peer;\n \tstruct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);\n \tstruct ieee80211_rx_status *status = rx_info-\u003erx_status;\n@@ -1384,9 +1385,19 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc\n \n \tpubsta = peer ? peer-\u003esta : NULL;\n \n-\tstatus-\u003elink_valid = 0;\n-\tif (pubsta \u0026\u0026 pubsta-\u003evalid_links)\n-\t\tath12k_hw_set_rx_link_id(dp-\u003ehw_params, peer, rxcb, status);\n+\tif (pubsta) {\n+\t\tif (pubsta-\u003evalid_links) {\n+\t\t\tint link_id =\n+\t\t\t\tath12k_hw_get_rx_link_id(dp-\u003ehw_params, peer,\n+\t\t\t\t\t\t\t rxcb, status);\n+\n+\t\t\tif (link_id \u003e= 0)\n+\t\t\t\tlink_pubsta =\n+\t\t\t\t\trcu_dereference(pubsta-\u003elink[link_id]);\n+\t\t} else {\n+\t\t\tlink_pubsta = \u0026pubsta-\u003edeflink;\n+\t\t}\n+\t}\n \n \tath12k_dbg(dp-\u003eab, ATH12K_DBG_DATA,\n \t\t   \"rx skb %p len %u peer %pM %d %s sn %u %s%s%s%s%s%s%s%s%s%s rate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\\n\",\n@@ -1422,7 +1433,8 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc\n \n \t/* TODO: trace rx packet */\n \n-\tieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), pubsta, msdu, napi);\n+\tieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), link_pubsta, msdu,\n+\t\t\t  napi);\n }\n EXPORT_SYMBOL(ath12k_dp_rx_deliver_msdu);\n \ndiff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h\nindex 011bb22e21c7b..e790de5737db4 100644\n--- a/drivers/net/wireless/ath/ath12k/hw.h\n+++ b/drivers/net/wireless/ath/ath12k/hw.h\n@@ -249,9 +249,9 @@ struct ath12k_hw_ops {\n \tbool (*dp_srng_is_tx_comp_ring)(int ring_num);\n \tbool (*is_frame_link_agnostic)(struct ath12k_link_vif *arvif,\n \t\t\t\t       struct ieee80211_mgmt *mgmt);\n-\tvoid (*set_rx_link_id)(struct ath12k_dp_peer *dp_peer,\n-\t\t\t       struct ath12k_skb_rxcb *rxcb,\n-\t\t\t       struct ieee80211_rx_status *status);\n+\tint (*get_rx_link_id)(struct ath12k_dp_peer *dp_peer,\n+\t\t\t      struct ath12k_skb_rxcb *rxcb,\n+\t\t\t      struct ieee80211_rx_status *status);\n };\n \n static inline\n@@ -282,13 +282,15 @@ static inline int ath12k_hw_mac_id_to_srng_id(const struct ath12k_hw_params *hw,\n \treturn 0;\n }\n \n-static inline void ath12k_hw_set_rx_link_id(const struct ath12k_hw_params *hw,\n-\t\t\t\t\t    struct ath12k_dp_peer *dp_peer,\n-\t\t\t\t\t    struct ath12k_skb_rxcb *rxcb,\n-\t\t\t\t\t    struct ieee80211_rx_status *status)\n+static inline int ath12k_hw_get_rx_link_id(const struct ath12k_hw_params *hw,\n+\t\t\t\t\t   struct ath12k_dp_peer *dp_peer,\n+\t\t\t\t\t   struct ath12k_skb_rxcb *rxcb,\n+\t\t\t\t\t   struct ieee80211_rx_status *status)\n {\n-\tif (hw-\u003ehw_ops-\u003eset_rx_link_id)\n-\t\thw-\u003ehw_ops-\u003eset_rx_link_id(dp_peer, rxcb, status);\n+\tif (hw-\u003ehw_ops-\u003eget_rx_link_id)\n+\t\treturn hw-\u003ehw_ops-\u003eget_rx_link_id(dp_peer, rxcb, status);\n+\n+\treturn -1;\n }\n \n struct ath12k_fw_ie {\ndiff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c\nindex 95d87dd67872f..8b722ee0ad8fa 100644\n--- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c\n+++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c\n@@ -2317,17 +2317,16 @@ ath12k_wifi7_dp_rxdesc_mpdu_valid(struct ath12k_base *ab,\n \treturn tlv_tag == HAL_RX_MPDU_START;\n }\n \n-void\n-ath12k_wifi7_dp_rx_set_link_id_qcn9274(struct ath12k_dp_peer *dp_peer,\n+int\n+ath12k_wifi7_dp_rx_get_link_id_qcn9274(struct ath12k_dp_peer *dp_peer,\n \t\t\t\t       struct ath12k_skb_rxcb *rxcb,\n \t\t\t\t       struct ieee80211_rx_status *status)\n {\n-\tstatus-\u003elink_valid = 1;\n-\tstatus-\u003elink_id = dp_peer-\u003ehw_links[rxcb-\u003ehw_link_id];\n+\treturn dp_peer-\u003ehw_links[rxcb-\u003ehw_link_id];\n }\n \n-void\n-ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,\n+int\n+ath12k_wifi7_dp_rx_get_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,\n \t\t\t\t       struct ath12k_skb_rxcb *rxcb,\n \t\t\t\t       struct ieee80211_rx_status *status)\n {\n@@ -2341,10 +2340,9 @@ ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,\n \tlinks_map = READ_ONCE(dp_peer-\u003elink_peers_map);\n \tfor_each_set_bit(i, \u0026links_map, ATH12K_NUM_MAX_LINKS) {\n \t\tlink_peer = rcu_dereference(dp_peer-\u003elink_peers[i]);\n-\t\tif (link_peer \u0026\u0026 link_peer-\u003epeer_id == rxcb-\u003epeer_id) {\n-\t\t\tstatus-\u003elink_valid = 1;\n-\t\t\tstatus-\u003elink_id = link_peer-\u003elink_id;\n-\t\t\treturn;\n-\t\t}\n+\t\tif (link_peer \u0026\u0026 link_peer-\u003epeer_id == rxcb-\u003epeer_id)\n+\t\t\treturn link_peer-\u003elink_id;\n \t}\n+\n+\treturn -1;\n }\ndiff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h\nindex 1d3a4788a2dd9..d68489ff7b037 100644\n--- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h\n+++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h\n@@ -57,10 +57,10 @@ ath12k_wifi7_dp_rxdesc_mpdu_valid(struct ath12k_base *ab,\n \t\t\t\t  struct hal_rx_desc *rx_desc);\n int ath12k_wifi7_dp_rx_tid_delete_handler(struct ath12k_base *ab,\n \t\t\t\t\t  struct ath12k_dp_rx_tid_rxq *rx_tid);\n-void ath12k_wifi7_dp_rx_set_link_id_qcn9274(struct ath12k_dp_peer *dp_peer,\n-\t\t\t\t\t    struct ath12k_skb_rxcb *rxcb,\n-\t\t\t\t\t    struct ieee80211_rx_status *status);\n-void ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,\n-\t\t\t\t\t    struct ath12k_skb_rxcb *rxcb,\n-\t\t\t\t\t    struct ieee80211_rx_status *status);\n+int ath12k_wifi7_dp_rx_get_link_id_qcn9274(struct ath12k_dp_peer *dp_peer,\n+\t\t\t\t\t   struct ath12k_skb_rxcb *rxcb,\n+\t\t\t\t\t   struct ieee80211_rx_status *status);\n+int ath12k_wifi7_dp_rx_get_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,\n+\t\t\t\t\t   struct ath12k_skb_rxcb *rxcb,\n+\t\t\t\t\t   struct ieee80211_rx_status *status);\n #endif\ndiff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c\nindex 7b86f324b2e68..4ded807763b61 100644\n--- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c\n+++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c\n@@ -158,7 +158,7 @@ static const struct ath12k_hw_ops qcn9274_ops = {\n \t.get_ring_selector = ath12k_wifi7_hw_get_ring_selector_qcn9274,\n \t.dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_qcn9274,\n \t.is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_qcn9274,\n-\t.set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_qcn9274,\n+\t.get_rx_link_id = ath12k_wifi7_dp_rx_get_link_id_qcn9274,\n };\n \n static const struct ath12k_hw_ops wcn7850_ops = {\n@@ -169,7 +169,7 @@ static const struct ath12k_hw_ops wcn7850_ops = {\n \t.get_ring_selector = ath12k_wifi7_hw_get_ring_selector_wcn7850,\n \t.dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_wcn7850,\n \t.is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_wcn7850,\n-\t.set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_wcn7850,\n+\t.get_rx_link_id = ath12k_wifi7_dp_rx_get_link_id_wcn7850,\n };\n \n static const struct ath12k_hw_ops qcc2072_ops = {\n@@ -180,7 +180,7 @@ static const struct ath12k_hw_ops qcc2072_ops = {\n \t.get_ring_selector = ath12k_wifi7_hw_get_ring_selector_wcn7850,\n \t.dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_wcn7850,\n \t.is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_wcn7850,\n-\t.set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_wcn7850,\n+\t.get_rx_link_id = ath12k_wifi7_dp_rx_get_link_id_wcn7850,\n };\n \n #define ATH12K_TX_RING_MASK_0 0x1\ndiff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c\nindex d5160af60e00d..7e3655966f02d 100644\n--- a/drivers/net/wireless/ath/ath12k/wmi.c\n+++ b/drivers/net/wireless/ath/ath12k/wmi.c\n@@ -7271,7 +7271,6 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb)\n \tstruct ath12k_wmi_mgmt_rx_arg rx_ev = {};\n \tstruct ath12k *ar;\n \tstruct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);\n-\tstruct ieee80211_sta *pubsta = NULL;\n \tstruct ath12k_dp_link_peer *peer;\n \tstruct ieee80211_hdr *hdr;\n \tbool is_4addr_null_pkt;\n@@ -7373,11 +7372,6 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb)\n \t\t\tdev_kfree_skb(skb);\n \t\t\tgoto exit;\n \t\t}\n-\t\tpubsta = peer-\u003esta;\n-\t\tif (pubsta \u0026\u0026 pubsta-\u003evalid_links) {\n-\t\t\tstatus-\u003elink_valid = 1;\n-\t\t\tstatus-\u003elink_id = peer-\u003elink_id;\n-\t\t}\n \t\tspin_unlock_bh(\u0026dp-\u003edp_lock);\n \t\tgoto send_rx;\n \t}\ndiff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/agg.c\nindex 96102d2af2cf9..c45c47337509e 100644\n--- a/drivers/net/wireless/intel/iwlwifi/mld/agg.c\n+++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c\n@@ -7,7 +7,8 @@\n #include \"hcmd.h\"\n \n static void\n-iwl_mld_reorder_release_frames(struct iwl_mld *mld, struct ieee80211_sta *sta,\n+iwl_mld_reorder_release_frames(struct iwl_mld *mld,\n+\t\t\t       struct ieee80211_link_sta *link_sta,\n \t\t\t       struct napi_struct *napi,\n \t\t\t       struct iwl_mld_baid_data *baid_data,\n \t\t\t       struct iwl_mld_reorder_buffer *reorder_buf,\n@@ -32,7 +33,7 @@ iwl_mld_reorder_release_frames(struct iwl_mld *mld, struct ieee80211_sta *sta,\n \t\twhile ((skb = __skb_dequeue(skb_list))) {\n \t\t\tiwl_mld_pass_packet_to_mac80211(mld, napi, skb,\n \t\t\t\t\t\t\treorder_buf-\u003equeue,\n-\t\t\t\t\t\t\tsta);\n+\t\t\t\t\t\t\tlink_sta);\n \t\t\treorder_buf-\u003enum_stored--;\n \t\t}\n \t}\n@@ -74,7 +75,7 @@ static void iwl_mld_release_frames_from_notif(struct iwl_mld *mld,\n \n \treorder_buf = \u0026ba_data-\u003ereorder_buf[queue];\n \n-\tiwl_mld_reorder_release_frames(mld, link_sta-\u003esta, napi, ba_data,\n+\tiwl_mld_reorder_release_frames(mld, link_sta, napi, ba_data,\n \t\t\t\t       reorder_buf, nssn);\n out_unlock:\n \trcu_read_unlock();\n@@ -180,7 +181,7 @@ void iwl_mld_del_ba(struct iwl_mld *mld, int queue,\n \treorder_buf = \u0026ba_data-\u003ereorder_buf[queue];\n \n \t/* release all frames that are in the reorder buffer to the stack */\n-\tiwl_mld_reorder_release_frames(mld, link_sta-\u003esta, NULL,\n+\tiwl_mld_reorder_release_frames(mld, link_sta, NULL,\n \t\t\t\t       ba_data, reorder_buf,\n \t\t\t\t       ieee80211_sn_add(reorder_buf-\u003ehead_sn,\n \t\t\t\t\t\t\tba_data-\u003ebuf_size));\n@@ -193,7 +194,7 @@ void iwl_mld_del_ba(struct iwl_mld *mld, int queue,\n  */\n enum iwl_mld_reorder_result\n iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,\n-\t\tint queue, struct ieee80211_sta *sta,\n+\t\tint queue, struct ieee80211_link_sta *link_sta,\n \t\tstruct sk_buff *skb, struct iwl_rx_mpdu_desc *desc)\n {\n \tstruct ieee80211_hdr *hdr = (void *)skb_mac_header(skb);\n@@ -228,12 +229,12 @@ iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,\n \t\treturn IWL_MLD_PASS_SKB;\n \n \t/* no sta yet */\n-\tif (IWL_FW_CHECK(mld, !sta,\n+\tif (IWL_FW_CHECK(mld, !link_sta,\n \t\t\t \"Got valid BAID without a valid station assigned - %d\\n\",\n \t\t\t baid))\n \t\treturn IWL_MLD_PASS_SKB;\n \n-\tmld_sta = iwl_mld_sta_from_mac80211(sta);\n+\tmld_sta = iwl_mld_sta_from_mac80211(link_sta-\u003esta);\n \n \t/* not a data packet */\n \tif (!ieee80211_is_data_qos(hdr-\u003eframe_control) ||\n@@ -324,7 +325,7 @@ iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,\n \t * will be released when the frame release notification arrives.\n \t */\n \tif (!amsdu || last_subframe)\n-\t\tiwl_mld_reorder_release_frames(mld, sta, napi, baid_data,\n+\t\tiwl_mld_reorder_release_frames(mld, link_sta, napi, baid_data,\n \t\t\t\t\t       buffer, nssn);\n \telse if (buffer-\u003enum_stored == 1)\n \t\tbuffer-\u003ehead_sn = nssn;\ndiff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.h b/drivers/net/wireless/intel/iwlwifi/mld/agg.h\nindex 651c80d1c7cda..c6cd5fa219bed 100644\n--- a/drivers/net/wireless/intel/iwlwifi/mld/agg.h\n+++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.h\n@@ -1,6 +1,6 @@\n /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */\n /*\n- * Copyright (C) 2024 Intel Corporation\n+ * Copyright (C) 2024, 2026 Intel Corporation\n  */\n #ifndef __iwl_agg_h__\n #define __iwl_agg_h__\n@@ -106,7 +106,7 @@ int iwl_mld_ampdu_rx_stop(struct iwl_mld *mld, struct ieee80211_sta *sta,\n \n enum iwl_mld_reorder_result\n iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,\n-\t\tint queue, struct ieee80211_sta *sta,\n+\t\tint queue, struct ieee80211_link_sta *link_sta,\n \t\tstruct sk_buff *skb, struct iwl_rx_mpdu_desc *desc);\n \n void iwl_mld_handle_frame_release_notif(struct iwl_mld *mld,\ndiff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c\nindex 269439d789f46..02bdf6ccbcdc9 100644\n--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c\n+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c\n@@ -47,7 +47,8 @@ iwl_mld_fill_phy_data_from_mpdu(struct iwl_mld *mld,\n }\n \n static inline int iwl_mld_check_pn(struct iwl_mld *mld, struct sk_buff *skb,\n-\t\t\t\t   int queue, struct ieee80211_sta *sta)\n+\t\t\t\t   int queue,\n+\t\t\t\t   struct ieee80211_link_sta *link_sta)\n {\n \tstruct ieee80211_hdr *hdr = (void *)skb_mac_header(skb);\n \tstruct ieee80211_rx_status *stats = IEEE80211_SKB_RXCB(skb);\n@@ -71,13 +72,13 @@ static inline int iwl_mld_check_pn(struct iwl_mld *mld, struct sk_buff *skb,\n \t\treturn 0;\n \n \t/* if we are here - this for sure is either CCMP or GCMP */\n-\tif (!sta) {\n+\tif (!link_sta) {\n \t\tIWL_DEBUG_DROP(mld,\n \t\t\t       \"expected hw-decrypted unicast frame for station\\n\");\n \t\treturn -1;\n \t}\n \n-\tmld_sta = iwl_mld_sta_from_mac80211(sta);\n+\tmld_sta = iwl_mld_sta_from_mac80211(link_sta-\u003esta);\n \n \textiv = (u8 *)hdr + ieee80211_hdrlen(hdr-\u003eframe_control);\n \tkeyidx = extiv[3] \u003e\u003e 6;\n@@ -119,17 +120,17 @@ static inline int iwl_mld_check_pn(struct iwl_mld *mld, struct sk_buff *skb,\n void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,\n \t\t\t\t     struct napi_struct *napi,\n \t\t\t\t     struct sk_buff *skb, int queue,\n-\t\t\t\t     struct ieee80211_sta *sta)\n+\t\t\t\t     struct ieee80211_link_sta *link_sta)\n {\n \tKUNIT_STATIC_STUB_REDIRECT(iwl_mld_pass_packet_to_mac80211,\n-\t\t\t\t   mld, napi, skb, queue, sta);\n+\t\t\t\t   mld, napi, skb, queue, link_sta);\n \n-\tif (unlikely(iwl_mld_check_pn(mld, skb, queue, sta))) {\n+\tif (unlikely(iwl_mld_check_pn(mld, skb, queue, link_sta))) {\n \t\tkfree_skb(skb);\n \t\treturn;\n \t}\n \n-\tieee80211_rx_napi(mld-\u003ehw, sta, skb, napi);\n+\tieee80211_rx_napi(mld-\u003ehw, link_sta, skb, napi);\n }\n EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_pass_packet_to_mac80211);\n \n@@ -1728,7 +1729,7 @@ static void iwl_mld_update_last_rx_timestamp(struct iwl_mld *mld, u8 baid)\n  * Sets *drop to true if the packet should be dropped.\n  * Returns the station if found, or NULL otherwise.\n  */\n-static struct ieee80211_sta *\n+static struct ieee80211_link_sta *\n iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr,\n \t\t    struct sk_buff *skb,\n \t\t    const struct iwl_rx_mpdu_desc *mpdu_desc,\n@@ -1764,11 +1765,6 @@ iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr,\n \n \trx_status = IEEE80211_SKB_RXCB(skb);\n \n-\tif (link_sta \u0026\u0026 sta-\u003evalid_links) {\n-\t\trx_status-\u003elink_valid = true;\n-\t\trx_status-\u003elink_id = link_sta-\u003elink_id;\n-\t}\n-\n \t/* fill checksum */\n \tif (ieee80211_is_data(hdr-\u003eframe_control) \u0026\u0026\n \t    pkt-\u003elen_n_flags \u0026 cpu_to_le32(FH_RSCSR_RPA_EN)) {\n@@ -1803,10 +1799,10 @@ iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr,\n \t\t\t\t\t\t\t    queue);\n \t}\n \n-\treturn sta;\n+\treturn link_sta;\n }\n \n-static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,\n+static int iwl_mld_rx_mgmt_prot(struct ieee80211_link_sta *link_sta,\n \t\t\t\tstruct ieee80211_hdr *hdr,\n \t\t\t\tstruct ieee80211_rx_status *rx_status,\n \t\t\t\tu32 mpdu_status,\n@@ -1820,7 +1816,6 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,\n \tstruct ieee80211_key_conf *key;\n \tconst u8 *frame = (void *)hdr;\n \tconst u8 *mmie;\n-\tu8 link_id;\n \n \tif ((mpdu_status \u0026 IWL_RX_MPDU_STATUS_SEC_MASK) ==\n \t     IWL_RX_MPDU_STATUS_SEC_NONE)\n@@ -1836,10 +1831,10 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,\n \tif (!ieee80211_is_beacon(hdr-\u003eframe_control))\n \t\treturn 0;\n \n-\tif (!sta)\n+\tif (!link_sta)\n \t\treturn -1;\n \n-\tmld_sta = iwl_mld_sta_from_mac80211(sta);\n+\tmld_sta = iwl_mld_sta_from_mac80211(link_sta-\u003esta);\n \tmld_vif = iwl_mld_vif_from_mac80211(mld_sta-\u003evif);\n \n \t/* key mismatch - will also report !MIC_OK but we shouldn't count it */\n@@ -1853,8 +1848,7 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,\n \t\treturn 0;\n \t}\n \n-\tlink_id = rx_status-\u003elink_valid ? rx_status-\u003elink_id : 0;\n-\tlink = rcu_dereference(mld_vif-\u003elink[link_id]);\n+\tlink = rcu_dereference(mld_vif-\u003elink[link_sta-\u003elink_id]);\n \tif (WARN_ON_ONCE(!link))\n \t\treturn -1;\n \n@@ -1905,7 +1899,7 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,\n }\n \n static int iwl_mld_rx_crypto(struct iwl_mld *mld,\n-\t\t\t     struct ieee80211_sta *sta,\n+\t\t\t     struct ieee80211_link_sta *link_sta,\n \t\t\t     struct ieee80211_hdr *hdr,\n \t\t\t     struct ieee80211_rx_status *rx_status,\n \t\t\t     struct iwl_rx_mpdu_desc *desc, int queue,\n@@ -1915,7 +1909,7 @@ static int iwl_mld_rx_crypto(struct iwl_mld *mld,\n \n \tif (unlikely(ieee80211_is_mgmt(hdr-\u003eframe_control) \u0026\u0026\n \t\t     !ieee80211_has_protected(hdr-\u003eframe_control)))\n-\t\treturn iwl_mld_rx_mgmt_prot(sta, hdr, rx_status, status,\n+\t\treturn iwl_mld_rx_mgmt_prot(link_sta, hdr, rx_status, status,\n \t\t\t\t\t    le16_to_cpu(desc-\u003empdu_len));\n \n \tif (!ieee80211_has_protected(hdr-\u003eframe_control) ||\n@@ -2023,7 +2017,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,\n \tstruct iwl_rx_packet *pkt = rxb_addr(rxb);\n \tstruct iwl_mld_rx_phy_data phy_data = {};\n \tstruct iwl_rx_mpdu_desc *mpdu_desc = (void *)pkt-\u003edata;\n-\tstruct ieee80211_sta *sta;\n+\tstruct ieee80211_link_sta *link_sta;\n \tstruct ieee80211_hdr *hdr;\n \tstruct sk_buff *skb;\n \tsize_t mpdu_desc_size = sizeof(*mpdu_desc);\n@@ -2086,7 +2080,8 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,\n \n \trcu_read_lock();\n \n-\tsta = iwl_mld_rx_with_sta(mld, hdr, skb, mpdu_desc, pkt, queue, \u0026drop);\n+\tlink_sta = iwl_mld_rx_with_sta(mld, hdr, skb, mpdu_desc, pkt, queue,\n+\t\t\t\t       \u0026drop);\n \tif (drop)\n \t\tgoto drop;\n \n@@ -2127,7 +2122,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,\n \n \tiwl_mld_rx_fill_status(mld, link_id, hdr, skb, \u0026phy_data);\n \n-\tif (iwl_mld_rx_crypto(mld, sta, hdr, rx_status, mpdu_desc, queue,\n+\tif (iwl_mld_rx_crypto(mld, link_sta, hdr, rx_status, mpdu_desc, queue,\n \t\t\t      le32_to_cpu(pkt-\u003elen_n_flags), \u0026crypto_len))\n \t\tgoto drop;\n \n@@ -2140,7 +2135,8 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,\n \tif (iwl_mld_time_sync_frame(mld, skb, hdr-\u003eaddr2))\n \t\tgoto out;\n \n-\treorder_res = iwl_mld_reorder(mld, napi, queue, sta, skb, mpdu_desc);\n+\treorder_res = iwl_mld_reorder(mld, napi, queue, link_sta, skb,\n+\t\t\t\t      mpdu_desc);\n \tswitch (reorder_res) {\n \tcase IWL_MLD_PASS_SKB:\n \t\tbreak;\n@@ -2153,7 +2149,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,\n \t\tgoto drop;\n \t}\n \n-\tiwl_mld_pass_packet_to_mac80211(mld, napi, skb, queue, sta);\n+\tiwl_mld_pass_packet_to_mac80211(mld, napi, skb, queue, link_sta);\n \n \tgoto out;\n \ndiff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.h b/drivers/net/wireless/intel/iwlwifi/mld/rx.h\nindex 573b89c3c9c61..b08c5ade3db8f 100644\n--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.h\n+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.h\n@@ -69,7 +69,7 @@ void iwl_mld_handle_rsc_notif(struct iwl_mld *mld,\n void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,\n \t\t\t\t     struct napi_struct *napi,\n \t\t\t\t     struct sk_buff *skb, int queue,\n-\t\t\t\t     struct ieee80211_sta *sta);\n+\t\t\t\t     struct ieee80211_link_sta *link_sta);\n \n void iwl_mld_handle_phy_air_sniffer_notif(struct iwl_mld *mld,\n \t\t\t\t\t  struct napi_struct *napi,\ndiff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c\nindex 29b0248cec3df..e9efe2996f071 100644\n--- a/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c\n+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c\n@@ -2,7 +2,7 @@\n /*\n  * KUnit tests for channel helper functions\n  *\n- * Copyright (C) 2024-2025 Intel Corporation\n+ * Copyright (C) 2024-2026 Intel Corporation\n  */\n #include \u003ckunit/test.h\u003e\n #include \u003ckunit/static_stub.h\u003e\n@@ -441,7 +441,7 @@ static void\n fake_iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,\n \t\t\t\t     struct napi_struct *napi,\n \t\t\t\t     struct sk_buff *skb, int queue,\n-\t\t\t\t     struct ieee80211_sta *sta)\n+\t\t\t\t     struct ieee80211_link_sta *link_sta)\n {\n \t__skb_queue_tail(\u0026g_released_skbs, skb);\n \tg_num_released_skbs++;\n@@ -630,7 +630,8 @@ static void test_reorder_buffer(struct kunit *test)\n \tmpdu_desc = setup_mpdu_desc();\n \n \trcu_read_lock();\n-\treorder_res = iwl_mld_reorder(mld, NULL, QUEUE, sta, skb, mpdu_desc);\n+\treorder_res = iwl_mld_reorder(mld, NULL, QUEUE, \u0026sta-\u003edeflink, skb,\n+\t\t\t\t      mpdu_desc);\n \trcu_read_unlock();\n \n \tKUNIT_ASSERT_EQ(test, reorder_res, param-\u003eexpected.reorder_res);\ndiff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c\nindex ab1eb2eb0c3cd..8c197a090c2b1 100644\n--- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c\n+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c\n@@ -90,7 +90,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,\n \t\t\t\tfraglen, rxb-\u003etruesize);\n \t}\n \n-\tieee80211_rx_napi(mvm-\u003ehw, sta, skb, napi);\n+\tieee80211_rx_napi(mvm-\u003ehw, sta ? \u0026sta-\u003edeflink : NULL, skb, napi);\n }\n \n /*\ndiff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c\nindex 7f0b4f5daa214..8c4009d3a00ca 100644\n--- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c\n+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c\n@@ -1,6 +1,6 @@\n // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause\n /*\n- * Copyright (C) 2012-2014, 2018-2025 Intel Corporation\n+ * Copyright (C) 2012-2014, 2018-2026 Intel Corporation\n  * Copyright (C) 2013-2015 Intel Mobile Communications GmbH\n  * Copyright (C) 2015-2017 Intel Deutschland GmbH\n  */\n@@ -243,7 +243,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,\n \t\treturn;\n \t}\n \n-\tieee80211_rx_napi(mvm-\u003ehw, sta, skb, napi);\n+\tieee80211_rx_napi(mvm-\u003ehw, sta ? \u0026sta-\u003edeflink : NULL, skb, napi);\n }\n \n static bool iwl_mvm_used_average_energy(struct iwl_mvm *mvm,\n@@ -2528,7 +2528,7 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi,\n \t}\n \n \trcu_read_lock();\n-\tieee80211_rx_napi(mvm-\u003ehw, sta, skb, napi);\n+\tieee80211_rx_napi(mvm-\u003ehw, sta ? \u0026sta-\u003edeflink : NULL, skb, napi);\n \trcu_read_unlock();\n }\n \ndiff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c\nindex abbe65cbcd89e..cda5c5d51aab0 100644\n--- a/drivers/net/wireless/mediatek/mt76/mac80211.c\n+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c\n@@ -1255,11 +1255,12 @@ EXPORT_SYMBOL(mt76_rx_signal);\n static void\n mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,\n \t\tstruct ieee80211_hw **hw,\n-\t\tstruct ieee80211_sta **sta)\n+\t\tstruct ieee80211_link_sta **link_sta)\n {\n \tstruct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);\n \tstruct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);\n \tstruct mt76_rx_status mstat;\n+\tstruct ieee80211_sta *sta;\n \n \tmstat = *((struct mt76_rx_status *)skb-\u003ecb);\n \tmemset(status, 0, sizeof(*status));\n@@ -1301,12 +1302,14 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,\n \tmemcpy(status-\u003echain_signal, mstat.chain_signal,\n \t       sizeof(mstat.chain_signal));\n \n-\tif (mstat.wcid) {\n-\t\tstatus-\u003elink_valid = mstat.wcid-\u003elink_valid;\n-\t\tstatus-\u003elink_id = mstat.wcid-\u003elink_id;\n-\t}\n+\tsta = wcid_to_sta(mstat.wcid);\n+\tif (!sta)\n+\t\t*link_sta = NULL;\n+\telse if (mstat.wcid-\u003elink_valid)\n+\t\t*link_sta = rcu_dereference(sta-\u003elink[mstat.wcid-\u003elink_id]);\n+\telse\n+\t\t*link_sta = \u0026sta-\u003edeflink;\n \n-\t*sta = wcid_to_sta(mstat.wcid);\n \t*hw = mt76_phy_hw(dev, mstat.phy_idx);\n }\n \n@@ -1530,7 +1533,7 @@ mt76_check_sta(struct mt76_dev *dev, struct sk_buff *skb)\n void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,\n \t\t      struct napi_struct *napi)\n {\n-\tstruct ieee80211_sta *sta;\n+\tstruct ieee80211_link_sta *link_sta;\n \tstruct ieee80211_hw *hw;\n \tstruct sk_buff *skb, *tmp;\n \tLIST_HEAD(list);\n@@ -1541,8 +1544,8 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,\n \n \t\tmt76_check_ccmp_pn(skb);\n \t\tskb_shinfo(skb)-\u003efrag_list = NULL;\n-\t\tmt76_rx_convert(dev, skb, \u0026hw, \u0026sta);\n-\t\tieee80211_rx_list(hw, sta, skb, \u0026list);\n+\t\tmt76_rx_convert(dev, skb, \u0026hw, \u0026link_sta);\n+\t\tieee80211_rx_list(hw, link_sta, skb, \u0026list);\n \n \t\t/* subsequent amsdu frames */\n \t\twhile (nskb) {\n@@ -1550,8 +1553,8 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,\n \t\t\tnskb = nskb-\u003enext;\n \t\t\tskb-\u003enext = NULL;\n \n-\t\t\tmt76_rx_convert(dev, skb, \u0026hw, \u0026sta);\n-\t\t\tieee80211_rx_list(hw, sta, skb, \u0026list);\n+\t\t\tmt76_rx_convert(dev, skb, \u0026hw, \u0026link_sta);\n+\t\t\tieee80211_rx_list(hw, link_sta, skb, \u0026list);\n \t\t}\n \t}\n \tspin_unlock(\u0026dev-\u003erx_lock);\ndiff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c\nindex 397ebbfcac09a..454d876aeb886 100644\n--- a/drivers/net/wireless/realtek/rtw89/core.c\n+++ b/drivers/net/wireless/realtek/rtw89/core.c\n@@ -3073,10 +3073,8 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,\n \tstruct rtw89_vif *rtwvif = vif_to_rtwvif(vif);\n \tstruct rtw89_rx_desc_info *desc_info = iter_data-\u003edesc_info;\n \tstruct sk_buff *skb = iter_data-\u003eskb;\n-\tstruct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);\n \tstruct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb-\u003edata;\n \tstruct rtw89_rx_phy_ppdu *phy_ppdu = iter_data-\u003ephy_ppdu;\n-\tbool is_mld = ieee80211_vif_is_mld(vif);\n \tstruct ieee80211_bss_conf *bss_conf;\n \tstruct rtw89_vif_link *rtwvif_link;\n \tconst u8 *bssid = iter_data-\u003ebssid;\n@@ -3110,11 +3108,6 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,\n \tif (!ether_addr_equal(target_bssid, bssid))\n \t\tgoto out;\n \n-\tif (is_mld) {\n-\t\trx_status-\u003elink_valid = true;\n-\t\trx_status-\u003elink_id = rtwvif_link-\u003elink_id;\n-\t}\n-\n \tbb = rtw89_get_bb_ctx(rtwdev, rtwvif_link-\u003ephy_idx);\n \tpkt_stat = \u0026bb-\u003ecur_pkt_stat;\n \ndiff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c\nindex 9f895ad718e9b..1a12261f3e8e5 100644\n--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c\n+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c\n@@ -1883,9 +1883,6 @@ static void mac80211_hwsim_rx(struct mac80211_hwsim_data *data,\n \t\t\t\tsp-\u003eactive_links_rx \u0026= ~BIT(link_id);\n \t\t\telse\n \t\t\t\tsp-\u003eactive_links_rx |= BIT(link_id);\n-\n-\t\t\trx_status-\u003elink_valid = true;\n-\t\t\trx_status-\u003elink_id = link_id;\n \t\t}\n \t\trcu_read_unlock();\n \t}\ndiff --git a/include/net/cfg80211.h b/include/net/cfg80211.h\nindex 6461a7bd0169d..9aa7d2d4a1841 100644\n--- a/include/net/cfg80211.h\n+++ b/include/net/cfg80211.h\n@@ -4009,6 +4009,7 @@ struct cfg80211_update_ft_ies_params {\n  * @link_id: for MLO, the link ID to transmit on, -1 if not given; note\n  *\tthat the link ID isn't validated (much), it's in range but the\n  *\tlink might not exist (or be used by the receiver STA)\n+ * @no_sta: set if the frame should not be transmitted using an existing STA\n  */\n struct cfg80211_mgmt_tx_params {\n \tstruct ieee80211_channel *chan;\n@@ -4021,6 +4022,7 @@ struct cfg80211_mgmt_tx_params {\n \tint n_csa_offsets;\n \tconst u16 *csa_offsets;\n \tint link_id;\n+\tbool no_sta;\n };\n \n /**\n@@ -9501,6 +9503,7 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,\n  * @flags: flags, as defined in \u0026enum nl80211_rxmgmt_flags\n  * @rx_tstamp: Hardware timestamp of frame RX in nanoseconds\n  * @ack_tstamp: Hardware timestamp of ack TX in nanoseconds\n+ * @no_sta: set if no station is known for the frame (relevant for MLD)\n  */\n struct cfg80211_rx_info {\n \tint freq;\n@@ -9512,6 +9515,7 @@ struct cfg80211_rx_info {\n \tu32 flags;\n \tu64 rx_tstamp;\n \tu64 ack_tstamp;\n+\tbool no_sta;\n };\n \n /**\ndiff --git a/include/net/mac80211.h b/include/net/mac80211.h\nindex 7bb4618065b67..1172d7b83ed18 100644\n--- a/include/net/mac80211.h\n+++ b/include/net/mac80211.h\n@@ -1770,10 +1770,9 @@ enum mac80211_rx_encoding {\n  * @ampdu_reference: A-MPDU reference number, must be a different value for\n  *\teach A-MPDU but the same for each subframe within one A-MPDU\n  * @zero_length_psdu_type: radiotap type of the 0-length PSDU\n- * @link_valid: if the link which is identified by @link_id is valid. This flag\n- *\tis set only when connection is MLO.\n- * @link_id: id of the link used to receive the packet. This is used along with\n- *\t@link_valid.\n+ * @link_id: id of the link used to receive the packet. Set and used by\n+ *\tmac80211 internally, it uses @freq set by the driver to identify the\n+ *\tcorrect link per vif.\n  */\n struct ieee80211_rx_status {\n \tu64 mactime;\n@@ -1813,7 +1812,7 @@ struct ieee80211_rx_status {\n \tu8 chains;\n \ts8 chain_signal[IEEE80211_MAX_CHAINS];\n \tu8 zero_length_psdu_type;\n-\tu8 link_valid:1, link_id:4;\n+\tu8 link_id:4;\n };\n \n static_assert(sizeof(struct ieee80211_rx_status) \u003c= sizeof_field(struct sk_buff, cb));\n@@ -5389,14 +5388,18 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw);\n  * mixed for a single hardware. Must not run concurrently with\n  * ieee80211_tx_status_skb() or ieee80211_tx_status_ni().\n  *\n+ * For data frames, when hardware has done address translation, a link station\n+ * has to be provided and the frequency information may be skipped.\n+ *\n  * This function must be called with BHs disabled and RCU read lock\n  *\n  * @hw: the hardware this frame came in on\n- * @sta: the station the frame was received from, or %NULL\n+ * @link_sta: the link station the data frame was received from, or %NULL\n  * @skb: the buffer to receive, owned by mac80211 after this call\n  * @list: the destination list\n  */\n-void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *sta,\n+void ieee80211_rx_list(struct ieee80211_hw *hw,\n+\t\t       struct ieee80211_link_sta *link_sta,\n \t\t       struct sk_buff *skb, struct list_head *list);\n \n /**\n@@ -5414,14 +5417,18 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *sta,\n  * mixed for a single hardware. Must not run concurrently with\n  * ieee80211_tx_status_skb() or ieee80211_tx_status_ni().\n  *\n+ * For data frames, when hardware has done address translation, a link station\n+ * has to be provided and the frequency information may be skipped.\n+ *\n  * This function must be called with BHs disabled.\n  *\n  * @hw: the hardware this frame came in on\n- * @sta: the station the frame was received from, or %NULL\n+ * @link_sta: the link station the data frame was received from, or %NULL\n  * @skb: the buffer to receive, owned by mac80211 after this call\n  * @napi: the NAPI context\n  */\n-void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *sta,\n+void ieee80211_rx_napi(struct ieee80211_hw *hw,\n+\t\t       struct ieee80211_link_sta *link_sta,\n \t\t       struct sk_buff *skb, struct napi_struct *napi);\n \n /**\ndiff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h\nindex 32553cacf9889..510102febc269 100644\n--- a/include/uapi/linux/nl80211.h\n+++ b/include/uapi/linux/nl80211.h\n@@ -3185,6 +3185,11 @@ enum nl80211_commands {\n  *\tThe aggregated message always precedes the per-link messages for the\n  *\tsame station within a dump sequence.\n  *\n+ * @NL80211_ATTR_FRAME_NO_STA: Valid for @NL80211_CMD_FRAME to denote that\n+ *\tthe kernel had no station for a received frame or should not use a\n+ *\tknown station to transmit a frame. This is relevant to know whether\n+ *\tMLD address translation happened or to disable it when sending a frame.\n+ *\n  * @NUM_NL80211_ATTR: total number of nl80211_attrs available\n  * @NL80211_ATTR_MAX: highest attribute number currently defined\n  * @__NL80211_ATTR_AFTER_LAST: internal use\n@@ -3785,6 +3790,8 @@ enum nl80211_attrs {\n \n \tNL80211_ATTR_STA_DUMP_LINK_STATS,\n \n+\tNL80211_ATTR_FRAME_NO_STA,\n+\n \t/* add attributes here, update the policy in nl80211.c */\n \n \t__NL80211_ATTR_AFTER_LAST,\ndiff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c\nindex 0832213430f43..ce9fb19d8c18c 100644\n--- a/net/mac80211/agg-tx.c\n+++ b/net/mac80211/agg-tx.c\n@@ -98,7 +98,7 @@ static void ieee80211_send_addba_request(struct sta_info *sta, u16 tid,\n \tif (sta-\u003esta.deflink.he_cap.has_he)\n \t\tieee80211_add_addbaext(skb, 0, agg_size);\n \n-\tieee80211_tx_skb_tid(sdata, skb, tid, -1);\n+\tieee80211_tx_skb_tid(sdata, skb, NULL, tid, -1);\n }\n \n void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)\n@@ -127,7 +127,7 @@ void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)\n \n \tIEEE80211_SKB_CB(skb)-\u003eflags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |\n \t\t\t\t\tIEEE80211_TX_CTL_REQ_TX_STATUS;\n-\tieee80211_tx_skb_tid(sdata, skb, tid, -1);\n+\tieee80211_tx_skb_tid(sdata, skb, NULL, tid, -1);\n }\n EXPORT_SYMBOL(ieee80211_send_bar);\n \ndiff --git a/net/mac80211/ap.c b/net/mac80211/ap.c\nindex e7ac99c5a22eb..fe255891c35f0 100644\n--- a/net/mac80211/ap.c\n+++ b/net/mac80211/ap.c\n@@ -82,9 +82,6 @@ ieee80211_rx_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata,\n \tif (!ift_ext_capa)\n \t\treturn;\n \n-\tif (!status-\u003elink_valid)\n-\t\treturn;\n-\n \tsta = sta_info_get_bss(sdata, mgmt-\u003esa);\n \tif (!sta)\n \t\treturn;\ndiff --git a/net/mac80211/ht.c b/net/mac80211/ht.c\nindex e1e1b7f82f3fe..c5c2113fa4f79 100644\n--- a/net/mac80211/ht.c\n+++ b/net/mac80211/ht.c\n@@ -545,7 +545,7 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,\n \tinfo-\u003estatus_data = IEEE80211_STATUS_TYPE_SMPS |\n \t\t\t    u16_encode_bits(status_link_id \u003c\u003c 2 | smps,\n \t\t\t\t\t    IEEE80211_STATUS_SUBDATA_MASK);\n-\tieee80211_tx_skb_tid(sdata, skb, 7, link_id);\n+\tieee80211_tx_skb_tid(sdata, skb, NULL, 7, link_id);\n \n \treturn 0;\n }\ndiff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h\nindex 65311340db406..9514f01778beb 100644\n--- a/net/mac80211/ieee80211_i.h\n+++ b/net/mac80211/ieee80211_i.h\n@@ -2471,7 +2471,8 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,\n \t\t    struct sta_info *sta, struct sk_buff *skb);\n \n void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,\n-\t\t\t\t struct sk_buff *skb, int tid, int link_id,\n+\t\t\t\t struct sk_buff *skb, struct sta_info *sta,\n+\t\t\t\t int tid, int link_id,\n \t\t\t\t enum nl80211_band band);\n \n static inline bool ieee80211_require_encrypted_assoc(__le16 fc,\n@@ -2486,18 +2487,20 @@ ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,\n \t\t\t  enum nl80211_band band)\n {\n \trcu_read_lock();\n-\t__ieee80211_tx_skb_tid_band(sdata, skb, tid, -1, band);\n+\t__ieee80211_tx_skb_tid_band(sdata, skb, ERR_PTR(-ENOENT),\n+\t\t\t\t    tid, -1, band);\n \trcu_read_unlock();\n }\n \n void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,\n-\t\t\t  struct sk_buff *skb, int tid, int link_id);\n+\t\t\t  struct sk_buff *skb, struct sta_info *sta,\n+\t\t\t  int tid, int link_id);\n \n static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,\n \t\t\t\t    struct sk_buff *skb)\n {\n \t/* Send all internal mgmt frames on VO. Accordingly set TID to 7. */\n-\tieee80211_tx_skb_tid(sdata, skb, 7, -1);\n+\tieee80211_tx_skb_tid(sdata, skb, NULL, 7, -1);\n }\n \n /**\ndiff --git a/net/mac80211/iface.c b/net/mac80211/iface.c\nindex 18e62c5ff0fd8..29d532183814d 100644\n--- a/net/mac80211/iface.c\n+++ b/net/mac80211/iface.c\n@@ -1674,11 +1674,8 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local,\n \t\t\t\tbreak;\n \n \t\t\tstatus = IEEE80211_SKB_RXCB(skb);\n-\t\t\tif (!status-\u003elink_valid)\n-\t\t\t\tlink_sta = \u0026sta-\u003edeflink;\n-\t\t\telse\n-\t\t\t\tlink_sta = rcu_dereference_protected(sta-\u003elink[status-\u003elink_id],\n-\t\t\t\t\t\t\tlockdep_is_held(\u0026local-\u003ehw.wiphy-\u003emtx));\n+\t\t\tlink_sta = wiphy_dereference(local-\u003ehw.wiphy,\n+\t\t\t\t\t\t     sta-\u003elink[status-\u003elink_id]);\n \t\t\tif (link_sta)\n \t\t\t\tieee80211_ht_handle_chanwidth_notif(local, sdata, sta,\n \t\t\t\t\t\t\t\t    link_sta, chanwidth,\ndiff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c\nindex 47ec703f10949..189dd3467f764 100644\n--- a/net/mac80211/mlme.c\n+++ b/net/mac80211/mlme.c\n@@ -11827,12 +11827,10 @@ void ieee80211_sta_rx_queued_frame(struct ieee80211_sub_if_data *sdata,\n \trx_status = (struct ieee80211_rx_status *) skb-\u003ecb;\n \tfc = le16_to_cpu(mgmt-\u003eframe_control);\n \n-\tif (rx_status-\u003elink_valid) {\n-\t\tlink = sdata_dereference(sdata-\u003elink[rx_status-\u003elink_id],\n-\t\t\t\t\t sdata);\n-\t\tif (!link)\n-\t\t\treturn;\n-\t}\n+\tlink = sdata_dereference(sdata-\u003elink[rx_status-\u003elink_id],\n+\t\t\t\t sdata);\n+\tif (!link)\n+\t\treturn;\n \n \tswitch (fc \u0026 IEEE80211_FCTL_STYPE) {\n \tcase IEEE80211_STYPE_BEACON:\ndiff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c\nindex 7acef80d5f1fb..ddd7a96a2f817 100644\n--- a/net/mac80211/offchannel.c\n+++ b/net/mac80211/offchannel.c\n@@ -854,8 +854,10 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,\n \t\t\tneed_offchan = true;\n \n \t\trcu_read_lock();\n-\t\tsta = sta_info_get_bss(sdata, mgmt-\u003eda);\n-\t\tmlo_sta = sta \u0026\u0026 sta-\u003esta.mlo;\n+\t\tif (!params-\u003eno_sta) {\n+\t\t\tsta = sta_info_get_bss(sdata, mgmt-\u003eda);\n+\t\t\tmlo_sta = sta \u0026\u0026 sta-\u003esta.mlo;\n+\t\t}\n \n \t\tif (!ieee80211_is_action(mgmt-\u003eframe_control) ||\n \t\t    mgmt-\u003eu.action.category == WLAN_CATEGORY_PUBLIC ||\n@@ -884,7 +886,8 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,\n \t\t     local-\u003eops-\u003eremain_on_channel \u0026\u0026\n \t\t     memcmp(sdata-\u003evif.cfg.ap_addr, mgmt-\u003ebssid, ETH_ALEN))) {\n \t\t\tneed_offchan = true;\n-\t\t} else if (sdata-\u003eu.mgd.associated \u0026\u0026\n+\t\t} else if (!params-\u003eno_sta \u0026\u0026\n+\t\t\t   sdata-\u003eu.mgd.associated \u0026\u0026\n \t\t\t   ether_addr_equal(sdata-\u003evif.cfg.ap_addr, mgmt-\u003eda)) {\n \t\t\tsta = sta_info_get_bss(sdata, mgmt-\u003eda);\n \t\t\tmlo_sta = sta \u0026\u0026 sta-\u003esta.mlo;\n@@ -1022,7 +1025,9 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,\n \t}\n \n \tif (!need_offchan) {\n-\t\tieee80211_tx_skb_tid(sdata, skb, 7, link_id);\n+\t\tieee80211_tx_skb_tid(sdata, skb,\n+\t\t\t\t     params-\u003eno_sta ? ERR_PTR(-ENOENT) : sta,\n+\t\t\t\t     7, link_id);\n \t\tret = 0;\n \t\tgoto out_unlock;\n \t}\ndiff --git a/net/mac80211/rx.c b/net/mac80211/rx.c\nindex 31dc6f83781b1..d6755a4c7d3c9 100644\n--- a/net/mac80211/rx.c\n+++ b/net/mac80211/rx.c\n@@ -222,43 +222,21 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,\n }\n \n static void __ieee80211_queue_skb_to_iface(struct ieee80211_sub_if_data *sdata,\n-\t\t\t\t\t   int link_id,\n-\t\t\t\t\t   struct sta_info *sta,\n+\t\t\t\t\t   struct link_sta_info *link_sta,\n \t\t\t\t\t   struct sk_buff *skb)\n {\n-\tstruct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);\n-\n-\tif (link_id \u003e= 0) {\n-\t\tstatus-\u003elink_valid = 1;\n-\t\tstatus-\u003elink_id = link_id;\n-\t} else {\n-\t\tstatus-\u003elink_valid = 0;\n-\t}\n-\n \tskb_queue_tail(\u0026sdata-\u003eskb_queue, skb);\n \twiphy_work_queue(sdata-\u003elocal-\u003ehw.wiphy, \u0026sdata-\u003ework);\n-\tif (sta) {\n-\t\tstruct link_sta_info *link_sta_info;\n-\n-\t\tif (link_id \u003e= 0) {\n-\t\t\tlink_sta_info = rcu_dereference(sta-\u003elink[link_id]);\n-\t\t\tif (!link_sta_info)\n-\t\t\t\treturn;\n-\t\t} else {\n-\t\t\tlink_sta_info = \u0026sta-\u003edeflink;\n-\t\t}\n-\n-\t\tlink_sta_info-\u003erx_stats.packets++;\n-\t}\n+\tif (link_sta)\n+\t\tlink_sta-\u003erx_stats.packets++;\n }\n \n static void ieee80211_queue_skb_to_iface(struct ieee80211_sub_if_data *sdata,\n-\t\t\t\t\t int link_id,\n-\t\t\t\t\t struct sta_info *sta,\n+\t\t\t\t\t struct link_sta_info *link_sta,\n \t\t\t\t\t struct sk_buff *skb)\n {\n \tskb-\u003eprotocol = 0;\n-\t__ieee80211_queue_skb_to_iface(sdata, link_id, sta, skb);\n+\t__ieee80211_queue_skb_to_iface(sdata, link_sta, skb);\n }\n \n static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata,\n@@ -301,7 +279,7 @@ static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata,\n \tif (!skb)\n \t\treturn;\n \n-\tieee80211_queue_skb_to_iface(sdata, -1, NULL, skb);\n+\tieee80211_queue_skb_to_iface(sdata, NULL, skb);\n }\n \n /*\n@@ -1500,7 +1478,7 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,\n \t/* if this mpdu is fragmented - terminate rx aggregation session */\n \tsc = le16_to_cpu(hdr-\u003eseq_ctrl);\n \tif (sc \u0026 IEEE80211_SCTL_FRAG) {\n-\t\tieee80211_queue_skb_to_iface(rx-\u003esdata, rx-\u003elink_id, NULL, skb);\n+\t\tieee80211_queue_skb_to_iface(rx-\u003esdata, NULL, skb);\n \t\treturn;\n \t}\n \n@@ -2524,7 +2502,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)\n \n  out:\n \tieee80211_led_rx(rx-\u003elocal);\n-\tif (rx-\u003esta)\n+\tif (rx-\u003elink_sta)\n \t\trx-\u003elink_sta-\u003erx_stats.packets++;\n \treturn RX_CONTINUE;\n }\n@@ -3335,8 +3313,8 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)\n \t\t    (tf-\u003eaction_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||\n \t\t     tf-\u003eaction_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {\n \t\t\trx-\u003eskb-\u003eprotocol = cpu_to_be16(ETH_P_TDLS);\n-\t\t\t__ieee80211_queue_skb_to_iface(sdata, rx-\u003elink_id,\n-\t\t\t\t\t\t       rx-\u003esta, rx-\u003eskb);\n+\t\t\t__ieee80211_queue_skb_to_iface(sdata, rx-\u003elink_sta,\n+\t\t\t\t\t\t       rx-\u003eskb);\n \t\t\treturn RX_QUEUED;\n \t\t}\n \t}\n@@ -3991,7 +3969,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)\n \treturn RX_QUEUED;\n \n  queue:\n-\tieee80211_queue_skb_to_iface(sdata, rx-\u003elink_id, rx-\u003esta, rx-\u003eskb);\n+\tieee80211_queue_skb_to_iface(sdata, rx-\u003elink_sta, rx-\u003eskb);\n \treturn RX_QUEUED;\n }\n \n@@ -4005,6 +3983,7 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)\n \t\t.len = rx-\u003eskb-\u003elen,\n \t\t.link_id = rx-\u003elink_id,\n \t\t.have_link_id = rx-\u003elink_id \u003e= 0,\n+\t\t.no_sta = !rx-\u003esta,\n \t};\n \n \t/* skip known-bad action frames and return them in the next handler */\n@@ -4127,7 +4106,7 @@ ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx)\n \t\t\t\t\tlocal-\u003ehw.offchannel_tx_hw_queue;\n \t\t}\n \n-\t\t__ieee80211_tx_skb_tid_band(rx-\u003esdata, nskb, 7, -1,\n+\t\t__ieee80211_tx_skb_tid_band(rx-\u003esdata, nskb, rx-\u003esta, 7, -1,\n \t\t\t\t\t    status-\u003eband);\n \t}\n \n@@ -4147,7 +4126,7 @@ ieee80211_rx_h_ext(struct ieee80211_rx_data *rx)\n \t\treturn RX_DROP_U_UNEXPECTED_EXT_FRAME;\n \n \t/* for now only beacons are ext, so queue them */\n-\tieee80211_queue_skb_to_iface(sdata, rx-\u003elink_id, rx-\u003esta, rx-\u003eskb);\n+\tieee80211_queue_skb_to_iface(sdata, rx-\u003elink_sta, rx-\u003eskb);\n \n \treturn RX_QUEUED;\n }\n@@ -4204,7 +4183,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)\n \t\treturn RX_DROP_U_UNHANDLED_MGMT_STYPE;\n \t}\n \n-\tieee80211_queue_skb_to_iface(sdata, rx-\u003elink_id, rx-\u003esta, rx-\u003eskb);\n+\tieee80211_queue_skb_to_iface(sdata, rx-\u003elink_sta, rx-\u003eskb);\n \n \treturn RX_QUEUED;\n }\n@@ -4248,12 +4227,28 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,\n \tspin_lock_bh(\u0026rx-\u003elocal-\u003erx_path_lock);\n \n \twhile ((skb = __skb_dequeue(frames))) {\n+\t\tstruct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);\n+\n \t\t/*\n-\t\t * all the other fields are valid across frames\n-\t\t * that belong to an aMPDU since they are on the\n-\t\t * same TID from the same station\n+\t\t * Most fields are valid across frames. However,\n+\t\t * in the case of reordering frames may have arrived\n+\t\t * on different links, so adjust the link_id, link\n+\t\t * and link_sta accordingly.\n \t\t */\n \t\trx-\u003eskb = skb;\n+\t\tif (ieee80211_vif_is_mld(\u0026rx-\u003esdata-\u003evif) \u0026\u0026\n+\t\t    unlikely(rx-\u003elink_id != status-\u003elink_id)) {\n+\t\t\tif (rx-\u003esta) {\n+\t\t\t\trx-\u003elink_sta =\n+\t\t\t\t\trcu_dereference(rx-\u003esta-\u003elink[rx-\u003elink_id]);\n+\n+\t\t\t\t/* Link got disabled, just use deflink */\n+\t\t\t\tif (!rx-\u003elink_sta)\n+\t\t\t\t\trx-\u003elink_sta = \u0026rx-\u003esta-\u003edeflink;\n+\t\t\t}\n+\t\t\trx-\u003elink_id = status-\u003elink_id;\n+\t\t\trx-\u003elink = rcu_dereference(rx-\u003esdata-\u003elink[status-\u003elink_id]);\n+\t\t}\n \n \t\tif (WARN_ON_ONCE(!rx-\u003elink)) {\n \t\t\tres = RX_DROP_U_NO_LINK;\n@@ -4323,7 +4318,34 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)\n static bool\n ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id)\n {\n-\treturn !!(sta-\u003evalid_links \u0026 BIT(link_id));\n+\tif (sta-\u003emlo)\n+\t\treturn sta-\u003evalid_links \u0026 BIT(link_id);\n+\n+\treturn sta-\u003edeflink.link_id == link_id;\n+}\n+\n+static bool ieee80211_rx_data_set_link_sta(struct ieee80211_rx_data *rx,\n+\t\t\t\t\t   struct link_sta_info *link_sta)\n+{\n+\tstruct sta_info *sta;\n+\n+\tif (WARN_ON_ONCE(!link_sta) ||\n+\t    !ieee80211_rx_is_valid_sta_link_id(\u0026link_sta-\u003esta-\u003esta,\n+\t\t\t\t\t       link_sta-\u003elink_id))\n+\t\treturn false;\n+\n+\tsta = link_sta-\u003esta;\n+\n+\trx-\u003esta = sta;\n+\trx-\u003elocal = sta-\u003esdata-\u003elocal;\n+\trx-\u003elink = rcu_dereference(sta-\u003esdata-\u003elink[link_sta-\u003elink_id]);\n+\tif (!rx-\u003elink)\n+\t\treturn false;\n+\n+\trx-\u003elink_id = rx-\u003elink-\u003elink_id;\n+\trx-\u003elink_sta = link_sta;\n+\n+\treturn true;\n }\n \n static bool ieee80211_rx_data_set_link(struct ieee80211_rx_data *rx,\n@@ -4360,11 +4382,13 @@ static bool ieee80211_rx_data_set_sta(struct ieee80211_rx_data *rx,\n \n \tif (link_id \u003c 0) {\n \t\tif (ieee80211_vif_is_mld(\u0026rx-\u003esdata-\u003evif) \u0026\u0026\n-\t\t    sta \u0026\u0026 !sta-\u003esta.valid_links)\n+\t\t    sta \u0026\u0026 !sta-\u003esta.valid_links) {\n \t\t\trx-\u003elink =\n \t\t\t\trcu_dereference(rx-\u003esdata-\u003elink[sta-\u003edeflink.link_id]);\n-\t\telse\n+\t\t\trx-\u003elink_sta = \u0026sta-\u003edeflink;\n+\t\t} else {\n \t\t\trx-\u003elink = \u0026rx-\u003esdata-\u003edeflink;\n+\t\t}\n \t} else if (!ieee80211_rx_data_set_link(rx, link_id)) {\n \t\treturn false;\n \t}\n@@ -4387,7 +4411,7 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)\n \tstruct tid_ampdu_rx *tid_agg_rx;\n \tint link_id = -1;\n \n-\t/* FIXME: statistics won't be right with this */\n+\t/* NOTE: the correct link STA for the frame is set later */\n \tif (sta-\u003esta.valid_links)\n \t\tlink_id = ffs(sta-\u003esta.valid_links) - 1;\n \n@@ -4914,20 +4938,14 @@ static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,\n {\n \tstruct ieee80211_sta_rx_stats *stats;\n \tstruct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx-\u003eskb);\n-\tstruct sta_info *sta = rx-\u003esta;\n-\tstruct link_sta_info *link_sta;\n+\tstruct link_sta_info *link_sta = rx-\u003elink_sta;\n \tstruct sk_buff *skb = rx-\u003eskb;\n \tvoid *sa = skb-\u003edata + ETH_ALEN;\n \tvoid *da = skb-\u003edata;\n \n-\tif (rx-\u003elink_id \u003e= 0) {\n-\t\tlink_sta = rcu_dereference(sta-\u003elink[rx-\u003elink_id]);\n-\t\tif (WARN_ON_ONCE(!link_sta)) {\n-\t\t\tdev_kfree_skb(rx-\u003eskb);\n-\t\t\treturn;\n-\t\t}\n-\t} else {\n-\t\tlink_sta = \u0026sta-\u003edeflink;\n+\tif (WARN_ON_ONCE(!link_sta)) {\n+\t\tdev_kfree_skb(rx-\u003eskb);\n+\t\treturn;\n \t}\n \n \tstats = \u0026link_sta-\u003erx_stats;\n@@ -5154,6 +5172,9 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,\n \t\tgoto drop;\n \t}\n \n+\tstatus = IEEE80211_SKB_RXCB(skb);\n+\tstatus-\u003elink_id = rx-\u003elink_id \u003c 0 ? 0 : rx-\u003elink_id;\n+\n \tieee80211_rx_8023(rx, fast_rx, orig_len);\n \n \treturn true;\n@@ -5178,6 +5199,7 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,\n \tstruct ieee80211_hdr *hdr = (void *)skb-\u003edata;\n \tstruct link_sta_info *link_sta = rx-\u003elink_sta;\n \tstruct ieee80211_link_data *link = rx-\u003elink;\n+\tstruct ieee80211_rx_status *status;\n \n \trx-\u003eskb = skb;\n \n@@ -5221,6 +5243,9 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,\n \t\thdr = (struct ieee80211_hdr *)rx-\u003eskb-\u003edata;\n \t}\n \n+\tstatus = IEEE80211_SKB_RXCB(rx-\u003eskb);\n+\tstatus-\u003elink_id = rx-\u003elink_id \u003c 0 ? 0 : rx-\u003elink_id;\n+\n \tif (ieee80211_is_s1g_beacon(hdr-\u003eframe_control)) {\n \t\tieee80211_invoke_rx_handlers(rx);\n \t\treturn true;\n@@ -5256,22 +5281,19 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,\n }\n \n static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,\n-\t\t\t\t       struct ieee80211_sta *pubsta,\n+\t\t\t\t       struct ieee80211_link_sta *link_pubsta,\n \t\t\t\t       struct sk_buff *skb,\n \t\t\t\t       struct list_head *list)\n {\n \tstruct ieee80211_local *local = hw_to_local(hw);\n-\tstruct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);\n \tstruct ieee80211_fast_rx *fast_rx;\n \tstruct ieee80211_rx_data rx;\n \tstruct sta_info *sta;\n-\tint link_id = -1;\n \n \tmemset(\u0026rx, 0, sizeof(rx));\n \trx.skb = skb;\n \trx.local = local;\n \trx.list = list;\n-\trx.link_id = -1;\n \n \tI802_DEBUG_INC(local-\u003edot11ReceivedFragmentCount);\n \n@@ -5279,23 +5301,15 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,\n \tif (skb-\u003elen \u003c sizeof(struct ethhdr))\n \t\tgoto drop;\n \n-\tif (!pubsta)\n+\tif (!link_pubsta)\n \t\tgoto drop;\n \n-\tif (status-\u003elink_valid)\n-\t\tlink_id = status-\u003elink_id;\n-\n-\t/*\n-\t * TODO: Should the frame be dropped if the right link_id is not\n-\t * available? Or may be it is fine in the current form to proceed with\n-\t * the frame processing because with frame being in 802.3 format,\n-\t * link_id is used only for stats purpose and updating the stats on\n-\t * the deflink is fine?\n-\t */\n-\tsta = container_of(pubsta, struct sta_info, sta);\n-\tif (!ieee80211_rx_data_set_sta(\u0026rx, sta, link_id))\n+\tsta = container_of(link_pubsta-\u003esta, struct sta_info, sta);\n+\tif (!ieee80211_rx_data_set_sta(\u0026rx, sta, link_pubsta-\u003elink_id))\n \t\tgoto drop;\n \n+\trx.link_id = link_pubsta-\u003elink_id;\n+\n \tfast_rx = rcu_dereference(rx.sta-\u003efast_rx);\n \tif (!fast_rx)\n \t\tgoto drop;\n@@ -5307,59 +5321,19 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,\n \tdev_kfree_skb(skb);\n }\n \n-static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx,\n-\t\t\t\t       struct sk_buff *skb, bool consume)\n+static bool ieee80211_rx_valid_freq(int freq, struct ieee80211_link_data *link)\n {\n-\tstruct link_sta_info *link_sta;\n-\tstruct ieee80211_hdr *hdr = (void *)skb-\u003edata;\n-\tstruct sta_info *sta;\n-\tint link_id = -1;\n-\n-\tif (ieee80211_is_s1g_beacon(hdr-\u003eframe_control)) {\n-\t\tif (!ieee80211_rx_data_set_sta(rx, NULL, -1))\n-\t\t\treturn false;\n-\n-\t\treturn ieee80211_prepare_and_rx_handle(rx, skb, consume);\n-\t}\n+\tstruct ieee80211_chanctx_conf *conf;\n \n-\t/*\n-\t * Look up link station first, in case there's a\n-\t * chance that they might have a link address that\n-\t * is identical to the MLD address, that way we'll\n-\t * have the link information if needed.\n-\t */\n-\tlink_sta = link_sta_info_get_bss(rx-\u003esdata, hdr-\u003eaddr2);\n-\tif (link_sta) {\n-\t\tsta = link_sta-\u003esta;\n-\t\tlink_id = link_sta-\u003elink_id;\n-\t} else {\n-\t\tstruct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);\n-\n-\t\tsta = sta_info_get_bss(rx-\u003esdata, hdr-\u003eaddr2);\n-\t\tif (status-\u003elink_valid) {\n-\t\t\tlink_id = status-\u003elink_id;\n-\t\t} else if (ieee80211_vif_is_mld(\u0026rx-\u003esdata-\u003evif) \u0026\u0026\n-\t\t\t   status-\u003efreq) {\n-\t\t\tstruct ieee80211_link_data *link;\n-\t\t\tstruct ieee80211_chanctx_conf *conf;\n-\n-\t\t\tfor_each_link_data_rcu(rx-\u003esdata, link) {\n-\t\t\t\tconf = rcu_dereference(link-\u003econf-\u003echanctx_conf);\n-\t\t\t\tif (!conf || !conf-\u003edef.chan)\n-\t\t\t\t\tcontinue;\n-\n-\t\t\t\tif (status-\u003efreq == conf-\u003edef.chan-\u003ecenter_freq) {\n-\t\t\t\t\tlink_id = link-\u003elink_id;\n-\t\t\t\t\tbreak;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t}\n-\t}\n+\tif (!freq || link-\u003esdata-\u003evif.type == NL80211_IFTYPE_NAN ||\n+\t    link-\u003esdata-\u003evif.type == NL80211_IFTYPE_NAN_DATA)\n+\t\treturn true;\n \n-\tif (!ieee80211_rx_data_set_sta(rx, sta, link_id))\n+\tconf = rcu_dereference(link-\u003econf-\u003echanctx_conf);\n+\tif (!conf || !conf-\u003edef.chan)\n \t\treturn false;\n \n-\treturn ieee80211_prepare_and_rx_handle(rx, skb, consume);\n+\treturn freq == conf-\u003edef.chan-\u003ecenter_freq;\n }\n \n /*\n@@ -5367,18 +5341,20 @@ static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx,\n  * be called with rcu_read_lock protection.\n  */\n static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,\n-\t\t\t\t\t struct ieee80211_sta *pubsta,\n+\t\t\t\t\t struct ieee80211_link_sta *link_pubsta,\n \t\t\t\t\t struct sk_buff *skb,\n \t\t\t\t\t struct list_head *list)\n {\n \tstruct ieee80211_local *local = hw_to_local(hw);\n-\tstruct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);\n \tstruct ieee80211_sub_if_data *sdata;\n+\tstruct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);\n \tstruct ieee80211_hdr *hdr;\n+\tstruct link_sta_info *link_sta;\n+\tstruct sta_info *sta;\n \t__le16 fc;\n \tstruct ieee80211_rx_data rx;\n-\tstruct ieee80211_sub_if_data *prev;\n \tstruct rhlist_head *tmp;\n+\tbool rx_data_pending;\n \tint err = 0;\n \n \tfc = ((struct ieee80211_hdr *)skb-\u003edata)-\u003eframe_control;\n@@ -5386,7 +5362,6 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,\n \trx.skb = skb;\n \trx.local = local;\n \trx.list = list;\n-\trx.link_id = -1;\n \n \tif (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc))\n \t\tI802_DEBUG_INC(local-\u003edot11ReceivedFragmentCount);\n@@ -5426,97 +5401,102 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,\n \t\t     ieee80211_is_s1g_beacon(hdr-\u003eframe_control)))\n \t\tieee80211_scan_rx(local, skb);\n \n+\t/*\n+\t * RX of a data frame should only happen to existing stations.\n+\t * It is therefore more efficient to use the one provided by the driver\n+\t * or search based on the station's address.\n+\t *\n+\t * Note we will fall through and handle a spurious data frame in the\n+\t * same way as a management frame.\n+\t */\n \tif (ieee80211_is_data(fc)) {\n-\t\tstruct sta_info *sta, *prev_sta;\n-\t\tint link_id = -1;\n-\n-\t\tif (status-\u003elink_valid)\n-\t\t\tlink_id = status-\u003elink_id;\n-\n-\t\tif (pubsta) {\n-\t\t\tsta = container_of(pubsta, struct sta_info, sta);\n-\t\t\tif (!ieee80211_rx_data_set_sta(\u0026rx, sta, link_id))\n-\t\t\t\tgoto out;\n-\n-\t\t\t/*\n-\t\t\t * In MLO connection, fetch the link_id using addr2\n-\t\t\t * when the driver does not pass link_id in status.\n-\t\t\t * When the address translation is already performed by\n-\t\t\t * driver/hw, the valid link_id must be passed in\n-\t\t\t * status.\n-\t\t\t */\n-\n-\t\t\tif (!status-\u003elink_valid \u0026\u0026 pubsta-\u003emlo) {\n-\t\t\t\tstruct link_sta_info *link_sta;\n-\n-\t\t\t\tlink_sta = link_sta_info_get_bss(rx.sdata,\n-\t\t\t\t\t\t\t\t hdr-\u003eaddr2);\n-\t\t\t\tif (!link_sta)\n-\t\t\t\t\tgoto out;\n-\n-\t\t\t\tif (!ieee80211_rx_data_set_link(\u0026rx,\n-\t\t\t\t\t\t\t\tlink_sta-\u003elink_id))\n-\t\t\t\t\tgoto out;\n-\t\t\t}\n-\n-\t\t\tif (ieee80211_prepare_and_rx_handle(\u0026rx, skb, true))\n+\t\tif (link_pubsta) {\n+\t\t\tsta = container_of(link_pubsta-\u003esta, struct sta_info,\n+\t\t\t\t\t   sta);\n+\t\t\tlink_sta = rcu_dereference(sta-\u003elink[link_pubsta-\u003elink_id]);\n+\n+\t\t\trx.sdata = sta-\u003esdata;\n+\t\t\tif (ieee80211_rx_data_set_link_sta(\u0026rx, link_sta) \u0026\u0026\n+\t\t\t    ieee80211_prepare_and_rx_handle(\u0026rx, skb, true))\n \t\t\t\treturn;\n+\n \t\t\tgoto out;\n \t\t}\n \n-\t\tprev_sta = NULL;\n+\t\trx_data_pending = false;\n \n+\t\t/* Search for stations on non-MLD interfaces */\n \t\tfor_each_sta_info(local, hdr-\u003eaddr2, sta, tmp) {\n-\t\t\tif (!prev_sta) {\n-\t\t\t\tprev_sta = sta;\n-\t\t\t\tcontinue;\n-\t\t\t}\n+\t\t\tstruct ieee80211_link_data *link;\n \n-\t\t\trx.sdata = prev_sta-\u003esdata;\n-\t\t\tif (!status-\u003elink_valid \u0026\u0026 prev_sta-\u003esta.mlo) {\n-\t\t\t\tstruct link_sta_info *link_sta;\n+\t\t\tif (ieee80211_vif_is_mld(\u0026sta-\u003esdata-\u003evif))\n+\t\t\t\tcontinue;\n \n-\t\t\t\tlink_sta = link_sta_info_get_bss(rx.sdata,\n-\t\t\t\t\t\t\t\t hdr-\u003eaddr2);\n-\t\t\t\tif (!link_sta)\n-\t\t\t\t\tcontinue;\n+\t\t\tlink = \u0026sta-\u003esdata-\u003edeflink;\n+\t\t\tif (!ieee80211_rx_valid_freq(status-\u003efreq, link))\n+\t\t\t\tcontinue;\n \n-\t\t\t\tlink_id = link_sta-\u003elink_id;\n+\t\t\tif (rx_data_pending) {\n+\t\t\t\tieee80211_prepare_and_rx_handle(\u0026rx, skb,\n+\t\t\t\t\t\t\t\tfalse);\n+\t\t\t\trx_data_pending = false;\n \t\t\t}\n \n-\t\t\tif (!ieee80211_rx_data_set_sta(\u0026rx, prev_sta, link_id))\n-\t\t\t\tgoto out;\n-\n-\t\t\tieee80211_prepare_and_rx_handle(\u0026rx, skb, false);\n+\t\t\trx.sdata = sta-\u003esdata;\n+\t\t\tif (!ieee80211_rx_data_set_link_sta(\u0026rx, \u0026sta-\u003edeflink))\n+\t\t\t\tcontinue;\n \n-\t\t\tprev_sta = sta;\n+\t\t\trx_data_pending = true;\n \t\t}\n \n-\t\tif (prev_sta) {\n-\t\t\trx.sdata = prev_sta-\u003esdata;\n-\t\t\tif (!status-\u003elink_valid \u0026\u0026 prev_sta-\u003esta.mlo) {\n-\t\t\t\tstruct link_sta_info *link_sta;\n+\t\t/* And search stations on MLD interfaces */\n+\t\tfor_each_link_sta_info(local, hdr-\u003eaddr2, link_sta, tmp) {\n+\t\t\tstruct ieee80211_link_data *link;\n \n-\t\t\t\tlink_sta = link_sta_info_get_bss(rx.sdata,\n-\t\t\t\t\t\t\t\t hdr-\u003eaddr2);\n-\t\t\t\tif (!link_sta)\n-\t\t\t\t\tgoto out;\n+\t\t\tsta = link_sta-\u003esta;\n+\t\t\tsdata =\tsta-\u003esdata;\n+\t\t\tlink = rcu_dereference(sdata-\u003elink[link_sta-\u003elink_id]);\n \n-\t\t\t\tlink_id = link_sta-\u003elink_id;\n+\t\t\tif (!ieee80211_rx_valid_freq(status-\u003efreq, link))\n+\t\t\t\tcontinue;\n+\n+\t\t\tif (rx_data_pending) {\n+\t\t\t\tieee80211_prepare_and_rx_handle(\u0026rx, skb,\n+\t\t\t\t\t\t\t\tfalse);\n+\t\t\t\trx_data_pending = false;\n \t\t\t}\n \n-\t\t\tif (!ieee80211_rx_data_set_sta(\u0026rx, prev_sta, link_id))\n-\t\t\t\tgoto out;\n+\t\t\trx.sdata = sta-\u003esdata;\n+\t\t\tif (!ieee80211_rx_data_set_link_sta(\u0026rx, link_sta))\n+\t\t\t\tcontinue;\n+\n+\t\t\trx_data_pending = true;\n+\t\t}\n \n+\t\tif (rx_data_pending) {\n \t\t\tif (ieee80211_prepare_and_rx_handle(\u0026rx, skb, true))\n \t\t\t\treturn;\n+\n \t\t\tgoto out;\n \t\t}\n+\n+\t\t/* fall through, e.g. for spurious frame notification */\n \t}\n \n-\tprev = NULL;\n+\t/*\n+\t * This is a management frame (or a data frame without a station) and\n+\t * it will be delivered independent of whether a station exists,\n+\t * so iterate the interfaces.\n+\t */\n+\trx_data_pending = false;\n+\n+\t/* We expect the driver to provide frequency information */\n+\tif (WARN_ON_ONCE(!local-\u003eemulate_chanctx \u0026\u0026 !status-\u003efreq))\n+\t\tgoto out;\n \n \tlist_for_each_entry_rcu(sdata, \u0026local-\u003einterfaces, list) {\n+\t\tstruct ieee80211_link_data *link = NULL;\n+\n \t\tif (!ieee80211_sdata_running(sdata))\n \t\t\tcontinue;\n \n@@ -5524,30 +5504,85 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,\n \t\t    sdata-\u003evif.type == NL80211_IFTYPE_AP_VLAN)\n \t\t\tcontinue;\n \n+\t\tlink_sta = NULL;\n+\t\tlink = NULL;\n+\n+\t\t/* Try to resolve the station */\n+\t\tif (!ieee80211_vif_is_mld(\u0026sdata-\u003evif)) {\n+\t\t\tsta = sta_info_get_bss(sdata, hdr-\u003eaddr2);\n+\n+\t\t\tif (sta) {\n+\t\t\t\tlink_sta = \u0026sta-\u003edeflink;\n+\t\t\t\tlink = \u0026sdata-\u003edeflink;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tlink_sta = link_sta_info_get_bss(sdata, hdr-\u003eaddr2);\n+\n+\t\t\tif (link_sta)\n+\t\t\t\tlink = rcu_dereference(sdata-\u003elink[link_sta-\u003elink_id]);\n+\t\t}\n+\n \t\t/*\n-\t\t * frame is destined for this interface, but if it's\n-\t\t * not also for the previous one we handle that after\n-\t\t * the loop to avoid copying the SKB once too much\n+\t\t * If we found a STA and it has a valid link information, then\n+\t\t * RX using the station.\n \t\t */\n+\t\tif (link_sta \u0026\u0026 link \u0026\u0026\n+\t\t    ieee80211_rx_valid_freq(status-\u003efreq, link)) {\n+\t\t\tif (rx_data_pending) {\n+\t\t\t\tieee80211_prepare_and_rx_handle(\u0026rx, skb, false);\n+\t\t\t\trx_data_pending = false;\n+\t\t\t}\n+\n+\t\t\t/* No valid_links check as we need to RX beacons */\n+\n+\t\t\trx.sdata = sdata;\n+\t\t\tif (ieee80211_rx_data_set_link_sta(\u0026rx, link_sta))\n+\t\t\t\trx_data_pending = true;\n \n-\t\tif (!prev) {\n-\t\t\tprev = sdata;\n \t\t\tcontinue;\n \t\t}\n \n-\t\trx.sdata = prev;\n-\t\tieee80211_rx_for_interface(\u0026rx, skb, false);\n+\t\t/* No station, try to resolve the link and RX */\n+\t\tif (ieee80211_vif_is_mld(\u0026sdata-\u003evif)) {\n+\t\t\tstruct ieee80211_chanctx_conf *conf;\n+\t\t\tbool found = false;\n \n-\t\tprev = sdata;\n-\t}\n+\t\t\tfor_each_link_data_rcu(sdata, link) {\n+\t\t\t\tconf = rcu_dereference(link-\u003econf-\u003echanctx_conf);\n+\t\t\t\tif (!conf || !conf-\u003edef.chan)\n+\t\t\t\t\tcontinue;\n \n-\tif (prev) {\n-\t\trx.sdata = prev;\n+\t\t\t\tif (status-\u003efreq == conf-\u003edef.chan-\u003ecenter_freq) {\n+\t\t\t\t\tfound = true;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n \n-\t\tif (ieee80211_rx_for_interface(\u0026rx, skb, true))\n-\t\t\treturn;\n+\t\t\tif (!found)\n+\t\t\t\tlink = \u0026sdata-\u003edeflink;\n+\t\t} else {\n+\t\t\tlink = \u0026sdata-\u003edeflink;\n+\t\t}\n+\n+\t\tif (rx_data_pending) {\n+\t\t\tieee80211_prepare_and_rx_handle(\u0026rx, skb, false);\n+\t\t\trx_data_pending = false;\n+\t\t}\n+\n+\t\trx.sdata = sdata;\n+\t\trx.local = sdata-\u003elocal;\n+\t\trx.link = link;\n+\t\trx.link_id = link-\u003elink_id;\n+\t\trx.sta = NULL;\n+\t\trx.link_sta = NULL;\n+\n+\t\trx_data_pending = true;\n \t}\n \n+\tif (rx_data_pending \u0026\u0026\n+\t    ieee80211_prepare_and_rx_handle(\u0026rx, skb, true))\n+\t\treturn;\n+\n  out:\n \tdev_kfree_skb(skb);\n }\n@@ -5556,7 +5591,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,\n  * This is the receive path handler. It is called by a low level driver when an\n  * 802.11 MPDU is received from the hardware.\n  */\n-void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,\n+void ieee80211_rx_list(struct ieee80211_hw *hw,\n+\t\t       struct ieee80211_link_sta *link_pubsta,\n \t\t       struct sk_buff *skb, struct list_head *list)\n {\n \tstruct ieee80211_local *local = hw_to_local(hw);\n@@ -5694,9 +5730,6 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,\n \t\t}\n \t}\n \n-\tif (WARN_ON_ONCE(status-\u003elink_id \u003e= IEEE80211_LINK_UNSPECIFIED))\n-\t\tgoto drop;\n-\n \tstatus-\u003erx_flags = 0;\n \n \tkcov_remote_start_common(skb_get_kcov_handle(skb));\n@@ -5715,9 +5748,10 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,\n \t\t\tieee80211_tpt_led_trig_rx(local, skb-\u003elen);\n \n \t\tif (status-\u003eflag \u0026 RX_FLAG_8023)\n-\t\t\t__ieee80211_rx_handle_8023(hw, pubsta, skb, list);\n+\t\t\t__ieee80211_rx_handle_8023(hw, link_pubsta, skb, list);\n \t\telse\n-\t\t\t__ieee80211_rx_handle_packet(hw, pubsta, skb, list);\n+\t\t\t__ieee80211_rx_handle_packet(hw, link_pubsta, skb,\n+\t\t\t\t\t\t     list);\n \t}\n \n \tkcov_remote_stop();\n@@ -5727,7 +5761,8 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,\n }\n EXPORT_SYMBOL(ieee80211_rx_list);\n \n-void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,\n+void ieee80211_rx_napi(struct ieee80211_hw *hw,\n+\t\t       struct ieee80211_link_sta *link_pubsta,\n \t\t       struct sk_buff *skb, struct napi_struct *napi)\n {\n \tstruct sk_buff *tmp;\n@@ -5740,7 +5775,7 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,\n \t * receive processing\n \t */\n \trcu_read_lock();\n-\tieee80211_rx_list(hw, pubsta, skb, \u0026list);\n+\tieee80211_rx_list(hw, link_pubsta, skb, \u0026list);\n \trcu_read_unlock();\n \n \tif (!napi) {\ndiff --git a/net/mac80211/scan.c b/net/mac80211/scan.c\nindex eeff230bd909f..324c2b6d7ce1b 100644\n--- a/net/mac80211/scan.c\n+++ b/net/mac80211/scan.c\n@@ -9,7 +9,7 @@\n  * Copyright 2007, Michael Wu \u003cflamingice@sourmilk.net\u003e\n  * Copyright 2013-2015  Intel Mobile Communications GmbH\n  * Copyright 2016-2017  Intel Deutschland GmbH\n- * Copyright (C) 2018-2025 Intel Corporation\n+ * Copyright (C) 2018-2026 Intel Corporation\n  */\n \n #include \u003clinux/if_arp.h\u003e\n@@ -204,12 +204,10 @@ ieee80211_bss_info_update(struct ieee80211_local *local,\n \t\t * an indication on which of the links the frame was received\n \t\t */\n \t\tif (ieee80211_vif_is_mld(\u0026scan_sdata-\u003evif)) {\n-\t\t\tif (rx_status-\u003elink_valid) {\n-\t\t\t\ts8 link_id = rx_status-\u003elink_id;\n+\t\t\ts8 link_id = rx_status-\u003elink_id;\n \n-\t\t\t\tlink_conf =\n-\t\t\t\t\trcu_dereference(scan_sdata-\u003evif.link_conf[link_id]);\n-\t\t\t}\n+\t\t\tlink_conf =\n+\t\t\t\trcu_dereference(scan_sdata-\u003evif.link_conf[link_id]);\n \t\t} else {\n \t\t\tlink_conf = \u0026scan_sdata-\u003evif.bss_conf;\n \t\t}\ndiff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c\nindex 4f4cae9fd274c..7ec33885a3e18 100644\n--- a/net/mac80211/tdls.c\n+++ b/net/mac80211/tdls.c\n@@ -1066,7 +1066,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,\n \t}\n \n \tif (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {\n-\t\tieee80211_tx_skb_tid(sdata, skb, 7, link_id);\n+\t\tieee80211_tx_skb_tid(sdata, skb, sta, 7, link_id);\n \t\treturn 0;\n \t}\n \ndiff --git a/net/mac80211/tx.c b/net/mac80211/tx.c\nindex 06c50183f064d..73affc540f0c7 100644\n--- a/net/mac80211/tx.c\n+++ b/net/mac80211/tx.c\n@@ -6448,7 +6448,8 @@ void ieee80211_unreserve_tid(struct ieee80211_sta *pubsta, u8 tid)\n EXPORT_SYMBOL(ieee80211_unreserve_tid);\n \n void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,\n-\t\t\t\t struct sk_buff *skb, int tid, int link_id,\n+\t\t\t\t struct sk_buff *skb, struct sta_info *sta,\n+\t\t\t\t int tid, int link_id,\n \t\t\t\t enum nl80211_band band)\n {\n \tconst struct ieee80211_hdr *hdr = (void *)skb-\u003edata;\n@@ -6500,12 +6501,13 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,\n \t */\n \tlocal_bh_disable();\n \tIEEE80211_SKB_CB(skb)-\u003eband = band;\n-\tieee80211_xmit(sdata, NULL, skb);\n+\tieee80211_xmit(sdata, sta, skb);\n \tlocal_bh_enable();\n }\n \n void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,\n-\t\t\t  struct sk_buff *skb, int tid, int link_id)\n+\t\t\t  struct sk_buff *skb, struct sta_info *sta,\n+\t\t\t  int tid, int link_id)\n {\n \tstruct ieee80211_chanctx_conf *chanctx_conf;\n \tenum nl80211_band band;\n@@ -6531,7 +6533,7 @@ void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,\n \t\tband = 0;\n \t}\n \n-\t__ieee80211_tx_skb_tid_band(sdata, skb, tid, link_id, band);\n+\t__ieee80211_tx_skb_tid_band(sdata, skb, sta, tid, link_id, band);\n \trcu_read_unlock();\n }\n \ndiff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c\nindex b081eeaf70162..e77a4c81d0317 100644\n--- a/net/wireless/nl80211.c\n+++ b/net/wireless/nl80211.c\n@@ -1097,6 +1097,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {\n \t[NL80211_ATTR_NPCA_PUNCT_BITMAP] =\n \t\tNLA_POLICY_FULL_RANGE(NLA_U32, \u0026nl80211_punct_bitmap_range),\n \t[NL80211_ATTR_STA_DUMP_LINK_STATS] = { .type = NLA_FLAG },\n+\t[NL80211_ATTR_FRAME_NO_STA] = { .type = NLA_FLAG },\n };\n \n /* policy for the key attributes */\n@@ -14873,6 +14874,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)\n \t    !(wdev-\u003evalid_links \u0026 BIT(params.link_id)))\n \t\treturn -EINVAL;\n \n+\tparams.no_sta =\n+\t\tnla_get_flag(info-\u003eattrs[NL80211_ATTR_FRAME_NO_STA]);\n+\n \tparams.buf = nla_data(info-\u003eattrs[NL80211_ATTR_FRAME]);\n \tparams.len = nla_len(info-\u003eattrs[NL80211_ATTR_FRAME]);\n \n@@ -22042,7 +22046,9 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,\n \t    (info-\u003eack_tstamp \u0026\u0026 nla_put_u64_64bit(msg,\n \t\t\t\t\t\t   NL80211_ATTR_TX_HW_TIMESTAMP,\n \t\t\t\t\t\t   info-\u003eack_tstamp,\n-\t\t\t\t\t\t   NL80211_ATTR_PAD)))\n+\t\t\t\t\t\t   NL80211_ATTR_PAD)) ||\n+\t    (info-\u003eno_sta \u0026\u0026\n+\t     nla_put_flag(msg, NL80211_ATTR_FRAME_NO_STA)))\n \t\tgoto nla_put_failure;\n \n \tgenlmsg_end(msg, hdr);\n"
}

2/1 2026/09/15 18:14 agent patch-evaluator
1m Model:
core

Args:
null

Results:
{
  "EnableConfigs": [
    "CFG80211",
    "MAC80211",
    "MAC80211_HWSIM"
  ],
  "FocusSymbols": [
    "nl80211_tx_mgmt",
    "nl80211_send_mgmt",
    "ieee80211_mgmt_tx",
    "ieee80211_tx_skb_tid",
    "__ieee80211_tx_skb_tid_band",
    "ieee80211_rx_list",
    "ieee80211_rx_napi",
    "__ieee80211_rx_handle_packet",
    "__ieee80211_rx_handle_8023",
    "ieee80211_rx_handlers",
    "ieee80211_rx_data_set_link_sta"
  ],
  "Reasoning": "The patch series refactors Multi-Link Operation (MLO) frame transmission and reception in the core mac80211 networking stack and the nl80211 netlink interface, as well as the virtual mac80211_hwsim driver. Key modifications include removing link_valid from rx_status, passing link_sta instead of sta to RX functions, restructuring station and link lookups in __ieee80211_rx_handle_packet and __ieee80211_rx_handle_8023, updating cross-link A-MPDU reordering in ieee80211_rx_handlers, and introducing NL80211_ATTR_FRAME_NO_STA handling in nl80211_tx_mgmt. These code paths are fully reachable in virtualized QEMU environments using mac80211_hwsim and unprivileged user namespaces via nl80211 netlink commands.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 05665aae504027f5bfd53117ebba1243a0c0f83c
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Sep 15 18:14:34 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index 33425707c0842..fbb3d18f55ddd 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -2500,7 +2500,7 @@ static void ath11k_dp_rx_deliver_msdu(struct ath11k *ar, struct napi_struct *nap
 	    !(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED))
 		rx_status->flag |= RX_FLAG_8023;
 
-	ieee80211_rx_napi(ar->hw, pubsta, msdu, napi);
+	ieee80211_rx_napi(ar->hw, pubsta ? &pubsta->deflink : NULL, msdu, napi);
 }
 
 static bool ath11k_dp_rx_check_nwifi_hdr_len_valid(struct ath11k_base *ab,
diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c
index 7d5be77b081f8..1faf1896d0ab1 100644
--- a/drivers/net/wireless/ath/ath12k/dp_mon.c
+++ b/drivers/net/wireless/ath/ath12k/dp_mon.c
@@ -503,8 +503,6 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
 	struct ieee80211_rx_status *rx_status;
 	struct ieee80211_radiotap_he *he = NULL;
 
-	status->link_valid = 0;
-
 	if ((status->encoding == RX_ENC_HE) && !(status->flag & RX_FLAG_RADIOTAP_HE) &&
 	    !(status->flag & RX_FLAG_SKIP_MONITOR)) {
 		he = skb_push(msdu, sizeof(known));
diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c
index 8fa0e90b45316..41ac0a4072aeb 100644
--- a/drivers/net/wireless/ath/ath12k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath12k/dp_rx.c
@@ -1375,6 +1375,7 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
 	struct ath12k_dp *dp = dp_pdev->dp;
 	struct ieee80211_rx_status *rx_status;
 	struct ieee80211_sta *pubsta;
+	struct ieee80211_link_sta *link_pubsta = NULL;
 	struct ath12k_dp_peer *peer;
 	struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
 	struct ieee80211_rx_status *status = rx_info->rx_status;
@@ -1384,9 +1385,19 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
 
 	pubsta = peer ? peer->sta : NULL;
 
-	status->link_valid = 0;
-	if (pubsta && pubsta->valid_links)
-		ath12k_hw_set_rx_link_id(dp->hw_params, peer, rxcb, status);
+	if (pubsta) {
+		if (pubsta->valid_links) {
+			int link_id =
+				ath12k_hw_get_rx_link_id(dp->hw_params, peer,
+							 rxcb, status);
+
+			if (link_id >= 0)
+				link_pubsta =
+					rcu_dereference(pubsta->link[link_id]);
+		} else {
+			link_pubsta = &pubsta->deflink;
+		}
+	}
 
 	ath12k_dbg(dp->ab, ATH12K_DBG_DATA,
 		   "rx skb %p len %u peer %pM %d %s sn %u %s%s%s%s%s%s%s%s%s%s rate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n",
@@ -1422,7 +1433,8 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
 
 	/* TODO: trace rx packet */
 
-	ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), pubsta, msdu, napi);
+	ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), link_pubsta, msdu,
+			  napi);
 }
 EXPORT_SYMBOL(ath12k_dp_rx_deliver_msdu);
 
diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h
index 011bb22e21c7b..e790de5737db4 100644
--- a/drivers/net/wireless/ath/ath12k/hw.h
+++ b/drivers/net/wireless/ath/ath12k/hw.h
@@ -249,9 +249,9 @@ struct ath12k_hw_ops {
 	bool (*dp_srng_is_tx_comp_ring)(int ring_num);
 	bool (*is_frame_link_agnostic)(struct ath12k_link_vif *arvif,
 				       struct ieee80211_mgmt *mgmt);
-	void (*set_rx_link_id)(struct ath12k_dp_peer *dp_peer,
-			       struct ath12k_skb_rxcb *rxcb,
-			       struct ieee80211_rx_status *status);
+	int (*get_rx_link_id)(struct ath12k_dp_peer *dp_peer,
+			      struct ath12k_skb_rxcb *rxcb,
+			      struct ieee80211_rx_status *status);
 };
 
 static inline
@@ -282,13 +282,15 @@ static inline int ath12k_hw_mac_id_to_srng_id(const struct ath12k_hw_params *hw,
 	return 0;
 }
 
-static inline void ath12k_hw_set_rx_link_id(const struct ath12k_hw_params *hw,
-					    struct ath12k_dp_peer *dp_peer,
-					    struct ath12k_skb_rxcb *rxcb,
-					    struct ieee80211_rx_status *status)
+static inline int ath12k_hw_get_rx_link_id(const struct ath12k_hw_params *hw,
+					   struct ath12k_dp_peer *dp_peer,
+					   struct ath12k_skb_rxcb *rxcb,
+					   struct ieee80211_rx_status *status)
 {
-	if (hw->hw_ops->set_rx_link_id)
-		hw->hw_ops->set_rx_link_id(dp_peer, rxcb, status);
+	if (hw->hw_ops->get_rx_link_id)
+		return hw->hw_ops->get_rx_link_id(dp_peer, rxcb, status);
+
+	return -1;
 }
 
 struct ath12k_fw_ie {
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c
index 95d87dd67872f..8b722ee0ad8fa 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c
@@ -2317,17 +2317,16 @@ ath12k_wifi7_dp_rxdesc_mpdu_valid(struct ath12k_base *ab,
 	return tlv_tag == HAL_RX_MPDU_START;
 }
 
-void
-ath12k_wifi7_dp_rx_set_link_id_qcn9274(struct ath12k_dp_peer *dp_peer,
+int
+ath12k_wifi7_dp_rx_get_link_id_qcn9274(struct ath12k_dp_peer *dp_peer,
 				       struct ath12k_skb_rxcb *rxcb,
 				       struct ieee80211_rx_status *status)
 {
-	status->link_valid = 1;
-	status->link_id = dp_peer->hw_links[rxcb->hw_link_id];
+	return dp_peer->hw_links[rxcb->hw_link_id];
 }
 
-void
-ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,
+int
+ath12k_wifi7_dp_rx_get_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,
 				       struct ath12k_skb_rxcb *rxcb,
 				       struct ieee80211_rx_status *status)
 {
@@ -2341,10 +2340,9 @@ ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,
 	links_map = READ_ONCE(dp_peer->link_peers_map);
 	for_each_set_bit(i, &links_map, ATH12K_NUM_MAX_LINKS) {
 		link_peer = rcu_dereference(dp_peer->link_peers[i]);
-		if (link_peer && link_peer->peer_id == rxcb->peer_id) {
-			status->link_valid = 1;
-			status->link_id = link_peer->link_id;
-			return;
-		}
+		if (link_peer && link_peer->peer_id == rxcb->peer_id)
+			return link_peer->link_id;
 	}
+
+	return -1;
 }
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h
index 1d3a4788a2dd9..d68489ff7b037 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h
+++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h
@@ -57,10 +57,10 @@ ath12k_wifi7_dp_rxdesc_mpdu_valid(struct ath12k_base *ab,
 				  struct hal_rx_desc *rx_desc);
 int ath12k_wifi7_dp_rx_tid_delete_handler(struct ath12k_base *ab,
 					  struct ath12k_dp_rx_tid_rxq *rx_tid);
-void ath12k_wifi7_dp_rx_set_link_id_qcn9274(struct ath12k_dp_peer *dp_peer,
-					    struct ath12k_skb_rxcb *rxcb,
-					    struct ieee80211_rx_status *status);
-void ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,
-					    struct ath12k_skb_rxcb *rxcb,
-					    struct ieee80211_rx_status *status);
+int ath12k_wifi7_dp_rx_get_link_id_qcn9274(struct ath12k_dp_peer *dp_peer,
+					   struct ath12k_skb_rxcb *rxcb,
+					   struct ieee80211_rx_status *status);
+int ath12k_wifi7_dp_rx_get_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,
+					   struct ath12k_skb_rxcb *rxcb,
+					   struct ieee80211_rx_status *status);
 #endif
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c
index 7b86f324b2e68..4ded807763b61 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c
@@ -158,7 +158,7 @@ static const struct ath12k_hw_ops qcn9274_ops = {
 	.get_ring_selector = ath12k_wifi7_hw_get_ring_selector_qcn9274,
 	.dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_qcn9274,
 	.is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_qcn9274,
-	.set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_qcn9274,
+	.get_rx_link_id = ath12k_wifi7_dp_rx_get_link_id_qcn9274,
 };
 
 static const struct ath12k_hw_ops wcn7850_ops = {
@@ -169,7 +169,7 @@ static const struct ath12k_hw_ops wcn7850_ops = {
 	.get_ring_selector = ath12k_wifi7_hw_get_ring_selector_wcn7850,
 	.dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_wcn7850,
 	.is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_wcn7850,
-	.set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_wcn7850,
+	.get_rx_link_id = ath12k_wifi7_dp_rx_get_link_id_wcn7850,
 };
 
 static const struct ath12k_hw_ops qcc2072_ops = {
@@ -180,7 +180,7 @@ static const struct ath12k_hw_ops qcc2072_ops = {
 	.get_ring_selector = ath12k_wifi7_hw_get_ring_selector_wcn7850,
 	.dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_wcn7850,
 	.is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_wcn7850,
-	.set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_wcn7850,
+	.get_rx_link_id = ath12k_wifi7_dp_rx_get_link_id_wcn7850,
 };
 
 #define ATH12K_TX_RING_MASK_0 0x1
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index d5160af60e00d..7e3655966f02d 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -7271,7 +7271,6 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb)
 	struct ath12k_wmi_mgmt_rx_arg rx_ev = {};
 	struct ath12k *ar;
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-	struct ieee80211_sta *pubsta = NULL;
 	struct ath12k_dp_link_peer *peer;
 	struct ieee80211_hdr *hdr;
 	bool is_4addr_null_pkt;
@@ -7373,11 +7372,6 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb)
 			dev_kfree_skb(skb);
 			goto exit;
 		}
-		pubsta = peer->sta;
-		if (pubsta && pubsta->valid_links) {
-			status->link_valid = 1;
-			status->link_id = peer->link_id;
-		}
 		spin_unlock_bh(&dp->dp_lock);
 		goto send_rx;
 	}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/agg.c
index 96102d2af2cf9..c45c47337509e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/agg.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c
@@ -7,7 +7,8 @@
 #include "hcmd.h"
 
 static void
-iwl_mld_reorder_release_frames(struct iwl_mld *mld, struct ieee80211_sta *sta,
+iwl_mld_reorder_release_frames(struct iwl_mld *mld,
+			       struct ieee80211_link_sta *link_sta,
 			       struct napi_struct *napi,
 			       struct iwl_mld_baid_data *baid_data,
 			       struct iwl_mld_reorder_buffer *reorder_buf,
@@ -32,7 +33,7 @@ iwl_mld_reorder_release_frames(struct iwl_mld *mld, struct ieee80211_sta *sta,
 		while ((skb = __skb_dequeue(skb_list))) {
 			iwl_mld_pass_packet_to_mac80211(mld, napi, skb,
 							reorder_buf->queue,
-							sta);
+							link_sta);
 			reorder_buf->num_stored--;
 		}
 	}
@@ -74,7 +75,7 @@ static void iwl_mld_release_frames_from_notif(struct iwl_mld *mld,
 
 	reorder_buf = &ba_data->reorder_buf[queue];
 
-	iwl_mld_reorder_release_frames(mld, link_sta->sta, napi, ba_data,
+	iwl_mld_reorder_release_frames(mld, link_sta, napi, ba_data,
 				       reorder_buf, nssn);
 out_unlock:
 	rcu_read_unlock();
@@ -180,7 +181,7 @@ void iwl_mld_del_ba(struct iwl_mld *mld, int queue,
 	reorder_buf = &ba_data->reorder_buf[queue];
 
 	/* release all frames that are in the reorder buffer to the stack */
-	iwl_mld_reorder_release_frames(mld, link_sta->sta, NULL,
+	iwl_mld_reorder_release_frames(mld, link_sta, NULL,
 				       ba_data, reorder_buf,
 				       ieee80211_sn_add(reorder_buf->head_sn,
 							ba_data->buf_size));
@@ -193,7 +194,7 @@ void iwl_mld_del_ba(struct iwl_mld *mld, int queue,
  */
 enum iwl_mld_reorder_result
 iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
-		int queue, struct ieee80211_sta *sta,
+		int queue, struct ieee80211_link_sta *link_sta,
 		struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc)
 {
 	struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb);
@@ -228,12 +229,12 @@ iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
 		return IWL_MLD_PASS_SKB;
 
 	/* no sta yet */
-	if (IWL_FW_CHECK(mld, !sta,
+	if (IWL_FW_CHECK(mld, !link_sta,
 			 "Got valid BAID without a valid station assigned - %d\n",
 			 baid))
 		return IWL_MLD_PASS_SKB;
 
-	mld_sta = iwl_mld_sta_from_mac80211(sta);
+	mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
 
 	/* not a data packet */
 	if (!ieee80211_is_data_qos(hdr->frame_control) ||
@@ -324,7 +325,7 @@ iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
 	 * will be released when the frame release notification arrives.
 	 */
 	if (!amsdu || last_subframe)
-		iwl_mld_reorder_release_frames(mld, sta, napi, baid_data,
+		iwl_mld_reorder_release_frames(mld, link_sta, napi, baid_data,
 					       buffer, nssn);
 	else if (buffer->num_stored == 1)
 		buffer->head_sn = nssn;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.h b/drivers/net/wireless/intel/iwlwifi/mld/agg.h
index 651c80d1c7cda..c6cd5fa219bed 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/agg.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.h
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
 /*
- * Copyright (C) 2024 Intel Corporation
+ * Copyright (C) 2024, 2026 Intel Corporation
  */
 #ifndef __iwl_agg_h__
 #define __iwl_agg_h__
@@ -106,7 +106,7 @@ int iwl_mld_ampdu_rx_stop(struct iwl_mld *mld, struct ieee80211_sta *sta,
 
 enum iwl_mld_reorder_result
 iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
-		int queue, struct ieee80211_sta *sta,
+		int queue, struct ieee80211_link_sta *link_sta,
 		struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc);
 
 void iwl_mld_handle_frame_release_notif(struct iwl_mld *mld,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
index 269439d789f46..02bdf6ccbcdc9 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
@@ -47,7 +47,8 @@ iwl_mld_fill_phy_data_from_mpdu(struct iwl_mld *mld,
 }
 
 static inline int iwl_mld_check_pn(struct iwl_mld *mld, struct sk_buff *skb,
-				   int queue, struct ieee80211_sta *sta)
+				   int queue,
+				   struct ieee80211_link_sta *link_sta)
 {
 	struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb);
 	struct ieee80211_rx_status *stats = IEEE80211_SKB_RXCB(skb);
@@ -71,13 +72,13 @@ static inline int iwl_mld_check_pn(struct iwl_mld *mld, struct sk_buff *skb,
 		return 0;
 
 	/* if we are here - this for sure is either CCMP or GCMP */
-	if (!sta) {
+	if (!link_sta) {
 		IWL_DEBUG_DROP(mld,
 			       "expected hw-decrypted unicast frame for station\n");
 		return -1;
 	}
 
-	mld_sta = iwl_mld_sta_from_mac80211(sta);
+	mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
 
 	extiv = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control);
 	keyidx = extiv[3] >> 6;
@@ -119,17 +120,17 @@ static inline int iwl_mld_check_pn(struct iwl_mld *mld, struct sk_buff *skb,
 void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
 				     struct napi_struct *napi,
 				     struct sk_buff *skb, int queue,
-				     struct ieee80211_sta *sta)
+				     struct ieee80211_link_sta *link_sta)
 {
 	KUNIT_STATIC_STUB_REDIRECT(iwl_mld_pass_packet_to_mac80211,
-				   mld, napi, skb, queue, sta);
+				   mld, napi, skb, queue, link_sta);
 
-	if (unlikely(iwl_mld_check_pn(mld, skb, queue, sta))) {
+	if (unlikely(iwl_mld_check_pn(mld, skb, queue, link_sta))) {
 		kfree_skb(skb);
 		return;
 	}
 
-	ieee80211_rx_napi(mld->hw, sta, skb, napi);
+	ieee80211_rx_napi(mld->hw, link_sta, skb, napi);
 }
 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_pass_packet_to_mac80211);
 
@@ -1728,7 +1729,7 @@ static void iwl_mld_update_last_rx_timestamp(struct iwl_mld *mld, u8 baid)
  * Sets *drop to true if the packet should be dropped.
  * Returns the station if found, or NULL otherwise.
  */
-static struct ieee80211_sta *
+static struct ieee80211_link_sta *
 iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr,
 		    struct sk_buff *skb,
 		    const struct iwl_rx_mpdu_desc *mpdu_desc,
@@ -1764,11 +1765,6 @@ iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr,
 
 	rx_status = IEEE80211_SKB_RXCB(skb);
 
-	if (link_sta && sta->valid_links) {
-		rx_status->link_valid = true;
-		rx_status->link_id = link_sta->link_id;
-	}
-
 	/* fill checksum */
 	if (ieee80211_is_data(hdr->frame_control) &&
 	    pkt->len_n_flags & cpu_to_le32(FH_RSCSR_RPA_EN)) {
@@ -1803,10 +1799,10 @@ iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr,
 							    queue);
 	}
 
-	return sta;
+	return link_sta;
 }
 
-static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
+static int iwl_mld_rx_mgmt_prot(struct ieee80211_link_sta *link_sta,
 				struct ieee80211_hdr *hdr,
 				struct ieee80211_rx_status *rx_status,
 				u32 mpdu_status,
@@ -1820,7 +1816,6 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
 	struct ieee80211_key_conf *key;
 	const u8 *frame = (void *)hdr;
 	const u8 *mmie;
-	u8 link_id;
 
 	if ((mpdu_status & IWL_RX_MPDU_STATUS_SEC_MASK) ==
 	     IWL_RX_MPDU_STATUS_SEC_NONE)
@@ -1836,10 +1831,10 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
 	if (!ieee80211_is_beacon(hdr->frame_control))
 		return 0;
 
-	if (!sta)
+	if (!link_sta)
 		return -1;
 
-	mld_sta = iwl_mld_sta_from_mac80211(sta);
+	mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
 	mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif);
 
 	/* key mismatch - will also report !MIC_OK but we shouldn't count it */
@@ -1853,8 +1848,7 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
 		return 0;
 	}
 
-	link_id = rx_status->link_valid ? rx_status->link_id : 0;
-	link = rcu_dereference(mld_vif->link[link_id]);
+	link = rcu_dereference(mld_vif->link[link_sta->link_id]);
 	if (WARN_ON_ONCE(!link))
 		return -1;
 
@@ -1905,7 +1899,7 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
 }
 
 static int iwl_mld_rx_crypto(struct iwl_mld *mld,
-			     struct ieee80211_sta *sta,
+			     struct ieee80211_link_sta *link_sta,
 			     struct ieee80211_hdr *hdr,
 			     struct ieee80211_rx_status *rx_status,
 			     struct iwl_rx_mpdu_desc *desc, int queue,
@@ -1915,7 +1909,7 @@ static int iwl_mld_rx_crypto(struct iwl_mld *mld,
 
 	if (unlikely(ieee80211_is_mgmt(hdr->frame_control) &&
 		     !ieee80211_has_protected(hdr->frame_control)))
-		return iwl_mld_rx_mgmt_prot(sta, hdr, rx_status, status,
+		return iwl_mld_rx_mgmt_prot(link_sta, hdr, rx_status, status,
 					    le16_to_cpu(desc->mpdu_len));
 
 	if (!ieee80211_has_protected(hdr->frame_control) ||
@@ -2023,7 +2017,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
 	struct iwl_mld_rx_phy_data phy_data = {};
 	struct iwl_rx_mpdu_desc *mpdu_desc = (void *)pkt->data;
-	struct ieee80211_sta *sta;
+	struct ieee80211_link_sta *link_sta;
 	struct ieee80211_hdr *hdr;
 	struct sk_buff *skb;
 	size_t mpdu_desc_size = sizeof(*mpdu_desc);
@@ -2086,7 +2080,8 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
 
 	rcu_read_lock();
 
-	sta = iwl_mld_rx_with_sta(mld, hdr, skb, mpdu_desc, pkt, queue, &drop);
+	link_sta = iwl_mld_rx_with_sta(mld, hdr, skb, mpdu_desc, pkt, queue,
+				       &drop);
 	if (drop)
 		goto drop;
 
@@ -2127,7 +2122,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
 
 	iwl_mld_rx_fill_status(mld, link_id, hdr, skb, &phy_data);
 
-	if (iwl_mld_rx_crypto(mld, sta, hdr, rx_status, mpdu_desc, queue,
+	if (iwl_mld_rx_crypto(mld, link_sta, hdr, rx_status, mpdu_desc, queue,
 			      le32_to_cpu(pkt->len_n_flags), &crypto_len))
 		goto drop;
 
@@ -2140,7 +2135,8 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
 	if (iwl_mld_time_sync_frame(mld, skb, hdr->addr2))
 		goto out;
 
-	reorder_res = iwl_mld_reorder(mld, napi, queue, sta, skb, mpdu_desc);
+	reorder_res = iwl_mld_reorder(mld, napi, queue, link_sta, skb,
+				      mpdu_desc);
 	switch (reorder_res) {
 	case IWL_MLD_PASS_SKB:
 		break;
@@ -2153,7 +2149,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
 		goto drop;
 	}
 
-	iwl_mld_pass_packet_to_mac80211(mld, napi, skb, queue, sta);
+	iwl_mld_pass_packet_to_mac80211(mld, napi, skb, queue, link_sta);
 
 	goto out;
 
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.h b/drivers/net/wireless/intel/iwlwifi/mld/rx.h
index 573b89c3c9c61..b08c5ade3db8f 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.h
@@ -69,7 +69,7 @@ void iwl_mld_handle_rsc_notif(struct iwl_mld *mld,
 void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
 				     struct napi_struct *napi,
 				     struct sk_buff *skb, int queue,
-				     struct ieee80211_sta *sta);
+				     struct ieee80211_link_sta *link_sta);
 
 void iwl_mld_handle_phy_air_sniffer_notif(struct iwl_mld *mld,
 					  struct napi_struct *napi,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c
index 29b0248cec3df..e9efe2996f071 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c
@@ -2,7 +2,7 @@
 /*
  * KUnit tests for channel helper functions
  *
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
  */
 #include <kunit/test.h>
 #include <kunit/static_stub.h>
@@ -441,7 +441,7 @@ static void
 fake_iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
 				     struct napi_struct *napi,
 				     struct sk_buff *skb, int queue,
-				     struct ieee80211_sta *sta)
+				     struct ieee80211_link_sta *link_sta)
 {
 	__skb_queue_tail(&g_released_skbs, skb);
 	g_num_released_skbs++;
@@ -630,7 +630,8 @@ static void test_reorder_buffer(struct kunit *test)
 	mpdu_desc = setup_mpdu_desc();
 
 	rcu_read_lock();
-	reorder_res = iwl_mld_reorder(mld, NULL, QUEUE, sta, skb, mpdu_desc);
+	reorder_res = iwl_mld_reorder(mld, NULL, QUEUE, &sta->deflink, skb,
+				      mpdu_desc);
 	rcu_read_unlock();
 
 	KUNIT_ASSERT_EQ(test, reorder_res, param->expected.reorder_res);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
index ab1eb2eb0c3cd..8c197a090c2b1 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
@@ -90,7 +90,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
 				fraglen, rxb->truesize);
 	}
 
-	ieee80211_rx_napi(mvm->hw, sta, skb, napi);
+	ieee80211_rx_napi(mvm->hw, sta ? &sta->deflink : NULL, skb, napi);
 }
 
 /*
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
index 7f0b4f5daa214..8c4009d3a00ca 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 /*
- * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2026 Intel Corporation
  * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
  * Copyright (C) 2015-2017 Intel Deutschland GmbH
  */
@@ -243,7 +243,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
 		return;
 	}
 
-	ieee80211_rx_napi(mvm->hw, sta, skb, napi);
+	ieee80211_rx_napi(mvm->hw, sta ? &sta->deflink : NULL, skb, napi);
 }
 
 static bool iwl_mvm_used_average_energy(struct iwl_mvm *mvm,
@@ -2528,7 +2528,7 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi,
 	}
 
 	rcu_read_lock();
-	ieee80211_rx_napi(mvm->hw, sta, skb, napi);
+	ieee80211_rx_napi(mvm->hw, sta ? &sta->deflink : NULL, skb, napi);
 	rcu_read_unlock();
 }
 
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index abbe65cbcd89e..cda5c5d51aab0 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -1255,11 +1255,12 @@ EXPORT_SYMBOL(mt76_rx_signal);
 static void
 mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
 		struct ieee80211_hw **hw,
-		struct ieee80211_sta **sta)
+		struct ieee80211_link_sta **link_sta)
 {
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);
 	struct mt76_rx_status mstat;
+	struct ieee80211_sta *sta;
 
 	mstat = *((struct mt76_rx_status *)skb->cb);
 	memset(status, 0, sizeof(*status));
@@ -1301,12 +1302,14 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
 	memcpy(status->chain_signal, mstat.chain_signal,
 	       sizeof(mstat.chain_signal));
 
-	if (mstat.wcid) {
-		status->link_valid = mstat.wcid->link_valid;
-		status->link_id = mstat.wcid->link_id;
-	}
+	sta = wcid_to_sta(mstat.wcid);
+	if (!sta)
+		*link_sta = NULL;
+	else if (mstat.wcid->link_valid)
+		*link_sta = rcu_dereference(sta->link[mstat.wcid->link_id]);
+	else
+		*link_sta = &sta->deflink;
 
-	*sta = wcid_to_sta(mstat.wcid);
 	*hw = mt76_phy_hw(dev, mstat.phy_idx);
 }
 
@@ -1530,7 +1533,7 @@ mt76_check_sta(struct mt76_dev *dev, struct sk_buff *skb)
 void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
 		      struct napi_struct *napi)
 {
-	struct ieee80211_sta *sta;
+	struct ieee80211_link_sta *link_sta;
 	struct ieee80211_hw *hw;
 	struct sk_buff *skb, *tmp;
 	LIST_HEAD(list);
@@ -1541,8 +1544,8 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
 
 		mt76_check_ccmp_pn(skb);
 		skb_shinfo(skb)->frag_list = NULL;
-		mt76_rx_convert(dev, skb, &hw, &sta);
-		ieee80211_rx_list(hw, sta, skb, &list);
+		mt76_rx_convert(dev, skb, &hw, &link_sta);
+		ieee80211_rx_list(hw, link_sta, skb, &list);
 
 		/* subsequent amsdu frames */
 		while (nskb) {
@@ -1550,8 +1553,8 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
 			nskb = nskb->next;
 			skb->next = NULL;
 
-			mt76_rx_convert(dev, skb, &hw, &sta);
-			ieee80211_rx_list(hw, sta, skb, &list);
+			mt76_rx_convert(dev, skb, &hw, &link_sta);
+			ieee80211_rx_list(hw, link_sta, skb, &list);
 		}
 	}
 	spin_unlock(&dev->rx_lock);
diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 397ebbfcac09a..454d876aeb886 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -3073,10 +3073,8 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,
 	struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
 	struct rtw89_rx_desc_info *desc_info = iter_data->desc_info;
 	struct sk_buff *skb = iter_data->skb;
-	struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct rtw89_rx_phy_ppdu *phy_ppdu = iter_data->phy_ppdu;
-	bool is_mld = ieee80211_vif_is_mld(vif);
 	struct ieee80211_bss_conf *bss_conf;
 	struct rtw89_vif_link *rtwvif_link;
 	const u8 *bssid = iter_data->bssid;
@@ -3110,11 +3108,6 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,
 	if (!ether_addr_equal(target_bssid, bssid))
 		goto out;
 
-	if (is_mld) {
-		rx_status->link_valid = true;
-		rx_status->link_id = rtwvif_link->link_id;
-	}
-
 	bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx);
 	pkt_stat = &bb->cur_pkt_stat;
 
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index 9f895ad718e9b..1a12261f3e8e5 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -1883,9 +1883,6 @@ static void mac80211_hwsim_rx(struct mac80211_hwsim_data *data,
 				sp->active_links_rx &= ~BIT(link_id);
 			else
 				sp->active_links_rx |= BIT(link_id);
-
-			rx_status->link_valid = true;
-			rx_status->link_id = link_id;
 		}
 		rcu_read_unlock();
 	}
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 6461a7bd0169d..9aa7d2d4a1841 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -4009,6 +4009,7 @@ struct cfg80211_update_ft_ies_params {
  * @link_id: for MLO, the link ID to transmit on, -1 if not given; note
  *	that the link ID isn't validated (much), it's in range but the
  *	link might not exist (or be used by the receiver STA)
+ * @no_sta: set if the frame should not be transmitted using an existing STA
  */
 struct cfg80211_mgmt_tx_params {
 	struct ieee80211_channel *chan;
@@ -4021,6 +4022,7 @@ struct cfg80211_mgmt_tx_params {
 	int n_csa_offsets;
 	const u16 *csa_offsets;
 	int link_id;
+	bool no_sta;
 };
 
 /**
@@ -9501,6 +9503,7 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
  * @flags: flags, as defined in &enum nl80211_rxmgmt_flags
  * @rx_tstamp: Hardware timestamp of frame RX in nanoseconds
  * @ack_tstamp: Hardware timestamp of ack TX in nanoseconds
+ * @no_sta: set if no station is known for the frame (relevant for MLD)
  */
 struct cfg80211_rx_info {
 	int freq;
@@ -9512,6 +9515,7 @@ struct cfg80211_rx_info {
 	u32 flags;
 	u64 rx_tstamp;
 	u64 ack_tstamp;
+	bool no_sta;
 };
 
 /**
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 7bb4618065b67..1172d7b83ed18 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1770,10 +1770,9 @@ enum mac80211_rx_encoding {
  * @ampdu_reference: A-MPDU reference number, must be a different value for
  *	each A-MPDU but the same for each subframe within one A-MPDU
  * @zero_length_psdu_type: radiotap type of the 0-length PSDU
- * @link_valid: if the link which is identified by @link_id is valid. This flag
- *	is set only when connection is MLO.
- * @link_id: id of the link used to receive the packet. This is used along with
- *	@link_valid.
+ * @link_id: id of the link used to receive the packet. Set and used by
+ *	mac80211 internally, it uses @freq set by the driver to identify the
+ *	correct link per vif.
  */
 struct ieee80211_rx_status {
 	u64 mactime;
@@ -1813,7 +1812,7 @@ struct ieee80211_rx_status {
 	u8 chains;
 	s8 chain_signal[IEEE80211_MAX_CHAINS];
 	u8 zero_length_psdu_type;
-	u8 link_valid:1, link_id:4;
+	u8 link_id:4;
 };
 
 static_assert(sizeof(struct ieee80211_rx_status) <= sizeof_field(struct sk_buff, cb));
@@ -5389,14 +5388,18 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw);
  * mixed for a single hardware. Must not run concurrently with
  * ieee80211_tx_status_skb() or ieee80211_tx_status_ni().
  *
+ * For data frames, when hardware has done address translation, a link station
+ * has to be provided and the frequency information may be skipped.
+ *
  * This function must be called with BHs disabled and RCU read lock
  *
  * @hw: the hardware this frame came in on
- * @sta: the station the frame was received from, or %NULL
+ * @link_sta: the link station the data frame was received from, or %NULL
  * @skb: the buffer to receive, owned by mac80211 after this call
  * @list: the destination list
  */
-void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+void ieee80211_rx_list(struct ieee80211_hw *hw,
+		       struct ieee80211_link_sta *link_sta,
 		       struct sk_buff *skb, struct list_head *list);
 
 /**
@@ -5414,14 +5417,18 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
  * mixed for a single hardware. Must not run concurrently with
  * ieee80211_tx_status_skb() or ieee80211_tx_status_ni().
  *
+ * For data frames, when hardware has done address translation, a link station
+ * has to be provided and the frequency information may be skipped.
+ *
  * This function must be called with BHs disabled.
  *
  * @hw: the hardware this frame came in on
- * @sta: the station the frame was received from, or %NULL
+ * @link_sta: the link station the data frame was received from, or %NULL
  * @skb: the buffer to receive, owned by mac80211 after this call
  * @napi: the NAPI context
  */
-void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+void ieee80211_rx_napi(struct ieee80211_hw *hw,
+		       struct ieee80211_link_sta *link_sta,
 		       struct sk_buff *skb, struct napi_struct *napi);
 
 /**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 32553cacf9889..510102febc269 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -3185,6 +3185,11 @@ enum nl80211_commands {
  *	The aggregated message always precedes the per-link messages for the
  *	same station within a dump sequence.
  *
+ * @NL80211_ATTR_FRAME_NO_STA: Valid for @NL80211_CMD_FRAME to denote that
+ *	the kernel had no station for a received frame or should not use a
+ *	known station to transmit a frame. This is relevant to know whether
+ *	MLD address translation happened or to disable it when sending a frame.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3785,6 +3790,8 @@ enum nl80211_attrs {
 
 	NL80211_ATTR_STA_DUMP_LINK_STATS,
 
+	NL80211_ATTR_FRAME_NO_STA,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 0832213430f43..ce9fb19d8c18c 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -98,7 +98,7 @@ static void ieee80211_send_addba_request(struct sta_info *sta, u16 tid,
 	if (sta->sta.deflink.he_cap.has_he)
 		ieee80211_add_addbaext(skb, 0, agg_size);
 
-	ieee80211_tx_skb_tid(sdata, skb, tid, -1);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, tid, -1);
 }
 
 void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)
@@ -127,7 +127,7 @@ void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)
 
 	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
 					IEEE80211_TX_CTL_REQ_TX_STATUS;
-	ieee80211_tx_skb_tid(sdata, skb, tid, -1);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, tid, -1);
 }
 EXPORT_SYMBOL(ieee80211_send_bar);
 
diff --git a/net/mac80211/ap.c b/net/mac80211/ap.c
index e7ac99c5a22eb..fe255891c35f0 100644
--- a/net/mac80211/ap.c
+++ b/net/mac80211/ap.c
@@ -82,9 +82,6 @@ ieee80211_rx_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata,
 	if (!ift_ext_capa)
 		return;
 
-	if (!status->link_valid)
-		return;
-
 	sta = sta_info_get_bss(sdata, mgmt->sa);
 	if (!sta)
 		return;
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index e1e1b7f82f3fe..c5c2113fa4f79 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -545,7 +545,7 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
 	info->status_data = IEEE80211_STATUS_TYPE_SMPS |
 			    u16_encode_bits(status_link_id << 2 | smps,
 					    IEEE80211_STATUS_SUBDATA_MASK);
-	ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, 7, link_id);
 
 	return 0;
 }
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 65311340db406..9514f01778beb 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -2471,7 +2471,8 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
 		    struct sta_info *sta, struct sk_buff *skb);
 
 void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
-				 struct sk_buff *skb, int tid, int link_id,
+				 struct sk_buff *skb, struct sta_info *sta,
+				 int tid, int link_id,
 				 enum nl80211_band band);
 
 static inline bool ieee80211_require_encrypted_assoc(__le16 fc,
@@ -2486,18 +2487,20 @@ ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
 			  enum nl80211_band band)
 {
 	rcu_read_lock();
-	__ieee80211_tx_skb_tid_band(sdata, skb, tid, -1, band);
+	__ieee80211_tx_skb_tid_band(sdata, skb, ERR_PTR(-ENOENT),
+				    tid, -1, band);
 	rcu_read_unlock();
 }
 
 void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
-			  struct sk_buff *skb, int tid, int link_id);
+			  struct sk_buff *skb, struct sta_info *sta,
+			  int tid, int link_id);
 
 static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
 				    struct sk_buff *skb)
 {
 	/* Send all internal mgmt frames on VO. Accordingly set TID to 7. */
-	ieee80211_tx_skb_tid(sdata, skb, 7, -1);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, 7, -1);
 }
 
 /**
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 18e62c5ff0fd8..29d532183814d 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1674,11 +1674,8 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local,
 				break;
 
 			status = IEEE80211_SKB_RXCB(skb);
-			if (!status->link_valid)
-				link_sta = &sta->deflink;
-			else
-				link_sta = rcu_dereference_protected(sta->link[status->link_id],
-							lockdep_is_held(&local->hw.wiphy->mtx));
+			link_sta = wiphy_dereference(local->hw.wiphy,
+						     sta->link[status->link_id]);
 			if (link_sta)
 				ieee80211_ht_handle_chanwidth_notif(local, sdata, sta,
 								    link_sta, chanwidth,
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 47ec703f10949..189dd3467f764 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -11827,12 +11827,10 @@ void ieee80211_sta_rx_queued_frame(struct ieee80211_sub_if_data *sdata,
 	rx_status = (struct ieee80211_rx_status *) skb->cb;
 	fc = le16_to_cpu(mgmt->frame_control);
 
-	if (rx_status->link_valid) {
-		link = sdata_dereference(sdata->link[rx_status->link_id],
-					 sdata);
-		if (!link)
-			return;
-	}
+	link = sdata_dereference(sdata->link[rx_status->link_id],
+				 sdata);
+	if (!link)
+		return;
 
 	switch (fc & IEEE80211_FCTL_STYPE) {
 	case IEEE80211_STYPE_BEACON:
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 7acef80d5f1fb..ddd7a96a2f817 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -854,8 +854,10 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 			need_offchan = true;
 
 		rcu_read_lock();
-		sta = sta_info_get_bss(sdata, mgmt->da);
-		mlo_sta = sta && sta->sta.mlo;
+		if (!params->no_sta) {
+			sta = sta_info_get_bss(sdata, mgmt->da);
+			mlo_sta = sta && sta->sta.mlo;
+		}
 
 		if (!ieee80211_is_action(mgmt->frame_control) ||
 		    mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
@@ -884,7 +886,8 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 		     local->ops->remain_on_channel &&
 		     memcmp(sdata->vif.cfg.ap_addr, mgmt->bssid, ETH_ALEN))) {
 			need_offchan = true;
-		} else if (sdata->u.mgd.associated &&
+		} else if (!params->no_sta &&
+			   sdata->u.mgd.associated &&
 			   ether_addr_equal(sdata->vif.cfg.ap_addr, mgmt->da)) {
 			sta = sta_info_get_bss(sdata, mgmt->da);
 			mlo_sta = sta && sta->sta.mlo;
@@ -1022,7 +1025,9 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 	}
 
 	if (!need_offchan) {
-		ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
+		ieee80211_tx_skb_tid(sdata, skb,
+				     params->no_sta ? ERR_PTR(-ENOENT) : sta,
+				     7, link_id);
 		ret = 0;
 		goto out_unlock;
 	}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 31dc6f83781b1..d6755a4c7d3c9 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -222,43 +222,21 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
 }
 
 static void __ieee80211_queue_skb_to_iface(struct ieee80211_sub_if_data *sdata,
-					   int link_id,
-					   struct sta_info *sta,
+					   struct link_sta_info *link_sta,
 					   struct sk_buff *skb)
 {
-	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-
-	if (link_id >= 0) {
-		status->link_valid = 1;
-		status->link_id = link_id;
-	} else {
-		status->link_valid = 0;
-	}
-
 	skb_queue_tail(&sdata->skb_queue, skb);
 	wiphy_work_queue(sdata->local->hw.wiphy, &sdata->work);
-	if (sta) {
-		struct link_sta_info *link_sta_info;
-
-		if (link_id >= 0) {
-			link_sta_info = rcu_dereference(sta->link[link_id]);
-			if (!link_sta_info)
-				return;
-		} else {
-			link_sta_info = &sta->deflink;
-		}
-
-		link_sta_info->rx_stats.packets++;
-	}
+	if (link_sta)
+		link_sta->rx_stats.packets++;
 }
 
 static void ieee80211_queue_skb_to_iface(struct ieee80211_sub_if_data *sdata,
-					 int link_id,
-					 struct sta_info *sta,
+					 struct link_sta_info *link_sta,
 					 struct sk_buff *skb)
 {
 	skb->protocol = 0;
-	__ieee80211_queue_skb_to_iface(sdata, link_id, sta, skb);
+	__ieee80211_queue_skb_to_iface(sdata, link_sta, skb);
 }
 
 static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata,
@@ -301,7 +279,7 @@ static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata,
 	if (!skb)
 		return;
 
-	ieee80211_queue_skb_to_iface(sdata, -1, NULL, skb);
+	ieee80211_queue_skb_to_iface(sdata, NULL, skb);
 }
 
 /*
@@ -1500,7 +1478,7 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
 	/* if this mpdu is fragmented - terminate rx aggregation session */
 	sc = le16_to_cpu(hdr->seq_ctrl);
 	if (sc & IEEE80211_SCTL_FRAG) {
-		ieee80211_queue_skb_to_iface(rx->sdata, rx->link_id, NULL, skb);
+		ieee80211_queue_skb_to_iface(rx->sdata, NULL, skb);
 		return;
 	}
 
@@ -2524,7 +2502,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
 
  out:
 	ieee80211_led_rx(rx->local);
-	if (rx->sta)
+	if (rx->link_sta)
 		rx->link_sta->rx_stats.packets++;
 	return RX_CONTINUE;
 }
@@ -3335,8 +3313,8 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
 		    (tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
 		     tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
 			rx->skb->protocol = cpu_to_be16(ETH_P_TDLS);
-			__ieee80211_queue_skb_to_iface(sdata, rx->link_id,
-						       rx->sta, rx->skb);
+			__ieee80211_queue_skb_to_iface(sdata, rx->link_sta,
+						       rx->skb);
 			return RX_QUEUED;
 		}
 	}
@@ -3991,7 +3969,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
 	return RX_QUEUED;
 
  queue:
-	ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb);
+	ieee80211_queue_skb_to_iface(sdata, rx->link_sta, rx->skb);
 	return RX_QUEUED;
 }
 
@@ -4005,6 +3983,7 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
 		.len = rx->skb->len,
 		.link_id = rx->link_id,
 		.have_link_id = rx->link_id >= 0,
+		.no_sta = !rx->sta,
 	};
 
 	/* skip known-bad action frames and return them in the next handler */
@@ -4127,7 +4106,7 @@ ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx)
 					local->hw.offchannel_tx_hw_queue;
 		}
 
-		__ieee80211_tx_skb_tid_band(rx->sdata, nskb, 7, -1,
+		__ieee80211_tx_skb_tid_band(rx->sdata, nskb, rx->sta, 7, -1,
 					    status->band);
 	}
 
@@ -4147,7 +4126,7 @@ ieee80211_rx_h_ext(struct ieee80211_rx_data *rx)
 		return RX_DROP_U_UNEXPECTED_EXT_FRAME;
 
 	/* for now only beacons are ext, so queue them */
-	ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb);
+	ieee80211_queue_skb_to_iface(sdata, rx->link_sta, rx->skb);
 
 	return RX_QUEUED;
 }
@@ -4204,7 +4183,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
 		return RX_DROP_U_UNHANDLED_MGMT_STYPE;
 	}
 
-	ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb);
+	ieee80211_queue_skb_to_iface(sdata, rx->link_sta, rx->skb);
 
 	return RX_QUEUED;
 }
@@ -4248,12 +4227,28 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,
 	spin_lock_bh(&rx->local->rx_path_lock);
 
 	while ((skb = __skb_dequeue(frames))) {
+		struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+
 		/*
-		 * all the other fields are valid across frames
-		 * that belong to an aMPDU since they are on the
-		 * same TID from the same station
+		 * Most fields are valid across frames. However,
+		 * in the case of reordering frames may have arrived
+		 * on different links, so adjust the link_id, link
+		 * and link_sta accordingly.
 		 */
 		rx->skb = skb;
+		if (ieee80211_vif_is_mld(&rx->sdata->vif) &&
+		    unlikely(rx->link_id != status->link_id)) {
+			if (rx->sta) {
+				rx->link_sta =
+					rcu_dereference(rx->sta->link[rx->link_id]);
+
+				/* Link got disabled, just use deflink */
+				if (!rx->link_sta)
+					rx->link_sta = &rx->sta->deflink;
+			}
+			rx->link_id = status->link_id;
+			rx->link = rcu_dereference(rx->sdata->link[status->link_id]);
+		}
 
 		if (WARN_ON_ONCE(!rx->link)) {
 			res = RX_DROP_U_NO_LINK;
@@ -4323,7 +4318,34 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
 static bool
 ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id)
 {
-	return !!(sta->valid_links & BIT(link_id));
+	if (sta->mlo)
+		return sta->valid_links & BIT(link_id);
+
+	return sta->deflink.link_id == link_id;
+}
+
+static bool ieee80211_rx_data_set_link_sta(struct ieee80211_rx_data *rx,
+					   struct link_sta_info *link_sta)
+{
+	struct sta_info *sta;
+
+	if (WARN_ON_ONCE(!link_sta) ||
+	    !ieee80211_rx_is_valid_sta_link_id(&link_sta->sta->sta,
+					       link_sta->link_id))
+		return false;
+
+	sta = link_sta->sta;
+
+	rx->sta = sta;
+	rx->local = sta->sdata->local;
+	rx->link = rcu_dereference(sta->sdata->link[link_sta->link_id]);
+	if (!rx->link)
+		return false;
+
+	rx->link_id = rx->link->link_id;
+	rx->link_sta = link_sta;
+
+	return true;
 }
 
 static bool ieee80211_rx_data_set_link(struct ieee80211_rx_data *rx,
@@ -4360,11 +4382,13 @@ static bool ieee80211_rx_data_set_sta(struct ieee80211_rx_data *rx,
 
 	if (link_id < 0) {
 		if (ieee80211_vif_is_mld(&rx->sdata->vif) &&
-		    sta && !sta->sta.valid_links)
+		    sta && !sta->sta.valid_links) {
 			rx->link =
 				rcu_dereference(rx->sdata->link[sta->deflink.link_id]);
-		else
+			rx->link_sta = &sta->deflink;
+		} else {
 			rx->link = &rx->sdata->deflink;
+		}
 	} else if (!ieee80211_rx_data_set_link(rx, link_id)) {
 		return false;
 	}
@@ -4387,7 +4411,7 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
 	struct tid_ampdu_rx *tid_agg_rx;
 	int link_id = -1;
 
-	/* FIXME: statistics won't be right with this */
+	/* NOTE: the correct link STA for the frame is set later */
 	if (sta->sta.valid_links)
 		link_id = ffs(sta->sta.valid_links) - 1;
 
@@ -4914,20 +4938,14 @@ static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
 {
 	struct ieee80211_sta_rx_stats *stats;
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
-	struct sta_info *sta = rx->sta;
-	struct link_sta_info *link_sta;
+	struct link_sta_info *link_sta = rx->link_sta;
 	struct sk_buff *skb = rx->skb;
 	void *sa = skb->data + ETH_ALEN;
 	void *da = skb->data;
 
-	if (rx->link_id >= 0) {
-		link_sta = rcu_dereference(sta->link[rx->link_id]);
-		if (WARN_ON_ONCE(!link_sta)) {
-			dev_kfree_skb(rx->skb);
-			return;
-		}
-	} else {
-		link_sta = &sta->deflink;
+	if (WARN_ON_ONCE(!link_sta)) {
+		dev_kfree_skb(rx->skb);
+		return;
 	}
 
 	stats = &link_sta->rx_stats;
@@ -5154,6 +5172,9 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
 		goto drop;
 	}
 
+	status = IEEE80211_SKB_RXCB(skb);
+	status->link_id = rx->link_id < 0 ? 0 : rx->link_id;
+
 	ieee80211_rx_8023(rx, fast_rx, orig_len);
 
 	return true;
@@ -5178,6 +5199,7 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
 	struct ieee80211_hdr *hdr = (void *)skb->data;
 	struct link_sta_info *link_sta = rx->link_sta;
 	struct ieee80211_link_data *link = rx->link;
+	struct ieee80211_rx_status *status;
 
 	rx->skb = skb;
 
@@ -5221,6 +5243,9 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
 		hdr = (struct ieee80211_hdr *)rx->skb->data;
 	}
 
+	status = IEEE80211_SKB_RXCB(rx->skb);
+	status->link_id = rx->link_id < 0 ? 0 : rx->link_id;
+
 	if (ieee80211_is_s1g_beacon(hdr->frame_control)) {
 		ieee80211_invoke_rx_handlers(rx);
 		return true;
@@ -5256,22 +5281,19 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
 }
 
 static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,
-				       struct ieee80211_sta *pubsta,
+				       struct ieee80211_link_sta *link_pubsta,
 				       struct sk_buff *skb,
 				       struct list_head *list)
 {
 	struct ieee80211_local *local = hw_to_local(hw);
-	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_fast_rx *fast_rx;
 	struct ieee80211_rx_data rx;
 	struct sta_info *sta;
-	int link_id = -1;
 
 	memset(&rx, 0, sizeof(rx));
 	rx.skb = skb;
 	rx.local = local;
 	rx.list = list;
-	rx.link_id = -1;
 
 	I802_DEBUG_INC(local->dot11ReceivedFragmentCount);
 
@@ -5279,23 +5301,15 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,
 	if (skb->len < sizeof(struct ethhdr))
 		goto drop;
 
-	if (!pubsta)
+	if (!link_pubsta)
 		goto drop;
 
-	if (status->link_valid)
-		link_id = status->link_id;
-
-	/*
-	 * TODO: Should the frame be dropped if the right link_id is not
-	 * available? Or may be it is fine in the current form to proceed with
-	 * the frame processing because with frame being in 802.3 format,
-	 * link_id is used only for stats purpose and updating the stats on
-	 * the deflink is fine?
-	 */
-	sta = container_of(pubsta, struct sta_info, sta);
-	if (!ieee80211_rx_data_set_sta(&rx, sta, link_id))
+	sta = container_of(link_pubsta->sta, struct sta_info, sta);
+	if (!ieee80211_rx_data_set_sta(&rx, sta, link_pubsta->link_id))
 		goto drop;
 
+	rx.link_id = link_pubsta->link_id;
+
 	fast_rx = rcu_dereference(rx.sta->fast_rx);
 	if (!fast_rx)
 		goto drop;
@@ -5307,59 +5321,19 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,
 	dev_kfree_skb(skb);
 }
 
-static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx,
-				       struct sk_buff *skb, bool consume)
+static bool ieee80211_rx_valid_freq(int freq, struct ieee80211_link_data *link)
 {
-	struct link_sta_info *link_sta;
-	struct ieee80211_hdr *hdr = (void *)skb->data;
-	struct sta_info *sta;
-	int link_id = -1;
-
-	if (ieee80211_is_s1g_beacon(hdr->frame_control)) {
-		if (!ieee80211_rx_data_set_sta(rx, NULL, -1))
-			return false;
-
-		return ieee80211_prepare_and_rx_handle(rx, skb, consume);
-	}
+	struct ieee80211_chanctx_conf *conf;
 
-	/*
-	 * Look up link station first, in case there's a
-	 * chance that they might have a link address that
-	 * is identical to the MLD address, that way we'll
-	 * have the link information if needed.
-	 */
-	link_sta = link_sta_info_get_bss(rx->sdata, hdr->addr2);
-	if (link_sta) {
-		sta = link_sta->sta;
-		link_id = link_sta->link_id;
-	} else {
-		struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-
-		sta = sta_info_get_bss(rx->sdata, hdr->addr2);
-		if (status->link_valid) {
-			link_id = status->link_id;
-		} else if (ieee80211_vif_is_mld(&rx->sdata->vif) &&
-			   status->freq) {
-			struct ieee80211_link_data *link;
-			struct ieee80211_chanctx_conf *conf;
-
-			for_each_link_data_rcu(rx->sdata, link) {
-				conf = rcu_dereference(link->conf->chanctx_conf);
-				if (!conf || !conf->def.chan)
-					continue;
-
-				if (status->freq == conf->def.chan->center_freq) {
-					link_id = link->link_id;
-					break;
-				}
-			}
-		}
-	}
+	if (!freq || link->sdata->vif.type == NL80211_IFTYPE_NAN ||
+	    link->sdata->vif.type == NL80211_IFTYPE_NAN_DATA)
+		return true;
 
-	if (!ieee80211_rx_data_set_sta(rx, sta, link_id))
+	conf = rcu_dereference(link->conf->chanctx_conf);
+	if (!conf || !conf->def.chan)
 		return false;
 
-	return ieee80211_prepare_and_rx_handle(rx, skb, consume);
+	return freq == conf->def.chan->center_freq;
 }
 
 /*
@@ -5367,18 +5341,20 @@ static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx,
  * be called with rcu_read_lock protection.
  */
 static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
-					 struct ieee80211_sta *pubsta,
+					 struct ieee80211_link_sta *link_pubsta,
 					 struct sk_buff *skb,
 					 struct list_head *list)
 {
 	struct ieee80211_local *local = hw_to_local(hw);
-	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_sub_if_data *sdata;
+	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_hdr *hdr;
+	struct link_sta_info *link_sta;
+	struct sta_info *sta;
 	__le16 fc;
 	struct ieee80211_rx_data rx;
-	struct ieee80211_sub_if_data *prev;
 	struct rhlist_head *tmp;
+	bool rx_data_pending;
 	int err = 0;
 
 	fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
@@ -5386,7 +5362,6 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 	rx.skb = skb;
 	rx.local = local;
 	rx.list = list;
-	rx.link_id = -1;
 
 	if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc))
 		I802_DEBUG_INC(local->dot11ReceivedFragmentCount);
@@ -5426,97 +5401,102 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 		     ieee80211_is_s1g_beacon(hdr->frame_control)))
 		ieee80211_scan_rx(local, skb);
 
+	/*
+	 * RX of a data frame should only happen to existing stations.
+	 * It is therefore more efficient to use the one provided by the driver
+	 * or search based on the station's address.
+	 *
+	 * Note we will fall through and handle a spurious data frame in the
+	 * same way as a management frame.
+	 */
 	if (ieee80211_is_data(fc)) {
-		struct sta_info *sta, *prev_sta;
-		int link_id = -1;
-
-		if (status->link_valid)
-			link_id = status->link_id;
-
-		if (pubsta) {
-			sta = container_of(pubsta, struct sta_info, sta);
-			if (!ieee80211_rx_data_set_sta(&rx, sta, link_id))
-				goto out;
-
-			/*
-			 * In MLO connection, fetch the link_id using addr2
-			 * when the driver does not pass link_id in status.
-			 * When the address translation is already performed by
-			 * driver/hw, the valid link_id must be passed in
-			 * status.
-			 */
-
-			if (!status->link_valid && pubsta->mlo) {
-				struct link_sta_info *link_sta;
-
-				link_sta = link_sta_info_get_bss(rx.sdata,
-								 hdr->addr2);
-				if (!link_sta)
-					goto out;
-
-				if (!ieee80211_rx_data_set_link(&rx,
-								link_sta->link_id))
-					goto out;
-			}
-
-			if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
+		if (link_pubsta) {
+			sta = container_of(link_pubsta->sta, struct sta_info,
+					   sta);
+			link_sta = rcu_dereference(sta->link[link_pubsta->link_id]);
+
+			rx.sdata = sta->sdata;
+			if (ieee80211_rx_data_set_link_sta(&rx, link_sta) &&
+			    ieee80211_prepare_and_rx_handle(&rx, skb, true))
 				return;
+
 			goto out;
 		}
 
-		prev_sta = NULL;
+		rx_data_pending = false;
 
+		/* Search for stations on non-MLD interfaces */
 		for_each_sta_info(local, hdr->addr2, sta, tmp) {
-			if (!prev_sta) {
-				prev_sta = sta;
-				continue;
-			}
+			struct ieee80211_link_data *link;
 
-			rx.sdata = prev_sta->sdata;
-			if (!status->link_valid && prev_sta->sta.mlo) {
-				struct link_sta_info *link_sta;
+			if (ieee80211_vif_is_mld(&sta->sdata->vif))
+				continue;
 
-				link_sta = link_sta_info_get_bss(rx.sdata,
-								 hdr->addr2);
-				if (!link_sta)
-					continue;
+			link = &sta->sdata->deflink;
+			if (!ieee80211_rx_valid_freq(status->freq, link))
+				continue;
 
-				link_id = link_sta->link_id;
+			if (rx_data_pending) {
+				ieee80211_prepare_and_rx_handle(&rx, skb,
+								false);
+				rx_data_pending = false;
 			}
 
-			if (!ieee80211_rx_data_set_sta(&rx, prev_sta, link_id))
-				goto out;
-
-			ieee80211_prepare_and_rx_handle(&rx, skb, false);
+			rx.sdata = sta->sdata;
+			if (!ieee80211_rx_data_set_link_sta(&rx, &sta->deflink))
+				continue;
 
-			prev_sta = sta;
+			rx_data_pending = true;
 		}
 
-		if (prev_sta) {
-			rx.sdata = prev_sta->sdata;
-			if (!status->link_valid && prev_sta->sta.mlo) {
-				struct link_sta_info *link_sta;
+		/* And search stations on MLD interfaces */
+		for_each_link_sta_info(local, hdr->addr2, link_sta, tmp) {
+			struct ieee80211_link_data *link;
 
-				link_sta = link_sta_info_get_bss(rx.sdata,
-								 hdr->addr2);
-				if (!link_sta)
-					goto out;
+			sta = link_sta->sta;
+			sdata =	sta->sdata;
+			link = rcu_dereference(sdata->link[link_sta->link_id]);
 
-				link_id = link_sta->link_id;
+			if (!ieee80211_rx_valid_freq(status->freq, link))
+				continue;
+
+			if (rx_data_pending) {
+				ieee80211_prepare_and_rx_handle(&rx, skb,
+								false);
+				rx_data_pending = false;
 			}
 
-			if (!ieee80211_rx_data_set_sta(&rx, prev_sta, link_id))
-				goto out;
+			rx.sdata = sta->sdata;
+			if (!ieee80211_rx_data_set_link_sta(&rx, link_sta))
+				continue;
+
+			rx_data_pending = true;
+		}
 
+		if (rx_data_pending) {
 			if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
 				return;
+
 			goto out;
 		}
+
+		/* fall through, e.g. for spurious frame notification */
 	}
 
-	prev = NULL;
+	/*
+	 * This is a management frame (or a data frame without a station) and
+	 * it will be delivered independent of whether a station exists,
+	 * so iterate the interfaces.
+	 */
+	rx_data_pending = false;
+
+	/* We expect the driver to provide frequency information */
+	if (WARN_ON_ONCE(!local->emulate_chanctx && !status->freq))
+		goto out;
 
 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+		struct ieee80211_link_data *link = NULL;
+
 		if (!ieee80211_sdata_running(sdata))
 			continue;
 
@@ -5524,30 +5504,85 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 		    sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
 			continue;
 
+		link_sta = NULL;
+		link = NULL;
+
+		/* Try to resolve the station */
+		if (!ieee80211_vif_is_mld(&sdata->vif)) {
+			sta = sta_info_get_bss(sdata, hdr->addr2);
+
+			if (sta) {
+				link_sta = &sta->deflink;
+				link = &sdata->deflink;
+			}
+		} else {
+			link_sta = link_sta_info_get_bss(sdata, hdr->addr2);
+
+			if (link_sta)
+				link = rcu_dereference(sdata->link[link_sta->link_id]);
+		}
+
 		/*
-		 * frame is destined for this interface, but if it's
-		 * not also for the previous one we handle that after
-		 * the loop to avoid copying the SKB once too much
+		 * If we found a STA and it has a valid link information, then
+		 * RX using the station.
 		 */
+		if (link_sta && link &&
+		    ieee80211_rx_valid_freq(status->freq, link)) {
+			if (rx_data_pending) {
+				ieee80211_prepare_and_rx_handle(&rx, skb, false);
+				rx_data_pending = false;
+			}
+
+			/* No valid_links check as we need to RX beacons */
+
+			rx.sdata = sdata;
+			if (ieee80211_rx_data_set_link_sta(&rx, link_sta))
+				rx_data_pending = true;
 
-		if (!prev) {
-			prev = sdata;
 			continue;
 		}
 
-		rx.sdata = prev;
-		ieee80211_rx_for_interface(&rx, skb, false);
+		/* No station, try to resolve the link and RX */
+		if (ieee80211_vif_is_mld(&sdata->vif)) {
+			struct ieee80211_chanctx_conf *conf;
+			bool found = false;
 
-		prev = sdata;
-	}
+			for_each_link_data_rcu(sdata, link) {
+				conf = rcu_dereference(link->conf->chanctx_conf);
+				if (!conf || !conf->def.chan)
+					continue;
 
-	if (prev) {
-		rx.sdata = prev;
+				if (status->freq == conf->def.chan->center_freq) {
+					found = true;
+					break;
+				}
+			}
 
-		if (ieee80211_rx_for_interface(&rx, skb, true))
-			return;
+			if (!found)
+				link = &sdata->deflink;
+		} else {
+			link = &sdata->deflink;
+		}
+
+		if (rx_data_pending) {
+			ieee80211_prepare_and_rx_handle(&rx, skb, false);
+			rx_data_pending = false;
+		}
+
+		rx.sdata = sdata;
+		rx.local = sdata->local;
+		rx.link = link;
+		rx.link_id = link->link_id;
+		rx.sta = NULL;
+		rx.link_sta = NULL;
+
+		rx_data_pending = true;
 	}
 
+	if (rx_data_pending &&
+	    ieee80211_prepare_and_rx_handle(&rx, skb, true))
+		return;
+
  out:
 	dev_kfree_skb(skb);
 }
@@ -5556,7 +5591,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
  * This is the receive path handler. It is called by a low level driver when an
  * 802.11 MPDU is received from the hardware.
  */
-void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
+void ieee80211_rx_list(struct ieee80211_hw *hw,
+		       struct ieee80211_link_sta *link_pubsta,
 		       struct sk_buff *skb, struct list_head *list)
 {
 	struct ieee80211_local *local = hw_to_local(hw);
@@ -5694,9 +5730,6 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
 		}
 	}
 
-	if (WARN_ON_ONCE(status->link_id >= IEEE80211_LINK_UNSPECIFIED))
-		goto drop;
-
 	status->rx_flags = 0;
 
 	kcov_remote_start_common(skb_get_kcov_handle(skb));
@@ -5715,9 +5748,10 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
 			ieee80211_tpt_led_trig_rx(local, skb->len);
 
 		if (status->flag & RX_FLAG_8023)
-			__ieee80211_rx_handle_8023(hw, pubsta, skb, list);
+			__ieee80211_rx_handle_8023(hw, link_pubsta, skb, list);
 		else
-			__ieee80211_rx_handle_packet(hw, pubsta, skb, list);
+			__ieee80211_rx_handle_packet(hw, link_pubsta, skb,
+						     list);
 	}
 
 	kcov_remote_stop();
@@ -5727,7 +5761,8 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
 }
 EXPORT_SYMBOL(ieee80211_rx_list);
 
-void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
+void ieee80211_rx_napi(struct ieee80211_hw *hw,
+		       struct ieee80211_link_sta *link_pubsta,
 		       struct sk_buff *skb, struct napi_struct *napi)
 {
 	struct sk_buff *tmp;
@@ -5740,7 +5775,7 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
 	 * receive processing
 	 */
 	rcu_read_lock();
-	ieee80211_rx_list(hw, pubsta, skb, &list);
+	ieee80211_rx_list(hw, link_pubsta, skb, &list);
 	rcu_read_unlock();
 
 	if (!napi) {
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index eeff230bd909f..324c2b6d7ce1b 100644
--- 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-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
  */
 
 #include <linux/if_arp.h>
@@ -204,12 +204,10 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
 		 * an indication on which of the links the frame was received
 		 */
 		if (ieee80211_vif_is_mld(&scan_sdata->vif)) {
-			if (rx_status->link_valid) {
-				s8 link_id = rx_status->link_id;
+			s8 link_id = rx_status->link_id;
 
-				link_conf =
-					rcu_dereference(scan_sdata->vif.link_conf[link_id]);
-			}
+			link_conf =
+				rcu_dereference(scan_sdata->vif.link_conf[link_id]);
 		} else {
 			link_conf = &scan_sdata->vif.bss_conf;
 		}
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 4f4cae9fd274c..7ec33885a3e18 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -1066,7 +1066,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
 	}
 
 	if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
-		ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
+		ieee80211_tx_skb_tid(sdata, skb, sta, 7, link_id);
 		return 0;
 	}
 
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 06c50183f064d..73affc540f0c7 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -6448,7 +6448,8 @@ void ieee80211_unreserve_tid(struct ieee80211_sta *pubsta, u8 tid)
 EXPORT_SYMBOL(ieee80211_unreserve_tid);
 
 void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
-				 struct sk_buff *skb, int tid, int link_id,
+				 struct sk_buff *skb, struct sta_info *sta,
+				 int tid, int link_id,
 				 enum nl80211_band band)
 {
 	const struct ieee80211_hdr *hdr = (void *)skb->data;
@@ -6500,12 +6501,13 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
 	 */
 	local_bh_disable();
 	IEEE80211_SKB_CB(skb)->band = band;
-	ieee80211_xmit(sdata, NULL, skb);
+	ieee80211_xmit(sdata, sta, skb);
 	local_bh_enable();
 }
 
 void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
-			  struct sk_buff *skb, int tid, int link_id)
+			  struct sk_buff *skb, struct sta_info *sta,
+			  int tid, int link_id)
 {
 	struct ieee80211_chanctx_conf *chanctx_conf;
 	enum nl80211_band band;
@@ -6531,7 +6533,7 @@ void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
 		band = 0;
 	}
 
-	__ieee80211_tx_skb_tid_band(sdata, skb, tid, link_id, band);
+	__ieee80211_tx_skb_tid_band(sdata, skb, sta, tid, link_id, band);
 	rcu_read_unlock();
 }
 
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index b081eeaf70162..e77a4c81d0317 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1097,6 +1097,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
 	[NL80211_ATTR_NPCA_PUNCT_BITMAP] =
 		NLA_POLICY_FULL_RANGE(NLA_U32, &nl80211_punct_bitmap_range),
 	[NL80211_ATTR_STA_DUMP_LINK_STATS] = { .type = NLA_FLAG },
+	[NL80211_ATTR_FRAME_NO_STA] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -14873,6 +14874,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
 	    !(wdev->valid_links & BIT(params.link_id)))
 		return -EINVAL;
 
+	params.no_sta =
+		nla_get_flag(info->attrs[NL80211_ATTR_FRAME_NO_STA]);
+
 	params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
 	params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
 
@@ -22042,7 +22046,9 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
 	    (info->ack_tstamp && nla_put_u64_64bit(msg,
 						   NL80211_ATTR_TX_HW_TIMESTAMP,
 						   info->ack_tstamp,
-						   NL80211_ATTR_PAD)))
+						   NL80211_ATTR_PAD)) ||
+	    (info->no_sta &&
+	     nla_put_flag(msg, NL80211_ATTR_FRAME_NO_STA)))
 		goto nla_put_failure;
 
 	genlmsg_end(msg, hdr);

3/2 2026/09/15 18:14 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 34089
							output: 38
							thoughts: 1429
						

Thoughts:
**Fuzzing Candidate: Wireless Networking Stack Patch**

Okay, let's break this down. My initial assessment is that this patch series is highly likely to be a worthwhile candidate for fuzzing with syzkaller. Here's my thought process:

First, the file touches are immediately telling. We're looking at a broad swathe of the wireless subsystem. Specifically, it touches numerous files within `net/mac80211/*`, including core components like `rx.c`, `tx.c`, `offchannel.c`, and crucial network interface and link management, as well as `net/wireless/nl80211.c`, and `drivers/net/wireless/virtual/mac80211_hwsim_main.c`. This isn't just a minor tweak; it's deep within the heart of the mac80211 wireless networking stack, nl80211 netlink interface, and critically, `mac80211_hwsim`.

Now, remembering my core fuzzing principles – reachability is key – `mac80211_hwsim` is an immediate green flag. This software-emulated driver, used extensively by syzkaller, means the code is *directly* executable within a standard virtualized environment (QEMU, specifically, as that is the standard syzkaller config), which satisfies the primary gate for fuzzing applicability. Syzkaller is purpose-built to fuzz `nl80211` and `mac80211` *using* `mac80211_hwsim`, so it's a perfect match.

Moreover, the scope of the changes is significant. The patch is refactoring fundamental aspects of how the wireless stack resolves link stations, handles received frames, processes reordered aMPDUs, and integrates `sta` (station) information throughout the TX path. The addition of `NL80211_ATTR_FRAME_NO_STA` and its handling in `nl80211_tx_mgmt` and serialization indicates changes in the netlink interface, which is a common fuzzing target for syzkaller. These are all changes that affect critical paths.

So, *yes*, the presence of changes within the core of the wireless stack, the netlink interface, and particularly the involvement of `mac80211_hwsim` make this a very strong candidate for fuzzing. To solidify this, I'll now need to dig deeper into the actual commit details. Reading the files, diffs, and the git commit details will provide the finer context for the changes. However, my initial assessment is a resounding "WorthFuzzing = true."



4/2 2026/09/15 18:14 tool grepper
0m Args:
{
  "Expression": "NL80211_ATTR_FRAME_NO_STA",
  "PathPrefix": "net/wireless/"
}

Results:
{
  "Output": "net/wireless/nl80211.c=701=static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {\n--\nnet/wireless/nl80211.c-1099-\t[NL80211_ATTR_STA_DUMP_LINK_STATS] = { .type = NLA_FLAG },\nnet/wireless/nl80211.c:1100:\t[NL80211_ATTR_FRAME_NO_STA] = { .type = NLA_FLAG },\nnet/wireless/nl80211.c-1101-};\n--\nnet/wireless/nl80211.c=14778=static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)\n--\nnet/wireless/nl80211.c-14877-\tparams.no_sta =\nnet/wireless/nl80211.c:14878:\t\tnla_get_flag(info-\u003eattrs[NL80211_ATTR_FRAME_NO_STA]);\nnet/wireless/nl80211.c-14879-\n--\nnet/wireless/nl80211.c=22010=int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,\n--\nnet/wireless/nl80211.c-22050-\t    (info-\u003eno_sta \u0026\u0026\nnet/wireless/nl80211.c:22051:\t     nla_put_flag(msg, NL80211_ATTR_FRAME_NO_STA)))\nnet/wireless/nl80211.c-22052-\t\tgoto nla_put_failure;\n"
}

5/2 2026/09/15 18:14 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 36608
							output: 36
							thoughts: 269
						

Thoughts:
**Analyzing the Code Change**

Okay, first things first, let's figure out what's actually been modified. Is there a way to quickly check the Git commit log within this environment? Maybe a `grepper` command that could indirectly access the commit information? Hmm, no direct Git tools available, but wait, the `diff` itself is right here in the prompt. That's it! Looking at `commit 05665aae504027f5bfd53117ebba1243a0c0f83c: syz-cluster: applied patch under review`.

Now, the real work begins. I need to dive into the file modifications. This will allow me to identify the `FocusSymbols` and `EnableConfigs` associated with this patch. Understanding the scope of the change is crucial.

Next, I have to figure out the configuration requirements. What configurations are needed for the code affected by the commit to compile and run? Specifically, within the `mac80211` context. Let's see... I'm thinking: `CFG80211`, `MAC80211`, and `MAC80211_HWSIM` – that's the virtual device driver for mac80211 simulation. Oh, and of course, `NL80211`, which is part of `CFG80211`, so that's covered.

So now, time to hit the `Kconfig` files for `mac80211` and `mac80211_hwsim`. I'll have to see where things have changed.



6/2 2026/09/15 18:14 tool grepper
0m Args:
{
  "Expression": "config MAC80211",
  "PathPrefix": "net/mac80211/"
}

Results:
{
  "Output": "net/mac80211/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nnet/mac80211/Kconfig:2:config MAC80211\nnet/mac80211/Kconfig-3-\ttristate \"Generic IEEE 802.11 Networking Stack (mac80211)\"\n--\nnet/mac80211/Kconfig=19=if MAC80211 != n\nnet/mac80211/Kconfig-20-\nnet/mac80211/Kconfig:21:config MAC80211_HAS_RC\nnet/mac80211/Kconfig-22-\tbool\nnet/mac80211/Kconfig-23-\nnet/mac80211/Kconfig:24:config MAC80211_RC_MINSTREL\nnet/mac80211/Kconfig-25-\tbool \"Minstrel\" if EXPERT\n--\nnet/mac80211/Kconfig=31=choice\n--\nnet/mac80211/Kconfig-40-\nnet/mac80211/Kconfig:41:config MAC80211_RC_DEFAULT_MINSTREL\nnet/mac80211/Kconfig-42-\tbool \"Minstrel\"\n--\nnet/mac80211/Kconfig=48=endchoice\nnet/mac80211/Kconfig-49-\nnet/mac80211/Kconfig:50:config MAC80211_RC_DEFAULT\nnet/mac80211/Kconfig-51-\tstring\n--\nnet/mac80211/Kconfig=57=comment \"Some wireless drivers require a rate control algorithm\"\n--\nnet/mac80211/Kconfig-59-\nnet/mac80211/Kconfig:60:config MAC80211_KUNIT_TEST\nnet/mac80211/Kconfig-61-\ttristate \"KUnit tests for mac80211\" if !KUNIT_ALL_TESTS\n--\nnet/mac80211/Kconfig-69-\nnet/mac80211/Kconfig:70:config MAC80211_MESH\nnet/mac80211/Kconfig-71-\tbool \"Enable mac80211 mesh networking support\"\n--\nnet/mac80211/Kconfig-78-\nnet/mac80211/Kconfig:79:config MAC80211_LEDS\nnet/mac80211/Kconfig-80-\tbool \"Enable LED triggers\"\n--\nnet/mac80211/Kconfig-87-\nnet/mac80211/Kconfig:88:config MAC80211_DEBUGFS\nnet/mac80211/Kconfig-89-\tbool \"Export mac80211 internals in DebugFS\"\n--\nnet/mac80211/Kconfig-96-\nnet/mac80211/Kconfig:97:config MAC80211_MESSAGE_TRACING\nnet/mac80211/Kconfig-98-\tbool \"Trace all mac80211 debug messages\"\n--\nnet/mac80211/Kconfig-109-\nnet/mac80211/Kconfig:110:menuconfig MAC80211_DEBUG_MENU\nnet/mac80211/Kconfig-111-\tbool \"Select mac80211 debugging features\"\n--\nnet/mac80211/Kconfig-115-\nnet/mac80211/Kconfig:116:config MAC80211_NOINLINE\nnet/mac80211/Kconfig-117-\tbool \"Do not inline TX/RX handlers\"\n--\nnet/mac80211/Kconfig-131-\nnet/mac80211/Kconfig:132:config MAC80211_VERBOSE_DEBUG\nnet/mac80211/Kconfig-133-\tbool \"Verbose debugging output\"\n--\nnet/mac80211/Kconfig-142-\nnet/mac80211/Kconfig:143:config MAC80211_MLME_DEBUG\nnet/mac80211/Kconfig-144-\tbool \"Verbose managed MLME output\"\n--\nnet/mac80211/Kconfig-153-\nnet/mac80211/Kconfig:154:config MAC80211_STA_DEBUG\nnet/mac80211/Kconfig-155-\tbool \"Verbose station debugging\"\n--\nnet/mac80211/Kconfig-162-\nnet/mac80211/Kconfig:163:config MAC80211_HT_DEBUG\nnet/mac80211/Kconfig-164-\tbool \"Verbose HT debugging\"\n--\nnet/mac80211/Kconfig-174-\nnet/mac80211/Kconfig:175:config MAC80211_OCB_DEBUG\nnet/mac80211/Kconfig-176-\tbool \"Verbose OCB debugging\"\n--\nnet/mac80211/Kconfig-185-\nnet/mac80211/Kconfig:186:config MAC80211_IBSS_DEBUG\nnet/mac80211/Kconfig-187-\tbool \"Verbose IBSS debugging\"\n--\nnet/mac80211/Kconfig-196-\nnet/mac80211/Kconfig:197:config MAC80211_PS_DEBUG\nnet/mac80211/Kconfig-198-\tbool \"Verbose powersave mode debugging\"\n--\nnet/mac80211/Kconfig-208-\nnet/mac80211/Kconfig:209:config MAC80211_MPL_DEBUG\nnet/mac80211/Kconfig-210-\tbool \"Verbose mesh peer link debugging\"\n--\nnet/mac80211/Kconfig-221-\nnet/mac80211/Kconfig:222:config MAC80211_MPATH_DEBUG\nnet/mac80211/Kconfig-223-\tbool \"Verbose mesh path debugging\"\n--\nnet/mac80211/Kconfig-234-\nnet/mac80211/Kconfig:235:config MAC80211_MHWMP_DEBUG\nnet/mac80211/Kconfig-236-\tbool \"Verbose mesh HWMP routing debugging\"\n--\nnet/mac80211/Kconfig-247-\nnet/mac80211/Kconfig:248:config MAC80211_MESH_SYNC_DEBUG\nnet/mac80211/Kconfig-249-\tbool \"Verbose mesh synchronization debugging\"\n--\nnet/mac80211/Kconfig-258-\nnet/mac80211/Kconfig:259:config MAC80211_MESH_CSA_DEBUG\nnet/mac80211/Kconfig-260-\tbool \"Verbose mesh channel switch debugging\"\n--\nnet/mac80211/Kconfig-269-\nnet/mac80211/Kconfig:270:config MAC80211_MESH_PS_DEBUG\nnet/mac80211/Kconfig-271-\tbool \"Verbose mesh powersave debugging\"\n--\nnet/mac80211/Kconfig-280-\nnet/mac80211/Kconfig:281:config MAC80211_TDLS_DEBUG\nnet/mac80211/Kconfig-282-\tbool \"Verbose TDLS debugging\"\n--\nnet/mac80211/Kconfig-292-\nnet/mac80211/Kconfig:293:config MAC80211_DEBUG_COUNTERS\nnet/mac80211/Kconfig-294-\tbool \"Extra statistics for TX/RX debugging\"\n--\nnet/mac80211/Kconfig-307-\nnet/mac80211/Kconfig:308:config MAC80211_STA_HASH_MAX_SIZE\nnet/mac80211/Kconfig-309-\tint \"Station hash table maximum size\" if MAC80211_DEBUG_MENU\n"
}

7/2 2026/09/15 18:14 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 39454
							output: 37
							thoughts: 14
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Diving into MAC80211 Dependencies**

Okay, time to get my hands dirty. I'm going to start by taking a close look at the dependencies of MAC80211. Specifically, I need to get a clear picture of what other kernel modules or components it relies on. I'm thinking beyond the obvious; I need to consider the more subtle, perhaps less-documented, integrations. Are there any indirect or undocumented dependencies that might bite me later? The goal is to build a solid, holistic understanding of its ecosystem. I'm talking about kernel headers, APIs, and configuration options that are essential for it to function correctly. I'll need to identify potential bottlenecks or areas where future changes in dependencies could impact stability or performance. Time to dig into the makefiles, configuration options, and kernel documentation... let's see what we can find.



8/2 2026/09/15 18:14 tool read-file
0m Args:
{
  "File": "net/mac80211/Kconfig",
  "FirstLine": 1,
  "LineCount": 25
}

Results:
{
  "Contents": "   1:\t# SPDX-License-Identifier: GPL-2.0-only\n   2:\tconfig MAC80211\n   3:\t\ttristate \"Generic IEEE 802.11 Networking Stack (mac80211)\"\n   4:\t\tdepends on CFG80211\n   5:\t\tselect CRYPTO\n   6:\t\tselect CRYPTO_LIB_AES_CBC_MACS\n   7:\t\tselect CRYPTO_LIB_ARC4\n   8:\t\tselect CRYPTO_AES\n   9:\t\tselect CRYPTO_CCM\n  10:\t\tselect CRYPTO_GCM\n  11:\t\tselect CRC32\n  12:\t\thelp\n  13:\t\t  This option enables the hardware independent IEEE 802.11\n  14:\t\t  networking stack.\n  15:\t\n  16:\tcomment \"CFG80211 needs to be enabled for MAC80211\"\n  17:\t\tdepends on CFG80211=n\n  18:\t\n  19:\tif MAC80211 != n\n  20:\t\n  21:\tconfig MAC80211_HAS_RC\n  22:\t\tbool\n  23:\t\n  24:\tconfig MAC80211_RC_MINSTREL\n  25:\t\tbool \"Minstrel\" if EXPERT\n"
}

9/2 2026/09/15 18:14 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 40051
							output: 37
							thoughts: 35
						

Thoughts:
Here's my take:

**Investigating Potential Simulation Environments**

Okay, so I'm thinking, beyond the obvious, it's worth taking a look at potential hardware simulation capabilities within the kernel's wireless drivers. Specifically, I should check the `drivers/net/wireless/virtual/` directory, and also in the general `drivers/net/wireless/` location. There may be a driver named `MAC80211_HWSIM` that's designed for hardware simulation. If it exists, it could offer a valuable framework or specific examples I can leverage. This could be instrumental in setting up the environment I need, or at least help me understand how others have tackled similar challenges.



10/2 2026/09/15 18:14 tool grepper
0m Args:
{
  "Expression": "config MAC80211_HWSIM",
  "PathPrefix": "drivers/net/wireless/"
}

Results:
{
  "Output": "drivers/net/wireless/virtual/Kconfig:1:config MAC80211_HWSIM\ndrivers/net/wireless/virtual/Kconfig-2-\ttristate \"Simulated radio testing tool for mac80211\"\n"
}

11/2 2026/09/15 18:14 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 40341
							output: 36
							thoughts: 16
						

Thoughts:
Here's a summary of my thought process, as if I were considering that Kconfig file:

**Analyzing the Virtual Wireless Network Kconfig**

Okay, time to dive into `drivers/net/wireless/virtual/Kconfig`. Let's see what's defined here and how the virtual wireless networking features are configured. Given my understanding of the kernel's networking subsystem and wireless drivers, I'm expecting to find options related to creating virtual network interfaces, perhaps emulating wireless behavior for testing, or providing a framework for software-defined radios.

I'll be looking for configuration symbols – those `config` lines – to understand the core features. I'll need to pay close attention to the dependencies (`depends on`) to understand the prerequisites for each feature. What other wireless subsystems, or even general networking components, must be enabled before these virtual drivers can be brought online? I'll be noting any `select` statements, as they tell me which other options are automatically enabled when specific virtual features are chosen.

Then, I'll need to figure out the default values and help text. The defaults give me a baseline, and the help text will tell me the intended use case for each feature and whether it should be enabled directly, or if it is part of a larger functionality. This should clue me into the design decisions behind the virtual driver.

This is all about getting a solid grasp of how these virtual wireless features are intended to fit into the broader kernel landscape. The relationships between features are critical and, through this analysis of the Kconfig, I hope to fully grasp the structure of the relevant components.



12/2 2026/09/15 18:14 tool read-file
0m Args:
{
  "File": "drivers/net/wireless/virtual/Kconfig",
  "FirstLine": 1,
  "LineCount": 25
}

Results:
{
  "Contents": "   1:\tconfig MAC80211_HWSIM\n   2:\t\ttristate \"Simulated radio testing tool for mac80211\"\n   3:\t\tdepends on MAC80211\n   4:\t\thelp\n   5:\t\t  This driver is a developer testing tool that can be used to test\n   6:\t\t  IEEE 802.11 networking stack (mac80211) functionality. This is not\n   7:\t\t  needed for normal wireless LAN usage and is only for testing. See\n   8:\t\t  Documentation/networking/mac80211_hwsim for more information on how\n   9:\t\t  to use this tool.\n  10:\t\n  11:\t\t  To compile this driver as a module, choose M here: the module will be\n  12:\t\t  called mac80211_hwsim.  If unsure, say N.\n  13:\t\n  14:\tconfig VIRT_WIFI\n  15:\t\ttristate \"Wifi wrapper for ethernet drivers\"\n  16:\t\tdepends on CFG80211\n  17:\t\thelp\n  18:\t\t  This option adds support for ethernet connections to appear as if they\n  19:\t\t  are wifi connections through a special rtnetlink device.\n  20:\t\n"
}

13/2 2026/09/15 18:14 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 41051
							output: 32
							thoughts: 82
						

Thoughts:
Here's my thought process, as it stands:

**Navigating the Wireless Stack Dependencies**

Okay, so I'm digging into `MAC80211_HWSIM` and I need to understand its dependencies. It looks like it's built on top of `MAC80211`, which itself relies on `CFG80211`. That makes sense - `MAC80211` is the core wireless networking stack, and `CFG80211` is the configuration interface using nl80211. Good. To get a better handle on `CFG80211`, I should check the kernel configuration to understand how it's defined. So, let's head over to `net/wireless/Kconfig` and see what that file reveals. That should give me the relevant dependencies and build options for `CFG80211`.



14/2 2026/09/15 18:15 tool grepper
0m Args:
{
  "Expression": "config CFG80211",
  "PathPrefix": "net/wireless/"
}

Results:
{
  "Output": "net/wireless/Kconfig=14=config WEXT_PRIV\n--\nnet/wireless/Kconfig-16-\nnet/wireless/Kconfig:17:config CFG80211\nnet/wireless/Kconfig-18-\ttristate \"cfg80211 - wireless configuration API\"\n--\nnet/wireless/Kconfig=38=config NL80211_TESTMODE\n--\nnet/wireless/Kconfig-51-\nnet/wireless/Kconfig:52:config CFG80211_DEVELOPER_WARNINGS\nnet/wireless/Kconfig-53-\tbool \"enable developer warnings\"\n--\nnet/wireless/Kconfig-67-\nnet/wireless/Kconfig:68:config CFG80211_CERTIFICATION_ONUS\nnet/wireless/Kconfig-69-\tbool \"cfg80211 certification onus\"\n--\nnet/wireless/Kconfig-88-\nnet/wireless/Kconfig:89:config CFG80211_REQUIRE_SIGNED_REGDB\nnet/wireless/Kconfig-90-\tbool \"require regdb signature\" if CFG80211_CERTIFICATION_ONUS\n--\nnet/wireless/Kconfig-98-\nnet/wireless/Kconfig:99:config CFG80211_USE_KERNEL_REGDB_KEYS\nnet/wireless/Kconfig-100-\tbool \"allow regdb keys shipped with the kernel\" if CFG80211_CERTIFICATION_ONUS\n--\nnet/wireless/Kconfig-110-\nnet/wireless/Kconfig:111:config CFG80211_EXTRA_REGDB_KEYDIR\nnet/wireless/Kconfig-112-\tstring \"additional regdb key directory\" if CFG80211_CERTIFICATION_ONUS\n--\nnet/wireless/Kconfig-121-\nnet/wireless/Kconfig:122:config CFG80211_REG_CELLULAR_HINTS\nnet/wireless/Kconfig-123-\tbool \"cfg80211 regulatory support for cellular base station hints\"\n--\nnet/wireless/Kconfig-134-\nnet/wireless/Kconfig:135:config CFG80211_REG_RELAX_NO_IR\nnet/wireless/Kconfig-136-\tbool \"cfg80211 support for NO_IR relaxation\"\n--\nnet/wireless/Kconfig-158-\nnet/wireless/Kconfig:159:config CFG80211_DEFAULT_PS\nnet/wireless/Kconfig-160-\tbool \"enable powersave by default\"\n--\nnet/wireless/Kconfig-168-\nnet/wireless/Kconfig:169:config CFG80211_DEBUGFS\nnet/wireless/Kconfig-170-\tbool \"cfg80211 DebugFS entries\"\n--\nnet/wireless/Kconfig-176-\nnet/wireless/Kconfig:177:config CFG80211_CRDA_SUPPORT\nnet/wireless/Kconfig-178-\tbool \"support CRDA\" if EXPERT\n--\nnet/wireless/Kconfig-186-\nnet/wireless/Kconfig:187:config CFG80211_WEXT\nnet/wireless/Kconfig-188-\tbool \"cfg80211 wireless extensions compatibility\"\n--\nnet/wireless/Kconfig-193-\nnet/wireless/Kconfig:194:config CFG80211_KUNIT_TEST\nnet/wireless/Kconfig-195-\ttristate \"KUnit tests for cfg80211\" if !KUNIT_ALL_TESTS\n"
}

15/2 2026/09/15 18:15 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 42278
							output: 34
							thoughts: 464
						

Thoughts:
**My Assessment of the Wireless Driver API Changes**

Okay, so I need to get a handle on these changes. My initial thought is to quickly ascertain whether this impacts just the core MAC80211/CFG80211/MAC80211_HWSIM, or if other wireless driver families need attention too. Specifically, I'll need to check ath11k, ath12k, iwlwifi, mt76, and rtw89 configurations.

Right, the core of this seems to be concentrated within `net/mac80211` and `net/wireless`. The drivers I mentioned are clearly adapting to a significant API shift within the `ieee80211_rx` path. Specifically, `ieee80211_rx_napi` and `ieee80211_rx_list` now use `struct ieee80211_link_sta *link_sta` instead of the old `struct ieee80211_sta *sta`. That's a big change in data structures that'll cascade through those drivers. And I see they've also removed `status->link_valid` from `struct ieee80211_rx_status`.

Interesting, `mac80211_hwsim` has also been updated, so my virtual wireless interfaces are affected. That's a good test bed, especially since syzkaller is involved. The way it works is important; it generates nl80211 commands, then transmits and receives the wireless frames.

Now, let's drill down into `net/wireless/nl80211.c`, and specifically `nl80211_tx_mgmt`. I need to see how it's called. The user space sends `NL80211_CMD_FRAME` via generic netlink, right? So, I need to look at the callers of `nl80211_tx_mgmt` and the netlink operations to understand how this is driven. That'll give me the bigger picture of where these changes are felt.



16/2 2026/09/15 18:15 tool grepper
0m Args:
{
  "Expression": "nl80211_tx_mgmt",
  "PathPrefix": "net/wireless/"
}

Results:
{
  "Output": "net/wireless/nl80211.c=14720=static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)\n--\nnet/wireless/nl80211.c-14777-\nnet/wireless/nl80211.c:14778:static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)\nnet/wireless/nl80211.c-14779-{\n--\nnet/wireless/nl80211.c-14926-\nnet/wireless/nl80211.c:14927:static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info)\nnet/wireless/nl80211.c-14928-{\n--\nnet/wireless/nl80211.c=19756=static const struct genl_small_ops nl80211_small_ops[] = {\n--\nnet/wireless/nl80211.c-20143-\t\t.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,\nnet/wireless/nl80211.c:20144:\t\t.doit = nl80211_tx_mgmt,\nnet/wireless/nl80211.c-20145-\t\t.flags = GENL_UNS_ADMIN_PERM,\n--\nnet/wireless/nl80211.c-20150-\t\t.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,\nnet/wireless/nl80211.c:20151:\t\t.doit = nl80211_tx_mgmt_cancel_wait,\nnet/wireless/nl80211.c-20152-\t\t.flags = GENL_UNS_ADMIN_PERM,\n"
}

17/2 2026/09/15 18:15 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 43773
							output: 33
							thoughts: 174
						

Thoughts:
**Vulnerability Discovered: Potential Unprivileged User Control via nl80211_tx_mgmt**

Okay, so I've just stumbled upon something potentially very interesting. It seems the `nl80211_tx_mgmt` function, which handles the `NL80211_CMD_FRAME` command, has `GENL_UNS_ADMIN_PERM`. And that, my friends, means an unprivileged user, even one within a user namespace, can potentially call this directly. That's a red flag immediately because Syzkaller can, of course, reach this functionality.

Now, let's trace this further. `nl80211_tx_mgmt` calls `rdev_mgmt_tx(rdev, ...)`, which in turn calls `rdev->ops->mgmt_tx(...)`. I'm going to bet that's `ieee80211_mgmt_tx` in `net/mac80211/offchannel.c`. Just a hunch, of course, but that would make perfect sense. Time to verify this in the source, just to be sure. I'm going to open up the code and confirm this immediately.



18/2 2026/09/15 18:15 tool grepper
0m Args:
{
  "Expression": "\\.mgmt_tx",
  "PathPrefix": "net/mac80211/"
}

Results:
{
  "Output": "net/mac80211/cfg.c=5976=const struct cfg80211_ops mac80211_config_ops = {\n--\nnet/mac80211/cfg.c-6038-\t.cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,\nnet/mac80211/cfg.c:6039:\t.mgmt_tx = ieee80211_mgmt_tx,\nnet/mac80211/cfg.c:6040:\t.mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait,\nnet/mac80211/cfg.c-6041-\t.set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,\n"
}

19/2 2026/09/15 18:15 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 44472
							output: 38
							thoughts: 119
						

Thoughts:
**Analyzing the mac80211 Hardware Simulation**

Okay, so I'm looking at how the `mac80211` layer interacts with the simulated hardware in `mac80211_hwsim`. I see that the management frame transmission function, `mac80211_config_ops.mgmt_tx`, is assigned to `ieee80211_mgmt_tx`. This makes sense; it's the standard function for handling management frame transmission within the `ieee80211` stack.

Now, on the reception side, things are a bit more interesting. `mac80211_hwsim` gets its frames either through the netlink interface, or from other simulated radios. It then funnels these frames up to `mac80211` using the `ieee80211_rx_list` or `ieee80211_rx_napi` functions. I need to dig deeper into the reception path now. I should examine the code in `mac80211_hwsim_main.c` to understand how the received frames are injected into the kernel and handled. I'm going to look in this area next.



20/2 2026/09/15 18:15 tool grepper
0m Args:
{
  "Expression": "ieee80211_rx_",
  "PathPrefix": "drivers/net/wireless/virtual/"
}

Results:
{
  "Output": "drivers/net/wireless/virtual/mac80211_hwsim_main.c=1774=struct tx_iter_data {\ndrivers/net/wireless/virtual/mac80211_hwsim_main.c-1775-\tstruct ieee80211_channel *channel;\ndrivers/net/wireless/virtual/mac80211_hwsim_main.c:1776:\tstruct ieee80211_rx_status *rx_status;\ndrivers/net/wireless/virtual/mac80211_hwsim_main.c-1777-\tstruct ieee80211_hw *hw;\n--\ndrivers/net/wireless/virtual/mac80211_hwsim_main.c=1863=static void mac80211_hwsim_rx(struct mac80211_hwsim_data *data,\ndrivers/net/wireless/virtual/mac80211_hwsim_main.c:1864:\t\t\t      struct ieee80211_rx_status *rx_status,\ndrivers/net/wireless/virtual/mac80211_hwsim_main.c-1865-\t\t\t      struct sk_buff *skb)\n--\ndrivers/net/wireless/virtual/mac80211_hwsim_main.c-1898-\tdata-\u003erx_bytes += skb-\u003elen;\ndrivers/net/wireless/virtual/mac80211_hwsim_main.c:1899:\tieee80211_rx_irqsafe(data-\u003ehw, skb);\ndrivers/net/wireless/virtual/mac80211_hwsim_main.c-1900-}\n--\ndrivers/net/wireless/virtual/mac80211_hwsim_main.c=1902=static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/virtual/mac80211_hwsim_main.c-1909-\tstruct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);\ndrivers/net/wireless/virtual/mac80211_hwsim_main.c:1910:\tstruct ieee80211_rx_status rx_status;\ndrivers/net/wireless/virtual/mac80211_hwsim_main.c-1911-\tu64 sim_tsf = mac80211_hwsim_get_sim_tsf();\n--\ndrivers/net/wireless/virtual/mac80211_hwsim_main.c=6489=static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,\n--\ndrivers/net/wireless/virtual/mac80211_hwsim_main.c-6492-\tstruct mac80211_hwsim_data *data2;\ndrivers/net/wireless/virtual/mac80211_hwsim_main.c:6493:\tstruct ieee80211_rx_status rx_status;\ndrivers/net/wireless/virtual/mac80211_hwsim_main.c-6494-\tstruct ieee80211_hdr *hdr;\n--\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.c=216=void mac80211_hwsim_nan_rx(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.c-223-\tconst struct ieee80211_nan_attr *nan_attr;\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.c:224:\tstruct ieee80211_rx_status rx_status;\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.c-225-\tbool joined_cluster = false;\n--\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.c-241-\tmemcpy(\u0026rx_status, IEEE80211_SKB_RXCB(skb),\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.c:242:\t       sizeof(struct ieee80211_rx_status));\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.c-243-\n--\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.c=1226=bool mac80211_hwsim_nan_receive(struct ieee80211_hw *hw,\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.c-1227-\t\t\t\tstruct ieee80211_channel *channel,\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.c:1228:\t\t\t\tstruct ieee80211_rx_status *rx_status)\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.c-1229-{\n--\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.h=92=bool mac80211_hwsim_nan_receive(struct ieee80211_hw *hw,\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.h-93-\t\t\t\tstruct ieee80211_channel *channel,\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.h:94:\t\t\t\tstruct ieee80211_rx_status *rx_status);\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.h-95-\n"
}

21/2 2026/09/15 18:15 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 46394
							output: 42
							thoughts: 25
						

Thoughts:
Here's my take on those thoughts:

**Dissecting the `ieee80211_rx_irqsafe` Call**

Okay, so I'm digging into this `ieee80211_rx_irqsafe` function. I need to understand its role, and the best way to do that is to trace its execution. Specifically, I want to know precisely what other functions and data structures it's interacting with. The name strongly suggests it's handling the receiving of 802.11 frames in a safe manner within an interrupt context. But I need to see the actual implementation. Time to crack open the source code and follow the call stack. Let's see what this routine is actually *doing* under the hood. What else does it touch? Where does the data flow? This is key to understanding the overall receive path. Let's start digging.



22/2 2026/09/15 18:15 tool grepper
0m Args:
{
  "Expression": "ieee80211_rx_irqsafe",
  "PathPrefix": "net/mac80211/"
}

Results:
{
  "Output": "net/mac80211/rx.c=5791=EXPORT_SYMBOL(ieee80211_rx_napi);\n--\nnet/mac80211/rx.c-5794- * context. Post the skb on the queue and schedule the tasklet */\nnet/mac80211/rx.c:5795:void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb)\nnet/mac80211/rx.c-5796-{\n--\nnet/mac80211/rx.c-5804-}\nnet/mac80211/rx.c:5805:EXPORT_SYMBOL(ieee80211_rx_irqsafe);\n"
}

23/2 2026/09/15 18:15 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 46871
							output: 41
							thoughts: 35
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Diving into `ieee80211_rx_irqsafe`**

Okay, I'm going to take a look at the `ieee80211_rx_irqsafe` function in `net/mac80211/rx.c`. I need to refresh my memory on how this critical function handles received frames, especially within the interrupt context. I want to understand its locking strategy, the interactions with hardware, and how it handles potential race conditions. Specifically, I'm interested in how it manages the workqueue and DMA buffers, and where it makes the crucial decision to pass packets up the stack. I'm hoping to get a clear picture of how it provides the safety guarantee that the name implies, and I expect there may be areas for potential optimization or clarification. I'll focus on what it does immediately after a hardware interrupt, and how it safely queues the processing of those packets.



24/2 2026/09/15 18:15 tool read-file
0m Args:
{
  "File": "net/mac80211/rx.c",
  "FirstLine": 5793,
  "LineCount": 30
}

Results:
{
  "Contents": "5793:\t/* This is a version of the rx handler that can be called from hard irq\n5794:\t * context. Post the skb on the queue and schedule the tasklet */\n5795:\tvoid ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb)\n5796:\t{\n5797:\t\tstruct ieee80211_local *local = hw_to_local(hw);\n5798:\t\n5799:\t\tBUILD_BUG_ON(sizeof(struct ieee80211_rx_status) \u003e sizeof(skb-\u003ecb));\n5800:\t\n5801:\t\tskb-\u003epkt_type = IEEE80211_RX_MSG;\n5802:\t\tskb_queue_tail(\u0026local-\u003eskb_queue, skb);\n5803:\t\ttasklet_schedule(\u0026local-\u003etasklet);\n5804:\t}\n5805:\tEXPORT_SYMBOL(ieee80211_rx_irqsafe);\n"
}

25/2 2026/09/15 18:15 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 47450
							output: 36
							thoughts: 21
						

Thoughts:
Here's my take on the thought process, as requested:

**Deconstructing a Tasklet Execution**

Okay, so I'm looking at this code and I need to understand exactly what gets executed when `local->tasklet` is invoked. The variable `local->tasklet`... that's the key. I need to know *precisely* what that pointer is referencing. So, let's just trace that and see where it goes.



26/2 2026/09/15 18:15 tool grepper
0m Args:
{
  "Expression": "tasklet_setup.*tasklet",
  "PathPrefix": "net/mac80211/"
}

Results:
{
  "Output": "net/mac80211/main.c=793=struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,\n--\nnet/mac80211/main.c-1031-\t}\nnet/mac80211/main.c:1032:\ttasklet_setup(\u0026local-\u003etx_pending_tasklet, ieee80211_tx_pending);\nnet/mac80211/main.c:1033:\ttasklet_setup(\u0026local-\u003ewake_txqs_tasklet, ieee80211_wake_txqs);\nnet/mac80211/main.c:1034:\ttasklet_setup(\u0026local-\u003etasklet, ieee80211_tasklet_handler);\nnet/mac80211/main.c-1035-\n"
}

27/2 2026/09/15 18:15 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 47846
							output: 41
							thoughts: 21
						

Thoughts:
Okay, let's dive into this `ieee80211_tasklet_handler`. I need to understand what this function is actually *doing*. I'm assuming, given the naming, that this has something to do with handling tasks within the IEEE 802.11 wireless networking context, likely within a driver or core networking stack. The "tasklet" part indicates this is likely dealing with deferred work, something that needs to be handled, but can't be done immediately within an interrupt context, probably due to locking issues or the need for a more complex execution environment. I'll need to figure out the context in which this tasklet is triggered – what events are kicking it off? What data is it operating on? Is it handling incoming frames, outgoing transmissions, beacon processing, or control frame management? Knowing the data structures this function accesses is crucial to understand its function. I need to trace this function back to its callers, and understand the surrounding logic. I will need to look into any related data structures it uses, such as buffers, control blocks, and queue descriptors. My initial impression is that this probably deals with processing information related to a wireless network, but I need to dive deeper to determine exactly what it does.



28/2 2026/09/15 18:15 tool grepper
0m Args:
{
  "Expression": "ieee80211_tasklet_handler",
  "PathPrefix": "net/mac80211/"
}

Results:
{
  "Output": "net/mac80211/main.c=441=void ieee80211_handle_queued_frames(struct ieee80211_local *local)\n--\nnet/mac80211/main.c-466-\nnet/mac80211/main.c:467:static void ieee80211_tasklet_handler(struct tasklet_struct *t)\nnet/mac80211/main.c-468-{\n--\nnet/mac80211/main.c=793=struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,\n--\nnet/mac80211/main.c-1033-\ttasklet_setup(\u0026local-\u003ewake_txqs_tasklet, ieee80211_wake_txqs);\nnet/mac80211/main.c:1034:\ttasklet_setup(\u0026local-\u003etasklet, ieee80211_tasklet_handler);\nnet/mac80211/main.c-1035-\n"
}

29/2 2026/09/15 18:15 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 48469
							output: 40
							thoughts: 29
						

Thoughts:
Okay, let's dive into this. I'm going to pull up `net/mac80211/main.c` and focus on lines 440 through 475. I need to refresh my memory on what that section's handling. Is it initialization, association, or maybe some regulatory domain stuff? Given the context of `main.c`, I'm leaning towards initial setup or overall driver management functions. I'll quickly scan that block of code, paying close attention to any calls to other functions or any conditional statements. I'm looking for clues about how the driver interacts with the hardware, configures the radio, or establishes the initial connection state. My goal is to quickly pinpoint the specific functionality in that section, so I can understand the bigger picture more quickly. Knowing the codebase, I'm expecting some standard mac80211 idioms, so it should be relatively straightforward to understand, even without a deep dive. Time to get my hands dirty.



30/2 2026/09/15 18:15 tool read-file
0m Args:
{
  "File": "net/mac80211/main.c",
  "FirstLine": 440,
  "LineCount": 35
}

Results:
{
  "Contents": " 440:\t/* context: requires softirqs disabled */\n 441:\tvoid ieee80211_handle_queued_frames(struct ieee80211_local *local)\n 442:\t{\n 443:\t\tstruct sk_buff *skb;\n 444:\t\n 445:\t\twhile ((skb = skb_dequeue(\u0026local-\u003eskb_queue)) ||\n 446:\t\t       (skb = skb_dequeue(\u0026local-\u003eskb_queue_unreliable))) {\n 447:\t\t\tswitch (skb-\u003epkt_type) {\n 448:\t\t\tcase IEEE80211_RX_MSG:\n 449:\t\t\t\t/* Clear skb-\u003epkt_type in order to not confuse kernel\n 450:\t\t\t\t * netstack. */\n 451:\t\t\t\tskb-\u003epkt_type = 0;\n 452:\t\t\t\tieee80211_rx(\u0026local-\u003ehw, skb);\n 453:\t\t\t\tbreak;\n 454:\t\t\tcase IEEE80211_TX_STATUS_MSG:\n 455:\t\t\t\tskb-\u003epkt_type = 0;\n 456:\t\t\t\tieee80211_tx_status_skb(\u0026local-\u003ehw, skb);\n 457:\t\t\t\tbreak;\n 458:\t\t\tdefault:\n 459:\t\t\t\tWARN(1, \"mac80211: Packet is of unknown type %d\\n\",\n 460:\t\t\t\t     skb-\u003epkt_type);\n 461:\t\t\t\tdev_kfree_skb(skb);\n 462:\t\t\t\tbreak;\n 463:\t\t\t}\n 464:\t\t}\n 465:\t}\n 466:\t\n 467:\tstatic void ieee80211_tasklet_handler(struct tasklet_struct *t)\n 468:\t{\n 469:\t\tstruct ieee80211_local *local = from_tasklet(local, t, tasklet);\n 470:\t\n 471:\t\tieee80211_handle_queued_frames(local);\n 472:\t}\n 473:\t\n 474:\tstatic void ieee80211_restart_work(struct work_struct *work)\n"
}

31/2 2026/09/15 18:15 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 49342
							output: 41
							thoughts: 770
						

Thoughts:
**Analyzing the mac80211 RX Path for Potential Vulnerabilities**

Okay, so I'm tracing the data flow here, starting from `ieee80211_rx`, which then calls `ieee80211_rx_list`, which in turn calls either `__ieee80211_rx_handle_packet` or `__ieee80211_rx_handle_8023`. The patch introduces modifications in both of those functions, so that's where I need to focus.

Specifically, I'm diving into `net/mac80211/rx.c` to look at the added code in `ieee80211_rx_handlers`. I see the calls to `__ieee80211_queue_skb_to_iface` and, of particular interest, the logic dealing with `rx->link_id`.  My spidey senses are tingling... there's a potential race condition or vulnerability brewing here.

Here's the snippet that's got my attention:

```c
		rx->skb = skb;
		if (ieee80211_vif_is_mld(&rx->sdata->vif) &&
		    unlikely(rx->link_id != status->link_id)) {
			if (rx->sta) {
				rx->link_sta =
					rcu_dereference(rx->sta->link[rx->link_id]);

				/* Link got disabled, just use deflink */
				if (!rx->link_sta)
					rx->link_sta = &rx->sta->deflink;
			}
			rx->link_id = status->link_id;
			rx->link = rcu_dereference(rx->sdata->link[status->link_id]);
		}
```

Wait a minute... I see it! The critical point is on line 4240: `rx->link_id != status->link_id`. Then at line 4242: `rx->link_sta = rcu_dereference(rx->sta->link[rx->link_id]);`. And THEN `rx->link_id = status->link_id;`. The `rx->link_id` is used to index into the `rx->sta->link` array *before* it's updated with the *new* link ID from `status->link_id`.

And if the old `rx->link_id` is an invalid index, say -1 due to uninitialized value? Or if it's out of bounds? That's a potential out-of-bounds read and a major problem!  I need to figure out what the initial value of `rx->link_id` is! I need to trace back to where it is initialized to see if it causes a problem.

I need to dig deeper in this file, probably around lines 4220-4260, to see what value `rx->link_id` is initialized to, and under what circumstances this code is even entered. This looks like a serious bug, and I need to determine the scope of this potentially memory corruption vulnerability.



32/2 2026/09/15 18:15 tool read-file
0m Args:
{
  "File": "net/mac80211/rx.c",
  "FirstLine": 4220,
  "LineCount": 45
}

Results:
{
  "Contents": "4220:\t\n4221:\t\t/* Lock here to avoid hitting all of the data used in the RX\n4222:\t\t * path (e.g. key data, station data, ...) concurrently when\n4223:\t\t * a frame is released from the reorder buffer due to timeout\n4224:\t\t * from the timer, potentially concurrently with RX from the\n4225:\t\t * driver.\n4226:\t\t */\n4227:\t\tspin_lock_bh(\u0026rx-\u003elocal-\u003erx_path_lock);\n4228:\t\n4229:\t\twhile ((skb = __skb_dequeue(frames))) {\n4230:\t\t\tstruct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);\n4231:\t\n4232:\t\t\t/*\n4233:\t\t\t * Most fields are valid across frames. However,\n4234:\t\t\t * in the case of reordering frames may have arrived\n4235:\t\t\t * on different links, so adjust the link_id, link\n4236:\t\t\t * and link_sta accordingly.\n4237:\t\t\t */\n4238:\t\t\trx-\u003eskb = skb;\n4239:\t\t\tif (ieee80211_vif_is_mld(\u0026rx-\u003esdata-\u003evif) \u0026\u0026\n4240:\t\t\t    unlikely(rx-\u003elink_id != status-\u003elink_id)) {\n4241:\t\t\t\tif (rx-\u003esta) {\n4242:\t\t\t\t\trx-\u003elink_sta =\n4243:\t\t\t\t\t\trcu_dereference(rx-\u003esta-\u003elink[rx-\u003elink_id]);\n4244:\t\n4245:\t\t\t\t\t/* Link got disabled, just use deflink */\n4246:\t\t\t\t\tif (!rx-\u003elink_sta)\n4247:\t\t\t\t\t\trx-\u003elink_sta = \u0026rx-\u003esta-\u003edeflink;\n4248:\t\t\t\t}\n4249:\t\t\t\trx-\u003elink_id = status-\u003elink_id;\n4250:\t\t\t\trx-\u003elink = rcu_dereference(rx-\u003esdata-\u003elink[status-\u003elink_id]);\n4251:\t\t\t}\n4252:\t\n4253:\t\t\tif (WARN_ON_ONCE(!rx-\u003elink)) {\n4254:\t\t\t\tres = RX_DROP_U_NO_LINK;\n4255:\t\t\t\tgoto rxh_next;\n4256:\t\t\t}\n4257:\t\n4258:\t\t\tCALL_RXH(ieee80211_rx_h_check_more_data);\n4259:\t\t\tCALL_RXH(ieee80211_rx_h_uapsd_and_pspoll);\n4260:\t\t\tCALL_RXH(ieee80211_rx_h_sta_process);\n4261:\t\t\tCALL_RXH(ieee80211_rx_h_decrypt);\n4262:\t\t\tCALL_RXH(ieee80211_rx_h_defragment);\n4263:\t\t\tCALL_RXH(ieee80211_rx_h_michael_mic_verify);\n4264:\t\t\t/* must be after MMIC verify so header is counted in MPDU mic */\n"
}

33/2 2026/09/15 18:15 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 51684
							output: 40
							thoughts: 66
						

Thoughts:
Here's my take on those thoughts:

**Analyzing a Potential IEEE 802.11 RX Issue**

Okay, so I'm digging into this `ieee80211_rx_handlers` function. First, I spot this crucial comparison: `rx->link_id != status->link_id`. That's interesting. I need to understand what's happening *before* this comparison. Specifically, what is the value of `rx->link_id` when the execution enters `ieee80211_rx_handlers`? That's going to be key to figuring out the root cause. My next move is to trace all the call sites of `ieee80211_rx_handlers`. Understanding how the function is invoked, and under what conditions, is essential to grasping the context of this `link_id` comparison and determining if there's a bug here.



34/2 2026/09/15 18:15 tool grepper
0m Args:
{
  "Expression": "ieee80211_rx_handlers",
  "PathPrefix": "net/mac80211/"
}

Results:
{
  "Output": "net/mac80211/rx.c=4135=ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)\n--\nnet/mac80211/rx.c-4190-\nnet/mac80211/rx.c:4191:static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx,\nnet/mac80211/rx.c-4192-\t\t\t\t\t ieee80211_rx_result res)\n--\nnet/mac80211/rx.c-4207-\nnet/mac80211/rx.c:4208:static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,\nnet/mac80211/rx.c-4209-\t\t\t\t  struct sk_buff_head *frames)\n--\nnet/mac80211/rx.c-4281- rxh_next:\nnet/mac80211/rx.c:4282:\t\tieee80211_rx_handlers_result(rx, res);\nnet/mac80211/rx.c-4283-\n--\nnet/mac80211/rx.c=4290=static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)\n--\nnet/mac80211/rx.c-4308-\nnet/mac80211/rx.c:4309:\tieee80211_rx_handlers(rx, \u0026reorder_release);\nnet/mac80211/rx.c-4310-\treturn;\n--\nnet/mac80211/rx.c-4312- rxh_next:\nnet/mac80211/rx.c:4313:\tieee80211_rx_handlers_result(rx, res);\nnet/mac80211/rx.c-4314-\n--\nnet/mac80211/rx.c=4403=void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)\n--\nnet/mac80211/rx.c-4439-\nnet/mac80211/rx.c:4440:\tieee80211_rx_handlers(\u0026rx, \u0026frames);\nnet/mac80211/rx.c-4441-}\n--\nnet/mac80211/rx.c=4443=void ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid,\n--\nnet/mac80211/rx.c-4519-\nnet/mac80211/rx.c:4520:\tieee80211_rx_handlers(\u0026rx, \u0026frames);\nnet/mac80211/rx.c-4521-\n"
}

35/2 2026/09/15 18:15 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 52810
							output: 41
							thoughts: 49
						

Thoughts:
Here's my summary, as if I were thinking it:

**Diving into `ieee80211_release_reorder_timeout`**

Okay, time to dissect `ieee80211_release_reorder_timeout`. I need to understand its behavior thoroughly. My immediate thought is to zero in on the core logic. Let's get right to the heart of the matter – lines 4403 through 4445 in `net/mac80211/rx.c`. That should provide a concentrated view of what's happening within this timeout handler. I'm expecting to see the critical code related to reordering and frame release. Time to see what we've got.



36/2 2026/09/15 18:15 tool read-file
0m Args:
{
  "File": "net/mac80211/rx.c",
  "FirstLine": 4403,
  "LineCount": 45
}

Results:
{
  "Contents": "4403:\tvoid ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)\n4404:\t{\n4405:\t\tstruct sk_buff_head frames;\n4406:\t\tstruct ieee80211_rx_data rx = {\n4407:\t\t\t/* This is OK -- must be QoS data frame */\n4408:\t\t\t.security_idx = tid,\n4409:\t\t\t.seqno_idx = tid,\n4410:\t\t};\n4411:\t\tstruct tid_ampdu_rx *tid_agg_rx;\n4412:\t\tint link_id = -1;\n4413:\t\n4414:\t\t/* NOTE: the correct link STA for the frame is set later */\n4415:\t\tif (sta-\u003esta.valid_links)\n4416:\t\t\tlink_id = ffs(sta-\u003esta.valid_links) - 1;\n4417:\t\n4418:\t\tif (!ieee80211_rx_data_set_sta(\u0026rx, sta, link_id))\n4419:\t\t\treturn;\n4420:\t\n4421:\t\ttid_agg_rx = rcu_dereference(sta-\u003eampdu_mlme.tid_rx[tid]);\n4422:\t\tif (!tid_agg_rx)\n4423:\t\t\treturn;\n4424:\t\n4425:\t\t__skb_queue_head_init(\u0026frames);\n4426:\t\n4427:\t\tspin_lock(\u0026tid_agg_rx-\u003ereorder_lock);\n4428:\t\tieee80211_sta_reorder_release(sta-\u003esdata, tid_agg_rx, \u0026frames);\n4429:\t\tspin_unlock(\u0026tid_agg_rx-\u003ereorder_lock);\n4430:\t\n4431:\t\tif (!skb_queue_empty(\u0026frames)) {\n4432:\t\t\tstruct ieee80211_event event = {\n4433:\t\t\t\t.type = BA_FRAME_TIMEOUT,\n4434:\t\t\t\t.u.ba.tid = tid,\n4435:\t\t\t\t.u.ba.sta = \u0026sta-\u003esta,\n4436:\t\t\t};\n4437:\t\t\tdrv_event_callback(rx.local, rx.sdata, \u0026event);\n4438:\t\t}\n4439:\t\n4440:\t\tieee80211_rx_handlers(\u0026rx, \u0026frames);\n4441:\t}\n4442:\t\n4443:\tvoid ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid,\n4444:\t\t\t\t\t\t  u16 ssn, u64 filtered,\n4445:\t\t\t\t\t\t  u16 received_mpdus)\n4446:\t{\n4447:\t\tstruct ieee80211_local *local;\n"
}

37/2 2026/09/15 18:15 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 53829
							output: 38
							thoughts: 1006
						

Thoughts:
**Potential Bug in IEEE 802.11 RX Handler: Index Out-of-Bounds or Incorrect Link Association**

Okay, let's break this down. I'm looking at the `ieee80211_rx_handlers` function, specifically the section dealing with Multiple Link Device (MLD) functionality. The code seems designed to update the link information (`rx->link_sta`, `rx->link`, `rx->link_id`) based on a received packet's `status->link_id`.

I see a possible issue here: if `rx->link_id` (the *old* link ID) is *not* equal to `status->link_id` (the *new* link ID), the code proceeds to update link-related variables. The problem is that it first uses the *old* `rx->link_id` to index `rx->sta->link` (`rx->link_sta = rcu_dereference(rx->sta->link[rx->link_id]);`), and *then* updates `rx->link_id` with `status->link_id`. So, `rx->link_sta` will be associated with the *wrong* link ID. This is a clear indexing error!

Furthermore, what if the *old* `rx->link_id` is -1? That's the default value or indicates an invalid link, I think. This potentially makes the situation even worse as it could result in accessing memory outside the array bounds. And since `rx->link_id` isn't initialized explicitly in the `rx` struct, it's reliant on the default zero value, which also adds complexity to this code.

I'm now thinking about the `ieee80211_rx_data_set_sta` function. This code path seems to address the default case and will provide a default link if `link_id < 0`. However, the important point is, if `link_id < 0`, and the `sta->deflink.link_id` is used, the situation still remains. `rx.link_id` is initialized to 0.

But wait, there's a bigger issue: the removal of the bounds check! In `ieee80211_rx_list`, there used to be a check (`WARN_ON_ONCE(status->link_id >= IEEE80211_LINK_UNSPECIFIED)`) to ensure that `status->link_id` was within a valid range. This check is *gone*. The current code now directly uses `status->link_id` to index into the `rx->sdata->link` array. This means that if `status->link_id` is out of bounds, we'll have a crash or memory corruption.

It's using `status->link_id` as an index. `status->link_id` is 4 bits, so it should be a value from 0 to 15. The `IEEE80211_MLD_MAX_NUM_LINKS` is 15. So, it's the maximum value. I'm assuming the index can go out of bounds.

Now, I *need* to know the size of the `rx->sta->link` array, the size of which is determined in the definition of the `struct sta_info` structure. That's the crucial question. If the array's size is smaller than the maximum possible `status->link_id`, we have a critical vulnerability, which can lead to information leak and crashing the program.



38/2 2026/09/15 18:15 tool grepper
0m Args:
{
  "Expression": "struct link_sta_info.*link\\[",
  "PathPrefix": "net/mac80211/"
}

Results:
{
  "Output": "net/mac80211/sta_info.h=715=struct sta_info {\n--\nnet/mac80211/sta_info.h-786-\tstruct link_sta_info deflink;\nnet/mac80211/sta_info.h:787:\tstruct link_sta_info __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];\nnet/mac80211/sta_info.h-788-\tstruct ieee80211_sta_removed_link_stats rem_link_stats;\n"
}

39/2 2026/09/15 18:15 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 55809
							output: 30
							thoughts: 124
						

Thoughts:
**My Immediate Concerns Regarding Potential Out-of-Bounds Memory Access**

Okay, so I'm looking at this code, and the value of `IEEE80211_MLD_MAX_NUM_LINKS` is defined as 15. Wait a minute... that's concerning. If I'm understanding this correctly, 4 bits are being used to represent the link ID. That should theoretically give us a range from 0 to 15—which is 16 possible values, right?

The issue jumps out at me: if `status->link_id` is ever equal to 15, then `rx->sta->link[15]` becomes an out-of-bounds array access, since the array `rx->sta->link` is only sized to hold 15 elements! It starts at 0, goes through 14, and there is no 15 element.

I need to confirm the definition of `IEEE80211_MLD_MAX_NUM_LINKS` because, this could be a nasty bug that needs immediate fixing to prevent a crash or security vulnerability.



40/2 2026/09/15 18:16 tool grepper
0m Args:
{
  "Expression": "IEEE80211_MLD_MAX_NUM_LINKS"
}

Results:
{
  "Output": "\nFull output is too long, showing 500 out of 1324 lines.\nUse more precise expression if possible.\n\ndrivers/net/wireless/ath/ath12k/cmn_defs.h-16-/* Define 1 scan link for each radio for parallel scan purposes */\ndrivers/net/wireless/ath/ath12k/cmn_defs.h:17:#define ATH12K_NUM_MAX_LINKS (IEEE80211_MLD_MAX_NUM_LINKS + ATH12K_SCAN_MAX_LINKS)\ndrivers/net/wireless/ath/ath12k/cmn_defs.h-18-\n--\ndrivers/net/wireless/ath/ath12k/core.h=354=struct ath12k_vif {\n--\ndrivers/net/wireless/ath/ath12k/core.h-383-\tstruct ath12k_link_vif __rcu *link[ATH12K_NUM_MAX_LINKS];\ndrivers/net/wireless/ath/ath12k/core.h:384:\tstruct ath12k_vif_cache *cache[IEEE80211_MLD_MAX_NUM_LINKS];\ndrivers/net/wireless/ath/ath12k/core.h-385-\t/* indicates bitmap of link vif created in FW */\n--\ndrivers/net/wireless/ath/ath12k/core.h=518=struct ath12k_sta {\n--\ndrivers/net/wireless/ath/ath12k/core.h-521-\tstruct ath12k_link_sta deflink;\ndrivers/net/wireless/ath/ath12k/core.h:522:\tstruct ath12k_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];\ndrivers/net/wireless/ath/ath12k/core.h-523-\t/* indicates bitmap of link sta created in FW */\n--\ndrivers/net/wireless/ath/ath12k/debugfs.c=906=static int ath12k_open_link_stats(struct inode *inode, struct file *file)\n--\ndrivers/net/wireless/ath/ath12k/debugfs.c-928-\tfor_each_set_bit(link_id, \u0026links_map,\ndrivers/net/wireless/ath/ath12k/debugfs.c:929:\t\t\t IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/debugfs.c-930-\t\tarvif = rcu_dereference_protected(ahvif-\u003elink[link_id],\n--\ndrivers/net/wireless/ath/ath12k/mac.c=681=ath12k_mac_get_link_bss_conf(struct ath12k_link_vif *arvif)\n--\ndrivers/net/wireless/ath/ath12k/mac.c-688-\ndrivers/net/wireless/ath/ath12k/mac.c:689:\tif (arvif-\u003elink_id \u003e= IEEE80211_MLD_MAX_NUM_LINKS)\ndrivers/net/wireless/ath/ath12k/mac.c-690-\t\treturn NULL;\n--\ndrivers/net/wireless/ath/ath12k/mac.c=698=static struct ieee80211_link_sta *ath12k_mac_get_link_sta(struct ath12k_link_sta *arsta)\n--\ndrivers/net/wireless/ath/ath12k/mac.c-705-\ndrivers/net/wireless/ath/ath12k/mac.c:706:\tif (arsta-\u003elink_id \u003e= IEEE80211_MLD_MAX_NUM_LINKS)\ndrivers/net/wireless/ath/ath12k/mac.c-707-\t\treturn NULL;\n--\ndrivers/net/wireless/ath/ath12k/mac.c=762=static void ath12k_get_arvif_iter(void *data, u8 *mac,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-770-\ndrivers/net/wireless/ath/ath12k/mac.c:771:\tfor_each_set_bit(link_id, \u0026links_map, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-772-\t\tarvif = rcu_dereference(ahvif-\u003elink[link_id]);\n--\ndrivers/net/wireless/ath/ath12k/mac.c=1188=static void ath12k_mac_link_sta_rhash_cleanup(void *data, struct ieee80211_sta *sta)\n--\ndrivers/net/wireless/ath/ath12k/mac.c-1201-\trcu_read_lock();\ndrivers/net/wireless/ath/ath12k/mac.c:1202:\tfor_each_set_bit(link_id, \u0026links_map, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-1203-\t\tarsta = rcu_dereference(ahsta-\u003elink[link_id]);\n--\ndrivers/net/wireless/ath/ath12k/mac.c=1217=void ath12k_mac_peer_cleanup_all(struct ath12k *ar)\n--\ndrivers/net/wireless/ath/ath12k/mac.c-1271-\t\tif ((arvif-\u003eahvif-\u003evdev_type == WMI_VDEV_TYPE_AP) \u0026\u0026\ndrivers/net/wireless/ath/ath12k/mac.c:1272:\t\t    (arvif-\u003elink_id \u003c IEEE80211_MLD_MAX_NUM_LINKS)) {\ndrivers/net/wireless/ath/ath12k/mac.c-1273-\t\t\tath12k_dp_peer_delete(dp_hw, arvif-\u003ebssid, NULL);\n--\ndrivers/net/wireless/ath/ath12k/mac.c=3530=static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-3582-\ndrivers/net/wireless/ath/ath12k/mac.c:3583:\tfor_each_set_bit(link_id, \u0026links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-3584-\t\tif (i \u003e= ATH12K_WMI_MLO_MAX_LINKS)\n--\ndrivers/net/wireless/ath/ath12k/mac.c=4253=static void ath12k_mac_remove_link_interface(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-4279-\ndrivers/net/wireless/ath/ath12k/mac.c:4280:\t\tif (arvif-\u003elink_id \u003c IEEE80211_MLD_MAX_NUM_LINKS)\ndrivers/net/wireless/ath/ath12k/mac.c-4281-\t\t\tath12k_dp_peer_delete(\u0026ah-\u003edp_hw, arvif-\u003ebssid, NULL);\n--\ndrivers/net/wireless/ath/ath12k/mac.c=4336=ath12k_mac_op_change_vif_links(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-4338-\t\t\t       u16 old_links, u16 new_links,\ndrivers/net/wireless/ath/ath12k/mac.c:4339:\t\t\t       struct ieee80211_bss_conf *ol[IEEE80211_MLD_MAX_NUM_LINKS])\ndrivers/net/wireless/ath/ath12k/mac.c-4340-{\n--\ndrivers/net/wireless/ath/ath12k/mac.c-4353-\ndrivers/net/wireless/ath/ath12k/mac.c:4354:\tfor_each_set_bit(link_id, \u0026to_add, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-4355-\t\tarvif = wiphy_dereference(hw-\u003ewiphy, ahvif-\u003elink[link_id]);\n--\ndrivers/net/wireless/ath/ath12k/mac.c-4366-\ndrivers/net/wireless/ath/ath12k/mac.c:4367:\tfor_each_set_bit(link_id, \u0026to_remove, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-4368-\t\tarvif = wiphy_dereference(hw-\u003ewiphy, ahvif-\u003elink[link_id]);\n--\ndrivers/net/wireless/ath/ath12k/mac.c=4437=void ath12k_mac_op_vif_cfg_changed(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-4487-\ndrivers/net/wireless/ath/ath12k/mac.c:4488:\t\tfor_each_set_bit(link_id, \u0026links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-4489-\t\t\tarvif = wiphy_dereference(hw-\u003ewiphy, ahvif-\u003elink[link_id]);\n--\ndrivers/net/wireless/ath/ath12k/mac.c=5137=static void ath12k_ahvif_put_link_cache(struct ath12k_vif *ahvif, u8 link_id)\ndrivers/net/wireless/ath/ath12k/mac.c-5138-{\ndrivers/net/wireless/ath/ath12k/mac.c:5139:\tif (link_id \u003e= IEEE80211_MLD_MAX_NUM_LINKS)\ndrivers/net/wireless/ath/ath12k/mac.c-5140-\t\treturn;\n--\ndrivers/net/wireless/ath/ath12k/mac.c=6221=int ath12k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-6250-\t\t\tlinks = ahsta-\u003elinks_map;\ndrivers/net/wireless/ath/ath12k/mac.c:6251:\t\t\tfor_each_set_bit(link_id, \u0026links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-6252-\t\t\t\tarvif = wiphy_dereference(hw-\u003ewiphy,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-6283-\ndrivers/net/wireless/ath/ath12k/mac.c:6284:\tif (key-\u003elink_id \u003e= 0 \u0026\u0026 key-\u003elink_id \u003c IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-6285-\t\tlink_id = key-\u003elink_id;\n--\ndrivers/net/wireless/ath/ath12k/mac.c=6639=static int ath12k_mac_sta_set_4addr(struct wiphy *wiphy, struct ath12k_sta *ahsta)\n--\ndrivers/net/wireless/ath/ath12k/mac.c-6651-\tlinks = ahsta-\u003elinks_map;\ndrivers/net/wireless/ath/ath12k/mac.c:6652:\tfor_each_set_bit(link_id, \u0026links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-6653-\t\tarsta = wiphy_dereference(wiphy, ahsta-\u003elink[link_id]);\n--\ndrivers/net/wireless/ath/ath12k/mac.c=6911=static void ath12k_mac_free_unassign_link_sta(struct ath12k_hw *ah,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-6918-\ndrivers/net/wireless/ath/ath12k/mac.c:6919:\tif (WARN_ON(link_id \u003e= IEEE80211_MLD_MAX_NUM_LINKS))\ndrivers/net/wireless/ath/ath12k/mac.c-6920-\t\treturn;\n--\ndrivers/net/wireless/ath/ath12k/mac.c=7238=static int ath12k_mac_assign_link_sta(struct ath12k_hw *ah,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-7250-\ndrivers/net/wireless/ath/ath12k/mac.c:7251:\tif (!arsta || link_id \u003e= IEEE80211_MLD_MAX_NUM_LINKS)\ndrivers/net/wireless/ath/ath12k/mac.c-7252-\t\treturn -EINVAL;\n--\ndrivers/net/wireless/ath/ath12k/mac.c=7290=static void ath12k_mac_ml_station_remove(struct ath12k_vif *ahvif,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-7306-\tlinks = ahsta-\u003elinks_map;\ndrivers/net/wireless/ath/ath12k/mac.c:7307:\tfor_each_set_bit(link_id, \u0026links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-7308-\t\tarvif = wiphy_dereference(ah-\u003ehw-\u003ewiphy, ahvif-\u003elink[link_id]);\n--\ndrivers/net/wireless/ath/ath12k/mac.c=7578=static int ath12k_mac_mlo_sta_update_link_active(struct ath12k_base *ab,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-7581-{\ndrivers/net/wireless/ath/ath12k/mac.c:7582:\tu8 mlo_vdev_id_lst[IEEE80211_MLD_MAX_NUM_LINKS] = {};\ndrivers/net/wireless/ath/ath12k/mac.c:7583:\tu32 mlo_freq_list[IEEE80211_MLD_MAX_NUM_LINKS] = {};\ndrivers/net/wireless/ath/ath12k/mac.c-7584-\tunsigned long links = ahvif-\u003elinks_map;\n--\ndrivers/net/wireless/ath/ath12k/mac.c-7592-\ndrivers/net/wireless/ath/ath12k/mac.c:7593:\tfor_each_set_bit(link_id, \u0026links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-7594-\t\tarvif = wiphy_dereference(hw-\u003ewiphy, ahvif-\u003elink[link_id]);\n--\ndrivers/net/wireless/ath/ath12k/mac.c=7652=static int ath12k_mac_select_links(struct ath12k_base *ab,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-7699-\tuseful_links \u0026= ~BIT(assoc_arvif-\u003elink_id);\ndrivers/net/wireless/ath/ath12k/mac.c:7700:\tfor_each_set_bit(link_id, \u0026useful_links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-7701-\t\tinfo = wiphy_dereference(hw-\u003ewiphy, vif-\u003elink_conf[link_id]);\n--\ndrivers/net/wireless/ath/ath12k/mac.c=7748=int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-7879-\tvalid_links = ahsta-\u003elinks_map;\ndrivers/net/wireless/ath/ath12k/mac.c:7880:\tfor_each_set_bit(link_id, \u0026valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-7881-\t\tarvif = wiphy_dereference(hw-\u003ewiphy, ahvif-\u003elink[link_id]);\n--\ndrivers/net/wireless/ath/ath12k/mac.c=8026=void ath12k_mac_op_link_sta_rc_update(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-8074-\ndrivers/net/wireless/ath/ath12k/mac.c:8075:\tif (arsta-\u003elink_id \u003e= IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-8076-\t\trcu_read_unlock();\n--\ndrivers/net/wireless/ath/ath12k/mac.c=8138=static struct ath12k_link_sta *ath12k_mac_alloc_assign_link_sta(struct ath12k_hw *ah,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-8147-\ndrivers/net/wireless/ath/ath12k/mac.c:8148:\tif (link_id \u003e= IEEE80211_MLD_MAX_NUM_LINKS)\ndrivers/net/wireless/ath/ath12k/mac.c-8149-\t\treturn NULL;\n--\ndrivers/net/wireless/ath/ath12k/mac.c=8168=int ath12k_mac_op_change_sta_links(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-8204-\tvalid_links = new_links;\ndrivers/net/wireless/ath/ath12k/mac.c:8205:\tfor_each_set_bit(link_id, \u0026valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-8206-\t\tif (ahsta-\u003elinks_map \u0026 BIT(link_id))\n--\ndrivers/net/wireless/ath/ath12k/mac.c=8354=int ath12k_mac_op_conf_tx(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-8365-\ndrivers/net/wireless/ath/ath12k/mac.c:8366:\tif (link_id \u003e= IEEE80211_MLD_MAX_NUM_LINKS)\ndrivers/net/wireless/ath/ath12k/mac.c-8367-\t\treturn -EINVAL;\n--\ndrivers/net/wireless/ath/ath12k/mac.c=10236=void ath12k_mac_op_update_vif_offload(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-10247-\t\tlinks = vif-\u003evalid_links;\ndrivers/net/wireless/ath/ath12k/mac.c:10248:\t\tfor_each_set_bit(link_id, \u0026links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-10249-\t\t\tarvif = wiphy_dereference(hw-\u003ewiphy, ahvif-\u003elink[link_id]);\n--\ndrivers/net/wireless/ath/ath12k/mac.c=10412=int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif)\n--\ndrivers/net/wireless/ath/ath12k/mac.c-10447-\ndrivers/net/wireless/ath/ath12k/mac.c:10448:\tif (link_id \u003c IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-10449-\t\tlink_conf = wiphy_dereference(hw-\u003ewiphy, vif-\u003elink_conf[link_id]);\n--\ndrivers/net/wireless/ath/ath12k/mac.c-10521-\ndrivers/net/wireless/ath/ath12k/mac.c:10522:\t\tif (arvif-\u003elink_id \u003c IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-10523-\t\t\tret = ath12k_dp_peer_create(\u0026ah-\u003edp_hw, arvif-\u003ebssid, \u0026params);\n--\ndrivers/net/wireless/ath/ath12k/mac.c=11147=int ath12k_mac_op_ampdu_action(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-11161-\ndrivers/net/wireless/ath/ath12k/mac.c:11162:\tfor_each_set_bit(link_id, \u0026links_map, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-11163-\t\tret = ath12k_mac_ampdu_action(hw, vif, params, link_id);\n--\ndrivers/net/wireless/ath/ath12k/mac.c=11294=ath12k_mac_mlo_get_vdev_args(struct ath12k_link_vif *arvif,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-11326-\tlinks = ahvif-\u003elinks_map;\ndrivers/net/wireless/ath/ath12k/mac.c:11327:\tfor_each_set_bit(link_id, \u0026links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-11328-\t\tarvif_p = wiphy_dereference(ahvif-\u003eah-\u003ehw-\u003ewiphy, ahvif-\u003elink[link_id]);\n--\ndrivers/net/wireless/ath/ath12k/mac.c=11513=ath12k_mac_change_chanctx_cnt_iter(void *data, u8 *mac,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-11525-\tlinks_map = ahvif-\u003elinks_map;\ndrivers/net/wireless/ath/ath12k/mac.c:11526:\tfor_each_set_bit(link_id, \u0026links_map, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-11527-\t\tarvif = wiphy_dereference(ahvif-\u003eah-\u003ehw-\u003ewiphy, ahvif-\u003elink[link_id]);\n--\ndrivers/net/wireless/ath/ath12k/mac.c=11547=ath12k_mac_change_chanctx_fill_iter(void *data, u8 *mac,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-11560-\tlinks_map = ahvif-\u003elinks_map;\ndrivers/net/wireless/ath/ath12k/mac.c:11561:\tfor_each_set_bit(link_id, \u0026links_map, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-11562-\t\tarvif = wiphy_dereference(ahvif-\u003eah-\u003ehw-\u003ewiphy, ahvif-\u003elink[link_id]);\n--\ndrivers/net/wireless/ath/ath12k/mac.c=12695=void ath12k_mac_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,\n--\ndrivers/net/wireless/ath/ath12k/mac.c-12722-\tlinks = ahvif-\u003elinks_map;\ndrivers/net/wireless/ath/ath12k/mac.c:12723:\tfor_each_set_bit(link_id, \u0026links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/mac.c-12724-\t\tarvif = wiphy_dereference(hw-\u003ewiphy, ahvif-\u003elink[link_id]);\n--\ndrivers/net/wireless/ath/ath12k/mac.h=20=struct ath12k_generic_iter {\n--\ndrivers/net/wireless/ath/ath12k/mac.h-55- */\ndrivers/net/wireless/ath/ath12k/mac.h:56:#define ATH12K_FIRST_SCAN_LINK\tIEEE80211_MLD_MAX_NUM_LINKS\ndrivers/net/wireless/ath/ath12k/mac.h:57:#define ATH12K_SCAN_LINKS_MASK\tGENMASK(ATH12K_NUM_MAX_LINKS, IEEE80211_MLD_MAX_NUM_LINKS)\ndrivers/net/wireless/ath/ath12k/mac.h-58-\n--\ndrivers/net/wireless/ath/ath12k/mac.h=230=ath12k_mac_op_change_vif_links\n--\ndrivers/net/wireless/ath/ath12k/mac.h-233-\t\t\t u16 old_links, u16 new_links,\ndrivers/net/wireless/ath/ath12k/mac.h:234:\t\t\t struct ieee80211_bss_conf *ol[IEEE80211_MLD_MAX_NUM_LINKS]);\ndrivers/net/wireless/ath/ath12k/mac.h-235-void ath12k_mac_op_configure_filter(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/ath/ath12k/peer.c=190=int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif,\n--\ndrivers/net/wireless/ath/ath12k/peer.c-317-\ndrivers/net/wireless/ath/ath12k/peer.c:318:\tif (arvif-\u003elink_id \u003c IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/peer.c-319-\t\tret = ath12k_dp_link_peer_assign(ath12k_ab_to_dp(ar-\u003eab),\n--\ndrivers/net/wireless/ath/ath12k/peer.c=368=int ath12k_peer_mlo_link_peers_delete(struct ath12k_vif *ahvif, struct ath12k_sta *ahsta)\ndrivers/net/wireless/ath/ath12k/peer.c-369-{\ndrivers/net/wireless/ath/ath12k/peer.c:370:\tDECLARE_BITMAP(registered, IEEE80211_MLD_MAX_NUM_LINKS);\ndrivers/net/wireless/ath/ath12k/peer.c-371-\tstruct ieee80211_sta *sta = ath12k_ahsta_to_sta(ahsta);\n--\ndrivers/net/wireless/ath/ath12k/peer.c-385-\tstruct ath12k_peer_delete_wait *waits __free(kfree) =\ndrivers/net/wireless/ath/ath12k/peer.c:386:\t\t\t\tkzalloc_objs(*waits, IEEE80211_MLD_MAX_NUM_LINKS);\ndrivers/net/wireless/ath/ath12k/peer.c-387-\tif (!waits)\n--\ndrivers/net/wireless/ath/ath12k/peer.c-389-\ndrivers/net/wireless/ath/ath12k/peer.c:390:\tbitmap_zero(registered, IEEE80211_MLD_MAX_NUM_LINKS);\ndrivers/net/wireless/ath/ath12k/peer.c-391-\n--\ndrivers/net/wireless/ath/ath12k/peer.c-399-\tlinks = ahsta-\u003elinks_map;\ndrivers/net/wireless/ath/ath12k/peer.c:400:\tfor_each_set_bit(link_id, \u0026links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/peer.c-401-\t\tarvif = wiphy_dereference(ah-\u003ehw-\u003ewiphy, ahvif-\u003elink[link_id]);\n--\ndrivers/net/wireless/ath/ath12k/peer.c-437-\tlinks = ahsta-\u003elinks_map;\ndrivers/net/wireless/ath/ath12k/peer.c:438:\tfor_each_set_bit(link_id, \u0026links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/peer.c-439-\t\tif (!test_bit(link_id, registered))\n--\ndrivers/net/wireless/ath/ath12k/wifi7/hw.c=901=static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/ath/ath12k/wifi7/hw.c-948-\t\tlink_id = ath12k_mac_get_tx_link(sta, vif, link_id, skb, info_flags);\ndrivers/net/wireless/ath/ath12k/wifi7/hw.c:949:\t\tif (link_id \u003e= IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/wifi7/hw.c-950-\t\t\tieee80211_free_txskb(hw, skb);\n--\ndrivers/net/wireless/ath/ath12k/wifi7/hw.c-1040-\t\tfor_each_set_bit(link_id, \u0026links_map,\ndrivers/net/wireless/ath/ath12k/wifi7/hw.c:1041:\t\t\t\t IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/wifi7/hw.c-1042-\t\t\ttmp_arvif = rcu_dereference(ahvif-\u003elink[link_id]);\n--\ndrivers/net/wireless/ath/ath12k/wmi.c=9078=ath12k_wmi_process_csa_switch_count_event(struct ath12k_base *ab,\n--\ndrivers/net/wireless/ath/ath12k/wmi.c-9106-\ndrivers/net/wireless/ath/ath12k/wmi.c:9107:\t\tif (arvif-\u003elink_id \u003e= IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/ath/ath12k/wmi.c-9108-\t\t\tath12k_warn(ab, \"Invalid CSA switch count even link id: %d\\n\",\n--\ndrivers/net/wireless/intel/iwlwifi/mld/d3.c=1193=static void iwl_mld_mlo_rekey(struct iwl_mld *mld,\n--\ndrivers/net/wireless/intel/iwlwifi/mld/d3.c-1207-\ndrivers/net/wireless/intel/iwlwifi/mld/d3.c:1208:\t\tif (IWL_FW_CHECK(mld, mlo_key-\u003elink_id \u003e= IEEE80211_MLD_MAX_NUM_LINKS ||\ndrivers/net/wireless/intel/iwlwifi/mld/d3.c-1209-\t\t\t\t      mlo_key-\u003eidx \u003e= 8 ||\n--\ndrivers/net/wireless/intel/iwlwifi/mld/iface.h=166=struct iwl_mld_vif {\n--\ndrivers/net/wireless/intel/iwlwifi/mld/iface.h-184-\tstruct iwl_mld_link deflink;\ndrivers/net/wireless/intel/iwlwifi/mld/iface.h:185:\tstruct iwl_mld_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];\ndrivers/net/wireless/intel/iwlwifi/mld/iface.h-186-\n--\ndrivers/net/wireless/intel/iwlwifi/mld/mac80211.c=2637=static bool iwl_mld_can_activate_links(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/intel/iwlwifi/mld/mac80211.c-2669-\t */\ndrivers/net/wireless/intel/iwlwifi/mld/mac80211.c:2670:\tfor_each_set_bit(i, \u0026to_deactivate, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/intel/iwlwifi/mld/mac80211.c-2671-\t\tstruct ieee80211_bss_conf *link_conf;\n--\ndrivers/net/wireless/intel/iwlwifi/mld/mac80211.c=2706=iwl_mld_change_vif_links(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/intel/iwlwifi/mld/mac80211.c-2708-\t\t\t u16 old_links, u16 new_links,\ndrivers/net/wireless/intel/iwlwifi/mld/mac80211.c:2709:\t\t\t struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS])\ndrivers/net/wireless/intel/iwlwifi/mld/mac80211.c-2710-{\n--\ndrivers/net/wireless/intel/iwlwifi/mld/mac80211.c-2732-\ndrivers/net/wireless/intel/iwlwifi/mld/mac80211.c:2733:\tfor (int i = 0; i \u003c IEEE80211_MLD_MAX_NUM_LINKS; i++) {\ndrivers/net/wireless/intel/iwlwifi/mld/mac80211.c-2734-\t\tif (removed \u0026 BIT(i) \u0026\u0026 !WARN_ON(!old[i]))\n--\ndrivers/net/wireless/intel/iwlwifi/mld/mac80211.c-2737-\ndrivers/net/wireless/intel/iwlwifi/mld/mac80211.c:2738:\tfor (int i = 0; i \u003c IEEE80211_MLD_MAX_NUM_LINKS; i++) {\ndrivers/net/wireless/intel/iwlwifi/mld/mac80211.c-2739-\t\tif (added \u0026 BIT(i)) {\n--\ndrivers/net/wireless/intel/iwlwifi/mld/mac80211.c-2767-remove_added_links:\ndrivers/net/wireless/intel/iwlwifi/mld/mac80211.c:2768:\tfor (int i = 0; i \u003c IEEE80211_MLD_MAX_NUM_LINKS; i++) {\ndrivers/net/wireless/intel/iwlwifi/mld/mac80211.c-2769-\t\tif (!(added \u0026 BIT(i)))\n--\ndrivers/net/wireless/intel/iwlwifi/mld/mlo.c=723=iwl_mld_set_link_sel_data(struct iwl_mld *mld,\n--\ndrivers/net/wireless/intel/iwlwifi/mld/mlo.c-732-\ndrivers/net/wireless/intel/iwlwifi/mld/mlo.c:733:\tfor_each_set_bit(link_id, \u0026usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/intel/iwlwifi/mld/mlo.c-734-\t\tstruct ieee80211_bss_conf *link_conf =\n--\ndrivers/net/wireless/intel/iwlwifi/mld/mlo.c=932=static void _iwl_mld_select_links(struct iwl_mld *mld,\n--\ndrivers/net/wireless/intel/iwlwifi/mld/mlo.c-934-{\ndrivers/net/wireless/intel/iwlwifi/mld/mlo.c:935:\tstruct iwl_mld_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS];\ndrivers/net/wireless/intel/iwlwifi/mld/mlo.c-936-\tstruct iwl_mld_link_sel_data *best_link;\n--\ndrivers/net/wireless/intel/iwlwifi/mld/scan.c=171=static void iwl_mld_scan_iterator(void *_data, u8 *mac,\n--\ndrivers/net/wireless/intel/iwlwifi/mld/scan.c-202-\tfor_each_set_bit(link_id, \u0026curr_vif_active_links,\ndrivers/net/wireless/intel/iwlwifi/mld/scan.c:203:\t\t\t IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/intel/iwlwifi/mld/scan.c-204-\t\tstruct iwl_mld_link *curr_mld_link =\n--\ndrivers/net/wireless/intel/iwlwifi/mld/scan.c=1948=void iwl_mld_int_mlo_scan(struct iwl_mld *mld, struct ieee80211_vif *vif)\ndrivers/net/wireless/intel/iwlwifi/mld/scan.c-1949-{\ndrivers/net/wireless/intel/iwlwifi/mld/scan.c:1950:\tstruct ieee80211_channel *channels[IEEE80211_MLD_MAX_NUM_LINKS];\ndrivers/net/wireless/intel/iwlwifi/mld/scan.c-1951-\tstruct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);\n--\ndrivers/net/wireless/intel/iwlwifi/mld/scan.c-1975-\ndrivers/net/wireless/intel/iwlwifi/mld/scan.c:1976:\tfor_each_set_bit(link_id, \u0026usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/intel/iwlwifi/mld/scan.c-1977-\t\tstruct ieee80211_bss_conf *link_conf =\n--\ndrivers/net/wireless/intel/iwlwifi/mld/sta.c=1330=int iwl_mld_update_link_stas(struct iwl_mld *mld,\n--\ndrivers/net/wireless/intel/iwlwifi/mld/sta.c-1347-\tfor_each_set_bit(link_id, \u0026old_links_long,\ndrivers/net/wireless/intel/iwlwifi/mld/sta.c:1348:\t\t\t IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/intel/iwlwifi/mld/sta.c-1349-\t\tmld_link_sta =\n--\ndrivers/net/wireless/intel/iwlwifi/mld/sta.c-1370-\ndrivers/net/wireless/intel/iwlwifi/mld/sta.c:1371:\tfor_each_set_bit(link_id, \u0026links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/intel/iwlwifi/mld/sta.c-1372-\t\tstruct ieee80211_link_sta *link_sta =\n--\ndrivers/net/wireless/intel/iwlwifi/mld/sta.c-1380-\ndrivers/net/wireless/intel/iwlwifi/mld/sta.c:1381:\tfor_each_set_bit(link_id, \u0026links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/intel/iwlwifi/mld/sta.c-1382-\t\tstruct ieee80211_link_sta *link_sta =\n--\ndrivers/net/wireless/intel/iwlwifi/mld/sta.c-1415-\t/* We couldn't activate the links before it has a STA. Now we can */\ndrivers/net/wireless/intel/iwlwifi/mld/sta.c:1416:\tfor_each_set_bit(link_id, \u0026links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/intel/iwlwifi/mld/sta.c-1417-\t\tstruct ieee80211_bss_conf *link =\n--\ndrivers/net/wireless/intel/iwlwifi/mld/sta.h=129=struct iwl_mld_sta {\n--\ndrivers/net/wireless/intel/iwlwifi/mld/sta.h-142-\tstruct iwl_mld_link_sta deflink;\ndrivers/net/wireless/intel/iwlwifi/mld/sta.h:143:\tstruct iwl_mld_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];\ndrivers/net/wireless/intel/iwlwifi/mld/sta.h-144-\tstruct iwl_mld_ptk_pn __rcu *ptk_pn[IWL_NUM_DEFAULT_KEYS];\n--\ndrivers/net/wireless/intel/iwlwifi/mvm/coex.c=414=static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,\n--\ndrivers/net/wireless/intel/iwlwifi/mvm/coex.c-434-\ndrivers/net/wireless/intel/iwlwifi/mvm/coex.c:435:\tfor (link_id = 0; link_id \u003c IEEE80211_MLD_MAX_NUM_LINKS; link_id++)\ndrivers/net/wireless/intel/iwlwifi/mvm/coex.c-436-\t\tiwl_mvm_bt_notif_per_link(mvm, vif, data, link_id);\n--\ndrivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c=655=int iwl_mvm_mld_add_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,\n--\ndrivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c-715-\tfor_each_set_bit(link_id, \u0026link_sta_added_to_fw,\ndrivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c:716:\t\t\t IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c-717-\t\tstruct iwl_mvm_link_sta *mvm_link_sta =\n--\ndrivers/net/wireless/intel/iwlwifi/mvm/mvm.h=421=struct iwl_mvm_vif {\n--\ndrivers/net/wireless/intel/iwlwifi/mvm/mvm.h-513-\tstruct iwl_mvm_vif_link_info deflink;\ndrivers/net/wireless/intel/iwlwifi/mvm/mvm.h:514:\tstruct iwl_mvm_vif_link_info *link[IEEE80211_MLD_MAX_NUM_LINKS];\ndrivers/net/wireless/intel/iwlwifi/mvm/mvm.h-515-};\n--\ndrivers/net/wireless/intel/iwlwifi/mvm/sta.h=400=struct iwl_mvm_sta {\n--\ndrivers/net/wireless/intel/iwlwifi/mvm/sta.h-431-\tstruct iwl_mvm_link_sta deflink;\ndrivers/net/wireless/intel/iwlwifi/mvm/sta.h:432:\tstruct iwl_mvm_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];\ndrivers/net/wireless/intel/iwlwifi/mvm/sta.h-433-};\n--\ndrivers/net/wireless/mediatek/mt76/mac80211.c=2118=u16 mt76_select_links(struct ieee80211_vif *vif, int max_active_links)\n--\ndrivers/net/wireless/mediatek/mt76/mac80211.c-2123-\t\tenum nl80211_band band;\ndrivers/net/wireless/mediatek/mt76/mac80211.c:2124:\t} data[IEEE80211_MLD_MAX_NUM_LINKS];\ndrivers/net/wireless/mediatek/mt76/mac80211.c-2125-\tunsigned int link_id;\n--\ndrivers/net/wireless/mediatek/mt76/mac80211.c-2135-\trcu_read_lock();\ndrivers/net/wireless/mediatek/mt76/mac80211.c:2136:\tfor_each_set_bit(link_id, \u0026usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/mediatek/mt76/mac80211.c-2137-\t\tstruct ieee80211_bss_conf *link_conf;\n--\ndrivers/net/wireless/mediatek/mt76/mac80211.c=2218=mt76_offchannel_notify_iter(void *_data, u8 *mac, struct ieee80211_vif *vif)\n--\ndrivers/net/wireless/mediatek/mt76/mac80211.c-2239-\ndrivers/net/wireless/mediatek/mt76/mac80211.c:2240:\tfor (link_id = 0; link_id \u003c IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {\ndrivers/net/wireless/mediatek/mt76/mac80211.c-2241-\t\tif (link_id == mvif-\u003edeflink_id)\n--\ndrivers/net/wireless/mediatek/mt76/mac80211.c=2280=static void mt76_rx_beacon_iter(void *_data, u8 *mac,\n--\ndrivers/net/wireless/mediatek/mt76/mac80211.c-2290-\ndrivers/net/wireless/mediatek/mt76/mac80211.c:2291:\tfor (link_id = 0; link_id \u003c IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {\ndrivers/net/wireless/mediatek/mt76/mac80211.c-2292-\t\tstruct ieee80211_bss_conf *link_conf;\n--\ndrivers/net/wireless/mediatek/mt76/mac80211.c=2341=static void mt76_beacon_mon_iter(void *data, u8 *mac,\n--\ndrivers/net/wireless/mediatek/mt76/mac80211.c-2351-\ndrivers/net/wireless/mediatek/mt76/mac80211.c:2352:\tfor (link_id = 0; link_id \u003c IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {\ndrivers/net/wireless/mediatek/mt76/mac80211.c-2353-\t\tif (link_id == mvif-\u003edeflink_id)\n--\ndrivers/net/wireless/mediatek/mt76/mt76.h=848=struct mt76_vif_data {\ndrivers/net/wireless/mediatek/mt76/mt76.h:849:\tstruct mt76_vif_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];\ndrivers/net/wireless/mediatek/mt76/mt76.h-850-\tstruct mt76_vif_link __rcu *offchannel_link;\n--\ndrivers/net/wireless/mediatek/mt76/mt76.h=2127=mt76_vif_link(struct mt76_dev *dev, struct ieee80211_vif *vif, int link_id)\n--\ndrivers/net/wireless/mediatek/mt76/mt76.h-2134-\ndrivers/net/wireless/mediatek/mt76/mt76.h:2135:\tif (link_id \u003e= IEEE80211_MLD_MAX_NUM_LINKS)\ndrivers/net/wireless/mediatek/mt76/mt76.h-2136-\t\treturn NULL;\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mac.c=1473=mt7925_vif_connect_iter(void *priv, u8 *mac,\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/mac.c-1487-\ndrivers/net/wireless/mediatek/mt76/mt7925/mac.c:1488:\tfor_each_set_bit(i, \u0026valid, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/mediatek/mt76/mt7925/mac.c-1489-\t\tbss_conf = mt792x_vif_to_bss_conf(vif, i);\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c=729=static int mt7925_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-757-\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c:758:\t\tfor_each_set_bit(link_id, \u0026add, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-759-\t\t\tmlink = mt792x_sta_to_link(msta, link_id);\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c=1060=mt7925_mac_sta_unwind_links_host(struct mt792x_dev *dev,\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-1066-\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c:1067:\tfor_each_set_bit(link_id, \u0026links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-1068-\t\tstruct mt792x_link_sta *mlink;\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c=1091=mt7925_mac_sta_add_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-1098-\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c:1099:\tfor_each_set_bit(link_id, \u0026new_links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-1100-\t\tstruct ieee80211_link_sta *link_sta;\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c=1327=mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-1334-\t/* clean up bss before starec */\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c:1335:\tfor_each_set_bit(link_id, \u0026old_links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-1336-\t\tstruct ieee80211_link_sta *link_sta;\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-1364-\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c:1365:\tfor_each_set_bit(link_id, \u0026old_links, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-1366-\t\tstruct ieee80211_link_sta *link_sta;\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c=1518=mt7925_mlo_pm_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-1530-\tmt792x_mutex_acquire(dev);\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c:1531:\tfor_each_set_bit(i, \u0026valid, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-1532-\t\tbss_conf = mt792x_vif_to_bss_conf(vif, i);\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c=1763=static void mt7925_sta_set_decap_offload(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-1780-\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c:1781:\tfor_each_set_bit(i, \u0026valid, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-1782-\t\tstruct mt792x_bss_conf *mconf;\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c=1861=static void mt7925_ipv6_addr_change(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-1870-\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c:1871:\tfor_each_set_bit(i, \u0026valid, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-1872-\t\tbss_conf = mt792x_vif_to_bss_conf(vif, i);\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c=2018=mt7925_change_chanctx(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-2043-\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c:2044:\t\t\tfor_each_set_bit(i, \u0026valid, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-2045-\t\t\t\tmconf = mt792x_vif_to_link(mvif, i);\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c=2095=static void mt7925_vif_cfg_changed(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-2118-\tif (changed \u0026 BSS_CHANGED_ARP_FILTER) {\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c:2119:\t\tfor_each_set_bit(i, \u0026valid, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-2120-\t\t\tbss_conf = mt792x_vif_to_bss_conf(vif, i);\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-2133-\t\t\t} else if (mvif-\u003emlo_pm_state == MT792x_MLO_CHANGED_PS) {\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c:2134:\t\t\t\tfor_each_set_bit(i, \u0026valid, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-2135-\t\t\t\t\tbss_conf = mt792x_vif_to_bss_conf(vif, i);\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c=2209=mt7925_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-2210-\t\t\tu16 old_links, u16 new_links,\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c:2211:\t\t\tstruct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS])\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-2212-{\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c:2213:\tstruct mt792x_bss_conf *mconfs[IEEE80211_MLD_MAX_NUM_LINKS] = {}, *mconf;\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c:2214:\tstruct mt792x_link_sta *mlinks[IEEE80211_MLD_MAX_NUM_LINKS] = {}, *mlink;\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-2215-\tstruct mt792x_vif *mvif = (struct mt792x_vif *)vif-\u003edrv_priv;\n--\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-2228-\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c:2229:\tfor_each_set_bit(link_id, \u0026rem, IEEE80211_MLD_MAX_NUM_LINKS) {\ndrivers/net/wireless/mediatek/mt76/mt7925/main.c-2230-\t\tmconf = mt792x_vif_to_link(mvif, link_id);\n\n"
}

41/2 2026/09/15 18:16 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 72349
							output: 47
							thoughts: 223
						

Thoughts:
**Investigating a Recent Kernel Change**

Okay, so I'm digging into this kernel patch and something immediately jumps out. It looks like `IEEE80211_MLD_MAX_NUM_LINKS` is defined as 15. The problem? `IEEE80211_LINK_UNSPECIFIED` *used* to also be 15, and now I'm starting to see why. I'm focusing on a removed check in `net/mac80211/rx.c`. I see the code was removed:

```c
-	if (WARN_ON_ONCE(status->link_id >= IEEE80211_LINK_UNSPECIFIED))
-		goto drop;
```

That check is gone, which is definitely worth a closer look. Now I see that in `ieee80211_rx_handlers` we have:

```c
			rx->link_id = status->link_id;
			rx->link = rcu_dereference(rx->sdata->link[status->link_id]);
```

Ah-ha! The code uses `status->link_id` to index into `rx->sdata->link`. That's where it all connects! The key is the array size of `rx->sdata->link`. I need to figure out what the size of that array is. I need to go look at either the `struct ieee80211_sub_if_data` or `struct ieee80211_vif` definitions to understand the bounds of that array to ensure there's no potential out-of-bounds access. Given that the `IEEE80211_LINK_UNSPECIFIED` value was the same as the maximum number of links, I'm concerned that removing this check created a vulnerability.



42/2 2026/09/15 18:16 tool grepper
0m Args:
{
  "Expression": "struct ieee80211_link_data __rcu \\*link\\[",
  "PathPrefix": "net/mac80211/"
}

Results:
{
  "Output": "net/mac80211/ieee80211_i.h=1166=struct ieee80211_sub_if_data {\n--\nnet/mac80211/ieee80211_i.h-1245-\tstruct ieee80211_link_data deflink;\nnet/mac80211/ieee80211_i.h:1246:\tstruct ieee80211_link_data __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];\nnet/mac80211/ieee80211_i.h-1247-\n"
}

43/2 2026/09/15 18:16 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 73213
							output: 30
							thoughts: 820
						

Thoughts:
**Vulnerability Discovery: Out-of-Bounds Read in mac80211**

Okay, so I've been digging into this, and it looks like we have a potential critical out-of-bounds read vulnerability in the mac80211 subsystem. Specifically, I see that `IEEE80211_MLD_MAX_NUM_LINKS` is defined with a limit of 15 elements (0..14). The code, however, doesn't always handle potential overflows of the `link_id`.

I'm seeing references to `status->link_id`, which is stored in a 4-bit field, which could have a value up to 15. The problem is when `status->link_id` is 15, we're going to attempt to read `link[15]`, which is outside the allocated bounds. This looks like a classic buffer overflow that could lead to information leaks or even arbitrary code execution.

I've spotted this issue in several key locations. I see the unsafe access in `iface.c`, `mlme.c`, and `scan.c` within the mac80211 core.  I also see it when looking at the `rx.c` code.

The original code appears to have had protections in place, like `status->link_valid` and the `WARN_ON_ONCE` check guarding against this, but these checks have been completely removed. This makes the vulnerability much more dangerous.

This is definitely a prime target for fuzzing. It touches critical parts of the networking subsystem: mac80211 and netlink (nl80211), and I think it's definitely worth pursuing.

Now, let's look at the configuration requirements:
1.  **WorthFuzzing**: It's definitely True.
2.  **Required Configurations**:
    *   `MAC80211`
    *   `CFG80211`
    *   `MAC80211_HWSIM`
    These seem to be the baseline requirements for compiling and reaching this code.  I'll verify this by examining the relevant source files and their makefiles. I've already checked in `net/wireless/nl80211.c` and found no conditional compilation based on other configurations. I'll need to double check any conditional includes within mac80211 specifically.



44/2 2026/09/15 18:16 tool grepper
0m Args:
{
  "Expression": "nl80211",
  "PathPrefix": "net/wireless/"
}

Results:
{
  "Output": "\nFull output is too long, showing 500 out of 5305 lines.\nUse more precise expression if possible.\n\nnet/wireless/Kconfig=38=config NL80211_TESTMODE\nnet/wireless/Kconfig:39:\tbool \"nl80211 testmode command\"\nnet/wireless/Kconfig-40-\thelp\nnet/wireless/Kconfig:41:\t  The nl80211 testmode command helps implementing things like\nnet/wireless/Kconfig-42-\t  factory calibration or validation tools for wireless chips.\n--\nnet/wireless/Makefile=7=obj-$(CONFIG_WEXT_PRIV) += wext-priv.o\nnet/wireless/Makefile-8-\nnet/wireless/Makefile:9:cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o\nnet/wireless/Makefile-10-cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o\n--\nnet/wireless/ap.c-8-#include \u003cnet/cfg80211.h\u003e\nnet/wireless/ap.c:9:#include \"nl80211.h\"\nnet/wireless/ap.c-10-#include \"core.h\"\n--\nnet/wireless/ap.c=14=static int ___cfg80211_stop_ap(struct cfg80211_registered_device *rdev,\n--\nnet/wireless/ap.c-41-\t\tif (notify)\nnet/wireless/ap.c:42:\t\t\tnl80211_send_ap_stopped(wdev, link_id);\nnet/wireless/ap.c-43-\n--\nnet/wireless/chan.c=23=void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,\nnet/wireless/chan.c-24-\t\t\t     struct ieee80211_channel *chan,\nnet/wireless/chan.c:25:\t\t\t     enum nl80211_channel_type chan_type)\nnet/wireless/chan.c-26-{\n--\nnet/wireless/chan.c=178=static bool cfg80211_edmg_chandef_valid(const struct cfg80211_chan_def *chandef)\n--\nnet/wireless/chan.c-262-\nnet/wireless/chan.c:263:int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width)\nnet/wireless/chan.c-264-{\n--\nnet/wireless/chan.c-305-}\nnet/wireless/chan.c:306:EXPORT_SYMBOL(nl80211_chan_width_to_mhz);\nnet/wireless/chan.c-307-\nnet/wireless/chan.c=308=static bool cfg80211_valid_center_freq(u32 center,\nnet/wireless/chan.c:309:\t\t\t\t       enum nl80211_chan_width width)\nnet/wireless/chan.c-310-{\n--\nnet/wireless/chan.c-317-\nnet/wireless/chan.c:318:\tbw = nl80211_chan_width_to_mhz(width);\nnet/wireless/chan.c-319-\tif (bw \u003c 0)\n--\nnet/wireless/chan.c=489=int cfg80211_chandef_primary(const struct cfg80211_chan_def *c,\nnet/wireless/chan.c:490:\t\t\t     enum nl80211_chan_width primary_chan_width,\nnet/wireless/chan.c-491-\t\t\t     u16 *punctured)\nnet/wireless/chan.c-492-{\nnet/wireless/chan.c:493:\tint pri_width = nl80211_chan_width_to_mhz(primary_chan_width);\nnet/wireless/chan.c-494-\tint width = cfg80211_chandef_get_width(c);\n--\nnet/wireless/chan.c=742=check_chandef_primary_compat(const struct cfg80211_chan_def *c1,\nnet/wireless/chan.c-743-\t\t\t     const struct cfg80211_chan_def *c2,\nnet/wireless/chan.c:744:\t\t\t     enum nl80211_chan_width primary_chan_width)\nnet/wireless/chan.c-745-{\n--\nnet/wireless/chan.c=866=void cfg80211_set_dfs_state(struct wiphy *wiphy,\nnet/wireless/chan.c-867-\t\t\t    const struct cfg80211_chan_def *chandef,\nnet/wireless/chan.c:868:\t\t\t    enum nl80211_dfs_state dfs_state)\nnet/wireless/chan.c-869-{\n--\nnet/wireless/chan.c=918=cfg80211_dfs_permissive_check_wdev(struct cfg80211_registered_device *rdev,\nnet/wireless/chan.c:919:\t\t\t\t   enum nl80211_iftype iftype,\nnet/wireless/chan.c-920-\t\t\t\t   struct wireless_dev *wdev,\n--\nnet/wireless/chan.c=959=static bool cfg80211_dfs_permissive_chan(struct wiphy *wiphy,\nnet/wireless/chan.c:960:\t\t\t\t\t enum nl80211_iftype iftype,\nnet/wireless/chan.c-961-\t\t\t\t\t struct ieee80211_channel *chan)\n--\nnet/wireless/chan.c=990=static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,\nnet/wireless/chan.c-991-\t\t\t\t\t   const struct cfg80211_chan_def *chandef,\nnet/wireless/chan.c:992:\t\t\t\t\t   enum nl80211_iftype iftype)\nnet/wireless/chan.c-993-{\n--\nnet/wireless/chan.c=1014=int cfg80211_chandef_dfs_required(struct wiphy *wiphy,\nnet/wireless/chan.c-1015-\t\t\t\t  const struct cfg80211_chan_def *chandef,\nnet/wireless/chan.c:1016:\t\t\t\t  enum nl80211_iftype iftype)\nnet/wireless/chan.c-1017-{\n--\nnet/wireless/chan.c=1621=EXPORT_SYMBOL(cfg80211_chandef_usable);\nnet/wireless/chan.c-1622-\nnet/wireless/chan.c:1623:static bool cfg80211_ir_permissive_check_wdev(enum nl80211_iftype iftype,\nnet/wireless/chan.c-1624-\t\t\t\t\t      struct wireless_dev *wdev,\n--\nnet/wireless/chan.c=1694=static bool cfg80211_ir_permissive_chan(struct wiphy *wiphy,\nnet/wireless/chan.c:1695:\t\t\t\t\tenum nl80211_iftype iftype,\nnet/wireless/chan.c-1696-\t\t\t\t\tstruct ieee80211_channel *chan)\n--\nnet/wireless/chan.c=1738=static bool _cfg80211_reg_can_beacon(struct wiphy *wiphy,\nnet/wireless/chan.c-1739-\t\t\t\t     struct cfg80211_chan_def *chandef,\nnet/wireless/chan.c:1740:\t\t\t\t     enum nl80211_iftype iftype,\nnet/wireless/chan.c-1741-\t\t\t\t     u32 prohibited_flags,\n--\nnet/wireless/core.c-17-#include \u003clinux/slab.h\u003e\nnet/wireless/core.c:18:#include \u003clinux/nl80211.h\u003e\nnet/wireless/core.c-19-#include \u003clinux/debugfs.h\u003e\n--\nnet/wireless/core.c-26-#include \u003cnet/cfg80211.h\u003e\nnet/wireless/core.c:27:#include \"nl80211.h\"\nnet/wireless/core.c-28-#include \"core.h\"\n--\nnet/wireless/core.c=129=int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,\n--\nnet/wireless/core.c-150-\nnet/wireless/core.c:151:\tnl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);\nnet/wireless/core.c-152-\n--\nnet/wireless/core.c=156=int cfg80211_switch_netns(struct cfg80211_registered_device *rdev,\n--\nnet/wireless/core.c-198-\t\t\tcontinue;\nnet/wireless/core.c:199:\t\tnl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE);\nnet/wireless/core.c-200-\t}\nnet/wireless/core.c-201-\nnet/wireless/core.c:202:\tnl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);\nnet/wireless/core.c-203-\n--\nnet/wireless/core.c-208-\nnet/wireless/core.c:209:\tnl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);\nnet/wireless/core.c-210-\n--\nnet/wireless/core.c-213-\t\t\tcontinue;\nnet/wireless/core.c:214:\t\tnl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);\nnet/wireless/core.c-215-\t}\n--\nnet/wireless/core.c=853=int wiphy_register(struct wiphy *wiphy)\n--\nnet/wireless/core.c-856-\tint res;\nnet/wireless/core.c:857:\tenum nl80211_band band;\nnet/wireless/core.c-858-\tstruct ieee80211_supported_band *sband;\n--\nnet/wireless/core.c-1185-\tcfg80211_debugfs_rdev_add(rdev);\nnet/wireless/core.c:1186:\tnl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);\nnet/wireless/core.c-1187-\twiphy_unlock(\u0026rdev-\u003ewiphy);\n--\nnet/wireless/core.c-1199-\nnet/wireless/core.c:1200:\t\tnl80211_send_reg_change_event(\u0026request);\nnet/wireless/core.c-1201-\t}\n--\nnet/wireless/core.c=1290=void wiphy_unregister(struct wiphy *wiphy)\n--\nnet/wireless/core.c-1305-\twiphy_lock(\u0026rdev-\u003ewiphy);\nnet/wireless/core.c:1306:\tnl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);\nnet/wireless/core.c-1307-\trdev-\u003ewiphy.registered = false;\n--\nnet/wireless/core.c=1408=static void _cfg80211_unregister_wdev(struct wireless_dev *wdev,\n--\nnet/wireless/core.c-1417-\nnet/wireless/core.c:1418:\tnl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE);\nnet/wireless/core.c-1419-\n--\nnet/wireless/core.c-1453-\twiphy_work_cancel(wdev-\u003ewiphy, \u0026wdev-\u003ecqm_rssi_work);\nnet/wireless/core.c:1454:\t/* deleted from the list, so can't be found from nl80211 any more */\nnet/wireless/core.c-1455-\tcqm_config = rcu_access_pointer(wdev-\u003ecqm_config);\n--\nnet/wireless/core.c=1493=void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,\nnet/wireless/core.c:1494:\t\t\t       enum nl80211_iftype iftype, int num)\nnet/wireless/core.c-1495-{\n--\nnet/wireless/core.c=1648=void cfg80211_register_wdev(struct cfg80211_registered_device *rdev,\n--\nnet/wireless/core.c-1671-\nnet/wireless/core.c:1672:\tnl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);\nnet/wireless/core.c-1673-}\n--\nnet/wireless/core.c=2037=static int __init cfg80211_init(void)\n--\nnet/wireless/core.c-2052-\nnet/wireless/core.c:2053:\terr = nl80211_init();\nnet/wireless/core.c-2054-\tif (err)\nnet/wireless/core.c:2055:\t\tgoto out_fail_nl80211;\nnet/wireless/core.c-2056-\n--\nnet/wireless/core.c-2074-\tdebugfs_remove(ieee80211_debugfs_dir);\nnet/wireless/core.c:2075:\tnl80211_exit();\nnet/wireless/core.c:2076:out_fail_nl80211:\nnet/wireless/core.c-2077-\tunregister_netdevice_notifier(\u0026cfg80211_netdev_notifier);\n--\nnet/wireless/core.c=2087=static void __exit cfg80211_exit(void)\n--\nnet/wireless/core.c-2089-\tdebugfs_remove(ieee80211_debugfs_dir);\nnet/wireless/core.c:2090:\tnl80211_exit();\nnet/wireless/core.c-2091-\tunregister_netdevice_notifier(\u0026cfg80211_netdev_notifier);\n--\nnet/wireless/core.h=306=struct cfg80211_cqm_config {\n--\nnet/wireless/core.h-309-\ts32 last_rssi_event_value;\nnet/wireless/core.h:310:\tenum nl80211_cqm_rssi_threshold_event last_rssi_event_type;\nnet/wireless/core.h-311-\tbool use_range_api;\n--\nnet/wireless/core.h=467=int cfg80211_change_iface(struct cfg80211_registered_device *rdev,\nnet/wireless/core.h:468:\t\t\t  struct net_device *dev, enum nl80211_iftype ntype,\nnet/wireless/core.h-469-\t\t\t  struct vif_params *params);\n--\nnet/wireless/core.h=485=void cfg80211_set_dfs_state(struct wiphy *wiphy,\nnet/wireless/core.h-486-\t\t\t    const struct cfg80211_chan_def *chandef,\nnet/wireless/core.h:487:\t\t\t    enum nl80211_dfs_state dfs_state);\nnet/wireless/core.h-488-\n--\nnet/wireless/core.h=543=int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,\nnet/wireless/core.h:544:\t\t\t\t enum nl80211_iftype iftype, u32 beacon_int);\nnet/wireless/core.h-545-\nnet/wireless/core.h=546=void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,\nnet/wireless/core.h:547:\t\t\t       enum nl80211_iftype iftype, int num);\nnet/wireless/core.h-548-\n--\nnet/wireless/debugfs.c=84=static ssize_t ht40allow_map_read(struct file *file,\n--\nnet/wireless/debugfs.c-90-\tunsigned int offset = 0, buf_size = PAGE_SIZE, i;\nnet/wireless/debugfs.c:91:\tenum nl80211_band band;\nnet/wireless/debugfs.c-92-\tstruct ieee80211_supported_band *sband;\n--\nnet/wireless/ibss.c-14-#include \"wext-compat.h\"\nnet/wireless/ibss.c:15:#include \"nl80211.h\"\nnet/wireless/ibss.c-16-#include \"rdev-ops.h\"\n--\nnet/wireless/ibss.c=19=void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,\n--\nnet/wireless/ibss.c-49-\nnet/wireless/ibss.c:50:\tnl80211_send_ibss_bssid(wiphy_to_rdev(wdev-\u003ewiphy), dev, bssid,\nnet/wireless/ibss.c-51-\t\t\t\tGFP_KERNEL);\n--\nnet/wireless/ibss.c=87=int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,\n--\nnet/wireless/ibss.c-109-\t\tstruct ieee80211_supported_band *sband;\nnet/wireless/ibss.c:110:\t\tenum nl80211_band band;\nnet/wireless/ibss.c-111-\t\tu32 flag;\n--\nnet/wireless/ibss.c=215=int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,\n--\nnet/wireless/ibss.c-218-\tstruct cfg80211_cached_keys *ck = NULL;\nnet/wireless/ibss.c:219:\tenum nl80211_band band;\nnet/wireless/ibss.c-220-\tint i, err;\n--\nnet/wireless/mesh.c-8-#include \u003cnet/cfg80211.h\u003e\nnet/wireless/mesh.c:9:#include \"nl80211.h\"\nnet/wireless/mesh.c-10-#include \"core.h\"\n--\nnet/wireless/mesh.c=102=int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,\n--\nnet/wireless/mesh.c-139-\t\t/* if we don't have that either, use the first usable channel */\nnet/wireless/mesh.c:140:\t\tenum nl80211_band band;\nnet/wireless/mesh.c-141-\n--\nnet/wireless/mlme.c-13-#include \u003clinux/netdevice.h\u003e\nnet/wireless/mlme.c:14:#include \u003clinux/nl80211.h\u003e\nnet/wireless/mlme.c-15-#include \u003clinux/slab.h\u003e\n--\nnet/wireless/mlme.c-19-#include \"core.h\"\nnet/wireless/mlme.c:20:#include \"nl80211.h\"\nnet/wireless/mlme.c-21-#include \"rdev-ops.h\"\n--\nnet/wireless/mlme.c=24=void cfg80211_rx_assoc_resp(struct net_device *dev,\n--\nnet/wireless/mlme.c-96-\nnet/wireless/mlme.c:97:\tnl80211_send_rx_assoc(rdev, dev, data);\nnet/wireless/mlme.c-98-\t/* update current_bss etc., consumes the bss reference */\n--\nnet/wireless/mlme.c=115=static void cfg80211_process_auth(struct wireless_dev *wdev,\n--\nnet/wireless/mlme.c-119-\nnet/wireless/mlme.c:120:\tnl80211_send_rx_auth(rdev, wdev-\u003enetdev, buf, len, GFP_KERNEL);\nnet/wireless/mlme.c-121-\tcfg80211_sme_rx_auth(wdev, buf, len);\n--\nnet/wireless/mlme.c=124=static void cfg80211_process_deauth(struct wireless_dev *wdev,\n--\nnet/wireless/mlme.c-133-\nnet/wireless/mlme.c:134:\tnl80211_send_deauth(rdev, wdev-\u003enetdev, buf, len, reconnect, GFP_KERNEL);\nnet/wireless/mlme.c-135-\n--\nnet/wireless/mlme.c=143=static void cfg80211_process_disassoc(struct wireless_dev *wdev,\n--\nnet/wireless/mlme.c-152-\nnet/wireless/mlme.c:153:\tnl80211_send_disassoc(rdev, wdev-\u003enetdev, buf, len, reconnect,\nnet/wireless/mlme.c-154-\t\t\t      GFP_KERNEL);\n--\nnet/wireless/mlme.c=201=void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr)\n--\nnet/wireless/mlme.c-208-\nnet/wireless/mlme.c:209:\tnl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);\nnet/wireless/mlme.c-210-\tcfg80211_sme_auth_timeout(wdev);\n--\nnet/wireless/mlme.c=214=void cfg80211_assoc_failure(struct net_device *dev,\n--\nnet/wireless/mlme.c-225-\tif (data-\u003etimeout) {\nnet/wireless/mlme.c:226:\t\tnl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);\nnet/wireless/mlme.c-227-\t\tcfg80211_sme_assoc_timeout(wdev);\n--\nnet/wireless/mlme.c=277=void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,\nnet/wireless/mlme.c:278:\t\t\t\t  enum nl80211_key_type key_type, int key_id,\nnet/wireless/mlme.c-279-\t\t\t\t  const u8 *tsc, gfp_t gfp)\n--\nnet/wireless/mlme.c-299-\ttrace_cfg80211_michael_mic_failure(dev, addr, key_type, key_id, tsc);\nnet/wireless/mlme.c:300:\tnl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);\nnet/wireless/mlme.c-301-}\n--\nnet/wireless/mlme.c=1010=bool cfg80211_rx_mgmt_ext(struct wireless_dev *wdev,\n--\nnet/wireless/mlme.c-1051-\t\t/* Indicate the received Action frame to user space */\nnet/wireless/mlme.c:1052:\t\tif (nl80211_send_mgmt(rdev, wdev, reg-\u003enlportid, info,\nnet/wireless/mlme.c-1053-\t\t\t\t      GFP_ATOMIC))\n--\nnet/wireless/mlme.c=1073=void cfg80211_dfs_channels_update_work(struct work_struct *work)\n--\nnet/wireless/mlme.c-1083-\tunsigned long time_dfs_update;\nnet/wireless/mlme.c:1084:\tenum nl80211_radar_event radar_event;\nnet/wireless/mlme.c-1085-\tint bandid, i;\n--\nnet/wireless/mlme.c-1128-\nnet/wireless/mlme.c:1129:\t\t\t\tnl80211_radar_notify(rdev, \u0026chandef,\nnet/wireless/mlme.c-1130-\t\t\t\t\t\t     radar_event, NULL,\n--\nnet/wireless/mlme.c=1155=void __cfg80211_radar_event(struct wiphy *wiphy,\n--\nnet/wireless/mlme.c-1175-\nnet/wireless/mlme.c:1176:\tnl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp);\nnet/wireless/mlme.c-1177-\n--\nnet/wireless/mlme.c=1183=void cfg80211_cac_event(struct net_device *netdev,\nnet/wireless/mlme.c-1184-\t\t\tconst struct cfg80211_chan_def *chandef,\nnet/wireless/mlme.c:1185:\t\t\tenum nl80211_radar_event event, gfp_t gfp,\nnet/wireless/mlme.c-1186-\t\t\tunsigned int link_id)\n--\nnet/wireless/mlme.c-1226-\nnet/wireless/mlme.c:1227:\tnl80211_radar_notify(rdev, chandef, event, netdev, gfp);\nnet/wireless/mlme.c-1228-}\n--\nnet/wireless/mlme.c=1232=__cfg80211_background_cac_event(struct cfg80211_registered_device *rdev,\n--\nnet/wireless/mlme.c-1234-\t\t\t\tconst struct cfg80211_chan_def *chandef,\nnet/wireless/mlme.c:1235:\t\t\t\tenum nl80211_radar_event event)\nnet/wireless/mlme.c-1236-{\n--\nnet/wireless/mlme.c-1265-\tnetdev = wdev ? wdev-\u003enetdev : NULL;\nnet/wireless/mlme.c:1266:\tnl80211_radar_notify(rdev, chandef, event, netdev, GFP_KERNEL);\nnet/wireless/mlme.c-1267-}\n--\nnet/wireless/mlme.c=1353=void cfg80211_stop_radar_detection(struct wireless_dev *wdev)\n--\nnet/wireless/mlme.c-1368-\t\tcfg80211_set_cac_state(wiphy, \u0026chandef, false);\nnet/wireless/mlme.c:1369:\t\tnl80211_radar_notify(rdev, \u0026chandef, NL80211_RADAR_CAC_ABORTED,\nnet/wireless/mlme.c-1370-\t\t\t\t     wdev-\u003enetdev, GFP_KERNEL);\n--\nnet/wireless/mlme.c=1419=void cfg80211_mlo_reconf_add_done(struct net_device *dev,\n--\nnet/wireless/mlme.c-1474-\twdev-\u003evalid_links |= data-\u003eadded_links;\nnet/wireless/mlme.c:1475:\tnl80211_mlo_reconf_add_done(dev, data);\nnet/wireless/mlme.c-1476-}\n--\nnet/wireless/nl80211.c-17-#include \u003clinux/ieee80211.h\u003e\nnet/wireless/nl80211.c:18:#include \u003clinux/nl80211.h\u003e\nnet/wireless/nl80211.c-19-#include \u003clinux/rtnetlink.h\u003e\n--\nnet/wireless/nl80211.c-30-#include \"core.h\"\nnet/wireless/nl80211.c:31:#include \"nl80211.h\"\nnet/wireless/nl80211.c-32-#include \"reg.h\"\n--\nnet/wireless/nl80211.c-34-\nnet/wireless/nl80211.c:35:static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,\nnet/wireless/nl80211.c-36-\t\t\t\t   struct genl_info *info,\n--\nnet/wireless/nl80211.c-40-/* the netlink family */\nnet/wireless/nl80211.c:41:static struct genl_family nl80211_fam;\nnet/wireless/nl80211.c-42-\nnet/wireless/nl80211.c-43-/* multicast groups */\nnet/wireless/nl80211.c:44:enum nl80211_multicast_groups {\nnet/wireless/nl80211.c-45-\tNL80211_MCGRP_CONFIG,\n--\nnet/wireless/nl80211.c-53-\nnet/wireless/nl80211.c:54:static const struct genl_multicast_group nl80211_mcgrps[] = {\nnet/wireless/nl80211.c-55-\t[NL80211_MCGRP_CONFIG] = { .name = NL80211_MULTICAST_GROUP_CONFIG },\n--\nnet/wireless/nl80211.c=438=static int validate_uhr_operation(const struct nlattr *attr,\n--\nnet/wireless/nl80211.c-449-/* policy for the attributes */\nnet/wireless/nl80211.c:450:static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR];\nnet/wireless/nl80211.c-451-\nnet/wireless/nl80211.c=452=static const struct nla_policy\nnet/wireless/nl80211.c:453:nl80211_ftm_responder_policy[NL80211_FTM_RESP_ATTR_MAX + 1] = {\nnet/wireless/nl80211.c-454-\t[NL80211_FTM_RESP_ATTR_ENABLED] = { .type = NLA_FLAG, },\n--\nnet/wireless/nl80211.c=461=static const struct nla_policy\nnet/wireless/nl80211.c:462:nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = {\nnet/wireless/nl80211.c-463-\t[NL80211_PMSR_FTM_REQ_ATTR_ASAP] = { .type = NLA_FLAG },\n--\nnet/wireless/nl80211.c=495=static const struct nla_policy\nnet/wireless/nl80211.c:496:nl80211_pmsr_req_data_policy[NL80211_PMSR_TYPE_MAX + 1] = {\nnet/wireless/nl80211.c-497-\t[NL80211_PMSR_TYPE_FTM] =\nnet/wireless/nl80211.c:498:\t\tNLA_POLICY_NESTED(nl80211_pmsr_ftm_req_attr_policy),\nnet/wireless/nl80211.c-499-};\n--\nnet/wireless/nl80211.c=501=static const struct nla_policy\nnet/wireless/nl80211.c:502:nl80211_pmsr_req_attr_policy[NL80211_PMSR_REQ_ATTR_MAX + 1] = {\nnet/wireless/nl80211.c-503-\t[NL80211_PMSR_REQ_ATTR_DATA] =\nnet/wireless/nl80211.c:504:\t\tNLA_POLICY_NESTED(nl80211_pmsr_req_data_policy),\nnet/wireless/nl80211.c-505-\t[NL80211_PMSR_REQ_ATTR_GET_AP_TSF] = { .type = NLA_FLAG },\n--\nnet/wireless/nl80211.c=508=static const struct nla_policy\nnet/wireless/nl80211.c:509:nl80211_pmsr_peer_attr_policy[NL80211_PMSR_PEER_ATTR_MAX + 1] = {\nnet/wireless/nl80211.c-510-\t[NL80211_PMSR_PEER_ATTR_ADDR] = NLA_POLICY_ETH_ADDR,\nnet/wireless/nl80211.c:511:\t[NL80211_PMSR_PEER_ATTR_CHAN] = NLA_POLICY_NESTED(nl80211_policy),\nnet/wireless/nl80211.c-512-\t[NL80211_PMSR_PEER_ATTR_REQ] =\nnet/wireless/nl80211.c:513:\t\tNLA_POLICY_NESTED(nl80211_pmsr_req_attr_policy),\nnet/wireless/nl80211.c-514-\t[NL80211_PMSR_PEER_ATTR_RESP] = { .type = NLA_REJECT },\n--\nnet/wireless/nl80211.c=519=static const struct nla_policy\nnet/wireless/nl80211.c:520:nl80211_pmsr_attr_policy[NL80211_PMSR_ATTR_MAX + 1] = {\nnet/wireless/nl80211.c-521-\t[NL80211_PMSR_ATTR_MAX_PEERS] = { .type = NLA_REJECT },\n--\nnet/wireless/nl80211.c-525-\t[NL80211_PMSR_ATTR_PEERS] =\nnet/wireless/nl80211.c:526:\t\tNLA_POLICY_NESTED_ARRAY(nl80211_pmsr_peer_attr_policy),\nnet/wireless/nl80211.c-527-};\n--\nnet/wireless/nl80211.c=545=he_bss_color_policy[NL80211_HE_BSS_COLOR_ATTR_MAX + 1] = {\n--\nnet/wireless/nl80211.c-550-\nnet/wireless/nl80211.c:551:static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {\nnet/wireless/nl80211.c-552-\t[NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,\n--\nnet/wireless/nl80211.c-555-\t\t\t\t.len = NL80211_MAX_SUPP_HT_RATES },\nnet/wireless/nl80211.c:556:\t[NL80211_TXRATE_VHT] = NLA_POLICY_EXACT_LEN_WARN(sizeof(struct nl80211_txrate_vht)),\nnet/wireless/nl80211.c-557-\t[NL80211_TXRATE_GI] = { .type = NLA_U8 },\nnet/wireless/nl80211.c:558:\t[NL80211_TXRATE_HE] = NLA_POLICY_EXACT_LEN(sizeof(struct nl80211_txrate_he)),\nnet/wireless/nl80211.c-559-\t[NL80211_TXRATE_HE_GI] =  NLA_POLICY_RANGE(NLA_U8,\n--\nnet/wireless/nl80211.c-564-\t\t\t\t\t\t   NL80211_RATE_INFO_HE_4XLTF),\nnet/wireless/nl80211.c:565:\t[NL80211_TXRATE_EHT] = NLA_POLICY_EXACT_LEN(sizeof(struct nl80211_txrate_eht)),\nnet/wireless/nl80211.c-566-\t[NL80211_TXRATE_EHT_GI] =  NLA_POLICY_RANGE(NLA_U8,\n--\nnet/wireless/nl80211.c=576=static const struct nla_policy\nnet/wireless/nl80211.c:577:nl80211_tid_config_attr_policy[NL80211_TID_CONFIG_ATTR_MAX + 1] = {\nnet/wireless/nl80211.c-578-\t[NL80211_TID_CONFIG_ATTR_VIF_SUPP] = { .type = NLA_U64 },\n--\nnet/wireless/nl80211.c-594-\t[NL80211_TID_CONFIG_ATTR_TX_RATE] =\nnet/wireless/nl80211.c:595:\t\t\tNLA_POLICY_NESTED(nl80211_txattr_policy),\nnet/wireless/nl80211.c-596-};\n--\nnet/wireless/nl80211.c=598=static const struct nla_policy\nnet/wireless/nl80211.c:599:nl80211_fils_discovery_policy[NL80211_FILS_DISCOVERY_ATTR_MAX + 1] = {\nnet/wireless/nl80211.c-600-\t[NL80211_FILS_DISCOVERY_ATTR_INT_MIN] = NLA_POLICY_MAX(NLA_U32, 10000),\n--\nnet/wireless/nl80211.c=608=static const struct nla_policy\nnet/wireless/nl80211.c:609:nl80211_unsol_bcast_probe_resp_policy[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_MAX + 1] = {\nnet/wireless/nl80211.c-610-\t[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT] = NLA_POLICY_MAX(NLA_U32, 20),\n--\nnet/wireless/nl80211.c=627=static const struct nla_policy\nnet/wireless/nl80211.c:628:nl80211_mbssid_config_policy[NL80211_MBSSID_CONFIG_ATTR_MAX + 1] = {\nnet/wireless/nl80211.c-629-\t[NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES] = NLA_POLICY_MIN(NLA_U8, 2),\n--\nnet/wireless/nl80211.c=639=static const struct nla_policy\nnet/wireless/nl80211.c:640:nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] = {\nnet/wireless/nl80211.c-641-\t[NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },\n--\nnet/wireless/nl80211.c=645=static const struct nla_policy\nnet/wireless/nl80211.c:646:nl80211_s1g_short_beacon[NL80211_S1G_SHORT_BEACON_ATTR_MAX + 1] = {\nnet/wireless/nl80211.c-647-\t[NL80211_S1G_SHORT_BEACON_ATTR_HEAD] =\n--\nnet/wireless/nl80211.c=655=static const struct nla_policy\nnet/wireless/nl80211.c:656:nl80211_nan_band_conf_policy[NL80211_NAN_BAND_CONF_ATTR_MAX + 1] = {\nnet/wireless/nl80211.c-657-\t[NL80211_NAN_BAND_CONF_BAND] = NLA_POLICY_MAX(NLA_U8,\n--\nnet/wireless/nl80211.c=666=static const struct nla_policy\nnet/wireless/nl80211.c:667:nl80211_nan_peer_map_policy[NL80211_NAN_PEER_MAP_ATTR_MAX + 1] = {\nnet/wireless/nl80211.c-668-\t[NL80211_NAN_PEER_MAP_ATTR_MAP_ID] = NLA_POLICY_MAX(NLA_U8, 15),\n--\nnet/wireless/nl80211.c=673=static const struct nla_policy\nnet/wireless/nl80211.c:674:nl80211_nan_conf_policy[NL80211_NAN_CONF_ATTR_MAX + 1] = {\nnet/wireless/nl80211.c-675-\t[NL80211_NAN_CONF_CLUSTER_ID] =\n--\nnet/wireless/nl80211.c-683-\t[NL80211_NAN_CONF_BAND_CONFIGS] =\nnet/wireless/nl80211.c:684:\t\tNLA_POLICY_NESTED_ARRAY(nl80211_nan_band_conf_policy),\nnet/wireless/nl80211.c-685-\t[NL80211_NAN_CONF_SCAN_PERIOD] = { .type = NLA_U16 },\n--\nnet/wireless/nl80211.c-691-\nnet/wireless/nl80211.c:692:static const struct netlink_range_validation nl80211_punct_bitmap_range = {\nnet/wireless/nl80211.c-693-\t.min = 0,\n--\nnet/wireless/nl80211.c=697=static const struct netlink_range_validation q_range = {\n--\nnet/wireless/nl80211.c-700-\nnet/wireless/nl80211.c:701:static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {\nnet/wireless/nl80211.c-702-\t[0] = { .strict_start_type = NL80211_ATTR_HE_OBSS_PD },\n--\nnet/wireless/nl80211.c-805-\t[NL80211_ATTR_STA_FLAGS2] =\nnet/wireless/nl80211.c:806:\t\tNLA_POLICY_EXACT_LEN_WARN(sizeof(struct nl80211_sta_flag_update)),\nnet/wireless/nl80211.c-807-\t[NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },\n--\nnet/wireless/nl80211.c-862-\t[NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG },\nnet/wireless/nl80211.c:863:\t[NL80211_ATTR_STA_WME] = NLA_POLICY_NESTED(nl80211_sta_wme_policy),\nnet/wireless/nl80211.c-864-\t[NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED },\n--\nnet/wireless/nl80211.c-960-\t[NL80211_ATTR_BANDS] = { .type = NLA_U32 },\nnet/wireless/nl80211.c:961:\t[NL80211_ATTR_NAN_CONFIG] = NLA_POLICY_NESTED(nl80211_nan_conf_policy),\nnet/wireless/nl80211.c-962-\t[NL80211_ATTR_NAN_FUNC] = { .type = NLA_NESTED },\n--\nnet/wireless/nl80211.c-969-\t[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST] = {\nnet/wireless/nl80211.c:970:\t\t.len = sizeof(struct nl80211_bss_select_rssi_adjust)\nnet/wireless/nl80211.c-971-\t},\n--\nnet/wireless/nl80211.c-992-\t[NL80211_ATTR_FTM_RESPONDER] =\nnet/wireless/nl80211.c:993:\t\tNLA_POLICY_NESTED(nl80211_ftm_responder_policy),\nnet/wireless/nl80211.c-994-\t[NL80211_ATTR_TIMEOUT] = NLA_POLICY_MIN(NLA_U32, 1),\n\n"
}

45/2 2026/09/15 18:16 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 86674
							output: 38
							thoughts: 2192
						

Thoughts:
**Focus Symbol & Configuration Analysis for mac80211 Code Changes**

Okay, let's break this down. The primary goal here is to identify the critical functions, the so-called "FocusSymbols", modified in mac80211, nl80211, and cfg80211. This analysis is crucial for ensuring effective coverage when testing these changes.

First, I've gone through the code, meticulously listing the functions affected: `nl80211_tx_mgmt`, `nl80211_send_mgmt`, `ieee80211_mgmt_tx`, `ieee80211_tx_skb_tid`, `__ieee80211_tx_skb_tid_band`, `ieee80211_rx_list`, `ieee80211_rx_napi`, `__ieee80211_rx_handle_packet`, `__ieee80211_rx_handle_8023`, `ieee80211_rx_handlers`, `ieee80211_rx_data_set_link_sta`, and `ieee80211_release_reorder_timeout`, along with `mac80211_hwsim_rx`.

Next, I need to understand whether those functions are static or non-static because of how the coverage tools work. I've confirmed that `__ieee80211_rx_handle_packet`, `__ieee80211_rx_handle_8023`, and `ieee80211_rx_handlers` are declared as static. But, I know that large static functions are often kept as symbols. Non-static, exported symbols like `ieee80211_rx_list`, and `ieee80211_rx_napi` are critical to ensure that coverage targeting is effective.

Regarding static functions, I know to keep in mind the guidelines for FocusSymbols. The goal is to avoid ubiquitous functions and target specific logic or entrypoints. For static inline helpers, it is best to identify the non-static callers.

Following these guidelines, my current list of FocusSymbols includes: `nl80211_tx_mgmt`, `nl80211_send_mgmt`, `ieee80211_mgmt_tx`, `ieee80211_tx_skb_tid`, `__ieee80211_tx_skb_tid_band`, `ieee80211_rx_list`, `ieee80211_rx_napi`, `__ieee80211_rx_handle_packet`, `__ieee80211_rx_handle_8023`, and `ieee80211_rx_handlers`.

Finally, it's essential to define the correct configuration options. I've confirmed that `CFG80211`, `MAC80211`, and `MAC80211_HWSIM` are the most relevant. While other wireless drivers are important, it's `mac80211_hwsim`, the simulated hardware, that's crucial for tools like syzkaller, especially in a QEMU/GCE environment, as it simplifies testing.

I'll check for any additional configurations related to MLO or 802.11be features within mac80211.



46/2 2026/09/15 18:16 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "config.*MLD|config.*MLO",
  "PathPrefix": "net/mac80211/"
}

Results:
{
  "error": "no matches"
}

47/2 2026/09/15 18:16 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 89704
							output: 36
							thoughts: 59
						

Thoughts:
Okay, here's my take on those thoughts, framed as an expert's inner monologue:

**Checking MLO Integration within mac80211 for 802.11be Support**

Right, so MLO, Multiple Link Operation, I recall it's deeply integrated into the mac80211 framework as a core component when mac80211 itself is enabled. That makes sense, keeps things clean and avoids unnecessary layers. Now, with the emergence of 802.11be, I need to understand the relationship between MLO and the new standard. To get a good handle on its implementation details, my next move is to dive straight into `net/mac80211/Kconfig`. I need to figure out the configuration dependencies specifically in that file. I expect to be digging for any dependencies or configuration options that are introduced for or specifically related to 802.11be and by extension, MLO support. Are there new configuration symbols that enable or disable it? Are there dependencies on specific hardware capabilities, firmware versions, or other features? The `Kconfig` file will be the key to understanding the conditional compilation and overall configuration of the feature in the kernel.



48/2 2026/09/15 18:16 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "MLO|MLD",
  "PathPrefix": "net/mac80211/Kconfig"
}

Results:
{
  "error": "no matches"
}

49/2 2026/09/15 18:16 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 90071
							output: 448
							thoughts: 404
						

Thoughts:
**MLO/MLD Configuration in mac80211: A Straightforward Integration**

Okay, so no, I don't need to consider any special configurations for Multi-Link Operation (MLO) or Multi-Link Device (MLD) within mac80211. It's essentially baked into the core of mac80211. Let's make sure that's clear and the reasoning is sound...

Right, let's break this down further. The gist of it is this: the recent patches fundamentally reworked MLO RX/TX frame handling within the mac80211 stack, the nl80211 netlink interface, and even touched some driver-specific code, including the always-useful `mac80211_hwsim` virtual driver. They didn't just add a toggle. Instead, there's been substantial refactoring related to `struct ieee80211_rx_status`, specifically the removal of `link_valid`, and the updating of `ieee80211_rx_list` and `ieee80211_rx_napi` to pass the `struct ieee80211_link_sta *link_pubsta` parameter. Also, a deeper look at `__ieee80211_rx_handle_packet`, `__ieee80211_rx_handle_8023`, and `ieee80211_rx_handlers` shows extensive work on link station lookup, validation, frequency checks and reordering across links.

Furthermore, they also added `NL80211_ATTR_FRAME_NO_STA` support in `nl80211_tx_mgmt` and passed station information through the management TX paths, covering `ieee80211_mgmt_tx` and `ieee80211_tx_skb_tid`.

And what's relevant to me is that all this modification is occurring within the core networking code and user-accessible interfaces, like nl80211 and mac80211. And this is being actively fuzzed via `mac80211_hwsim` in standard virtualized QEMU environments, too. A good sign, suggesting this isn't some experimental branch.

Therefore, this confirms that MLO/MLD functionality is part of mac80211's core functionality.

Okay, that seems solid. Let's call `set-results`.



50/2 2026/09/15 18:16 tool set-results
0m Args:
{
  "EnableConfigs": [
    "CFG80211",
    "MAC80211",
    "MAC80211_HWSIM"
  ],
  "FocusSymbols": [
    "nl80211_tx_mgmt",
    "nl80211_send_mgmt",
    "ieee80211_mgmt_tx",
    "ieee80211_tx_skb_tid",
    "__ieee80211_tx_skb_tid_band",
    "ieee80211_rx_list",
    "ieee80211_rx_napi",
    "__ieee80211_rx_handle_packet",
    "__ieee80211_rx_handle_8023",
    "ieee80211_rx_handlers",
    "ieee80211_rx_data_set_link_sta"
  ],
  "Reasoning": "The patch series refactors Multi-Link Operation (MLO) frame transmission and reception in the core mac80211 networking stack and the nl80211 netlink interface, as well as the virtual mac80211_hwsim driver. Key modifications include removing link_valid from rx_status, passing link_sta instead of sta to RX functions, restructuring station and link lookups in __ieee80211_rx_handle_packet and __ieee80211_rx_handle_8023, updating cross-link A-MPDU reordering in ieee80211_rx_handlers, and introducing NL80211_ATTR_FRAME_NO_STA handling in nl80211_tx_mgmt. These code paths are fully reachable in virtualized QEMU environments using mac80211_hwsim and unprivileged user namespaces via nl80211 netlink commands.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "CFG80211",
    "MAC80211",
    "MAC80211_HWSIM"
  ],
  "FocusSymbols": [
    "nl80211_tx_mgmt",
    "nl80211_send_mgmt",
    "ieee80211_mgmt_tx",
    "ieee80211_tx_skb_tid",
    "__ieee80211_tx_skb_tid_band",
    "ieee80211_rx_list",
    "ieee80211_rx_napi",
    "__ieee80211_rx_handle_packet",
    "__ieee80211_rx_handle_8023",
    "ieee80211_rx_handlers",
    "ieee80211_rx_data_set_link_sta"
  ],
  "Reasoning": "The patch series refactors Multi-Link Operation (MLO) frame transmission and reception in the core mac80211 networking stack and the nl80211 netlink interface, as well as the virtual mac80211_hwsim driver. Key modifications include removing link_valid from rx_status, passing link_sta instead of sta to RX functions, restructuring station and link lookups in __ieee80211_rx_handle_packet and __ieee80211_rx_handle_8023, updating cross-link A-MPDU reordering in ieee80211_rx_handlers, and introducing NL80211_ATTR_FRAME_NO_STA handling in nl80211_tx_mgmt. These code paths are fully reachable in virtualized QEMU environments using mac80211_hwsim and unprivileged user namespaces via nl80211 netlink commands.",
  "WorthFuzzing": true
}

51/1 2026/09/15 18:16 action If
3m Args:
{
  "WorthFuzzing": true
}

Results:
null

52/2 2026/09/15 18:16 agent kmsan-evaluator
3m Model:
core

Args:
null

Results:
{
  "KMSANReasoning": "The patch series updates IEEE 802.11 Multi-Link Operation (MLO) handling across wireless drivers (ath11k, ath12k, iwlwifi, mt76, rtw89, mac80211_hwsim) and mac80211/cfg80211. Specifically:\n1. It replaces passing `struct ieee80211_sta *pubsta` to `ieee80211_rx_napi`/`ieee80211_rx_list` with `struct ieee80211_link_sta *link_pubsta`.\n2. It removes the bitfield `link_valid:1` from `struct ieee80211_rx_status`, keeping `link_id:4`, and determines link IDs internally based on frequency or resolved link stations.\n3. It introduces the netlink attribute `NL80211_ATTR_FRAME_NO_STA` and corresponding `bool no_sta` flags in `struct cfg80211_mgmt_tx_params` and `struct cfg80211_rx_info`.\n4. It refactors frame resolution in `__ieee80211_rx_handle_packet` and link handling across A-MPDU reorder and fast-rx.\n\nEvaluation regarding KMSAN vs. KASAN:\n- No uninitialized memory leaks to userspace: `NL80211_ATTR_FRAME_NO_STA` is a flag attribute (`NLA_FLAG`) added via `nla_put_flag()` which carries zero payload bytes. The instances of `struct cfg80211_rx_info` populated in mac80211 and cfg80211 use designated C initializers (`struct cfg80211_rx_info info = { ... }`), ensuring all fields and padding are zero-initialized.\n- No uninitialized variable or memory reads: `struct ieee80211_rx_data` is explicitly zeroed via `memset(\u0026rx, 0, sizeof(rx))` in `__ieee80211_rx_handle_8023` and `__ieee80211_rx_handle_packet`. In skb receive paths, `skb-\u003ecb` / `rx_status` is either zeroed upon allocation or explicitly cleared via `memset(status, 0, ...)` by drivers, and `status-\u003elink_id` is assigned before handlers run (`status-\u003elink_id = rx-\u003elink_id \u003c 0 ? 0 : rx-\u003elink_id`).\n- Potential issues introduced by this patch involve pointer resolution logic, RCU dereferencing (`rcu_dereference(sta-\u003elink[...])`), array bounds, or NULL pointer dereferencing, all of which fall squarely within standard bug detectors (KASAN, UBSAN, NULL checks, LOCKDEP).\n\nTherefore, there is no risk of uninitialized memory usage or kernel information leaks, and a dedicated KMSAN fuzzing session is not warranted.",
  "NeedsKMSAN": false
}

Instruction:
You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.

CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
  a comprehensive suite of debugging tools and sanitizers, including KASAN
  (out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
  (locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
  or page allocations) and kernel-to-user memory info-leaks.

Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.

Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.

Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
   attributes, ioctl output arguments, socket options, or BPF buffers) where fields
   or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
   or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
   could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
   uninitialized bytes of existing buffers.

Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
  dereferences, locking deadlocks, or use-after-free bugs (these are already caught
  by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
  or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.

Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 05665aae504027f5bfd53117ebba1243a0c0f83c
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Sep 15 18:14:34 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index 33425707c0842..fbb3d18f55ddd 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -2500,7 +2500,7 @@ static void ath11k_dp_rx_deliver_msdu(struct ath11k *ar, struct napi_struct *nap
 	    !(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED))
 		rx_status->flag |= RX_FLAG_8023;
 
-	ieee80211_rx_napi(ar->hw, pubsta, msdu, napi);
+	ieee80211_rx_napi(ar->hw, pubsta ? &pubsta->deflink : NULL, msdu, napi);
 }
 
 static bool ath11k_dp_rx_check_nwifi_hdr_len_valid(struct ath11k_base *ab,
diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c
index 7d5be77b081f8..1faf1896d0ab1 100644
--- a/drivers/net/wireless/ath/ath12k/dp_mon.c
+++ b/drivers/net/wireless/ath/ath12k/dp_mon.c
@@ -503,8 +503,6 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
 	struct ieee80211_rx_status *rx_status;
 	struct ieee80211_radiotap_he *he = NULL;
 
-	status->link_valid = 0;
-
 	if ((status->encoding == RX_ENC_HE) && !(status->flag & RX_FLAG_RADIOTAP_HE) &&
 	    !(status->flag & RX_FLAG_SKIP_MONITOR)) {
 		he = skb_push(msdu, sizeof(known));
diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c
index 8fa0e90b45316..41ac0a4072aeb 100644
--- a/drivers/net/wireless/ath/ath12k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath12k/dp_rx.c
@@ -1375,6 +1375,7 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
 	struct ath12k_dp *dp = dp_pdev->dp;
 	struct ieee80211_rx_status *rx_status;
 	struct ieee80211_sta *pubsta;
+	struct ieee80211_link_sta *link_pubsta = NULL;
 	struct ath12k_dp_peer *peer;
 	struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
 	struct ieee80211_rx_status *status = rx_info->rx_status;
@@ -1384,9 +1385,19 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
 
 	pubsta = peer ? peer->sta : NULL;
 
-	status->link_valid = 0;
-	if (pubsta && pubsta->valid_links)
-		ath12k_hw_set_rx_link_id(dp->hw_params, peer, rxcb, status);
+	if (pubsta) {
+		if (pubsta->valid_links) {
+			int link_id =
+				ath12k_hw_get_rx_link_id(dp->hw_params, peer,
+							 rxcb, status);
+
+			if (link_id >= 0)
+				link_pubsta =
+					rcu_dereference(pubsta->link[link_id]);
+		} else {
+			link_pubsta = &pubsta->deflink;
+		}
+	}
 
 	ath12k_dbg(dp->ab, ATH12K_DBG_DATA,
 		   "rx skb %p len %u peer %pM %d %s sn %u %s%s%s%s%s%s%s%s%s%s rate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n",
@@ -1422,7 +1433,8 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
 
 	/* TODO: trace rx packet */
 
-	ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), pubsta, msdu, napi);
+	ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), link_pubsta, msdu,
+			  napi);
 }
 EXPORT_SYMBOL(ath12k_dp_rx_deliver_msdu);
 
diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h
index 011bb22e21c7b..e790de5737db4 100644
--- a/drivers/net/wireless/ath/ath12k/hw.h
+++ b/drivers/net/wireless/ath/ath12k/hw.h
@@ -249,9 +249,9 @@ struct ath12k_hw_ops {
 	bool (*dp_srng_is_tx_comp_ring)(int ring_num);
 	bool (*is_frame_link_agnostic)(struct ath12k_link_vif *arvif,
 				       struct ieee80211_mgmt *mgmt);
-	void (*set_rx_link_id)(struct ath12k_dp_peer *dp_peer,
-			       struct ath12k_skb_rxcb *rxcb,
-			       struct ieee80211_rx_status *status);
+	int (*get_rx_link_id)(struct ath12k_dp_peer *dp_peer,
+			      struct ath12k_skb_rxcb *rxcb,
+			      struct ieee80211_rx_status *status);
 };
 
 static inline
@@ -282,13 +282,15 @@ static inline int ath12k_hw_mac_id_to_srng_id(const struct ath12k_hw_params *hw,
 	return 0;
 }
 
-static inline void ath12k_hw_set_rx_link_id(const struct ath12k_hw_params *hw,
-					    struct ath12k_dp_peer *dp_peer,
-					    struct ath12k_skb_rxcb *rxcb,
-					    struct ieee80211_rx_status *status)
+static inline int ath12k_hw_get_rx_link_id(const struct ath12k_hw_params *hw,
+					   struct ath12k_dp_peer *dp_peer,
+					   struct ath12k_skb_rxcb *rxcb,
+					   struct ieee80211_rx_status *status)
 {
-	if (hw->hw_ops->set_rx_link_id)
-		hw->hw_ops->set_rx_link_id(dp_peer, rxcb, status);
+	if (hw->hw_ops->get_rx_link_id)
+		return hw->hw_ops->get_rx_link_id(dp_peer, rxcb, status);
+
+	return -1;
 }
 
 struct ath12k_fw_ie {
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c
index 95d87dd67872f..8b722ee0ad8fa 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c
@@ -2317,17 +2317,16 @@ ath12k_wifi7_dp_rxdesc_mpdu_valid(struct ath12k_base *ab,
 	return tlv_tag == HAL_RX_MPDU_START;
 }
 
-void
-ath12k_wifi7_dp_rx_set_link_id_qcn9274(struct ath12k_dp_peer *dp_peer,
+int
+ath12k_wifi7_dp_rx_get_link_id_qcn9274(struct ath12k_dp_peer *dp_peer,
 				       struct ath12k_skb_rxcb *rxcb,
 				       struct ieee80211_rx_status *status)
 {
-	status->link_valid = 1;
-	status->link_id = dp_peer->hw_links[rxcb->hw_link_id];
+	return dp_peer->hw_links[rxcb->hw_link_id];
 }
 
-void
-ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,
+int
+ath12k_wifi7_dp_rx_get_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,
 				       struct ath12k_skb_rxcb *rxcb,
 				       struct ieee80211_rx_status *status)
 {
@@ -2341,10 +2340,9 @@ ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,
 	links_map = READ_ONCE(dp_peer->link_peers_map);
 	for_each_set_bit(i, &links_map, ATH12K_NUM_MAX_LINKS) {
 		link_peer = rcu_dereference(dp_peer->link_peers[i]);
-		if (link_peer && link_peer->peer_id == rxcb->peer_id) {
-			status->link_valid = 1;
-			status->link_id = link_peer->link_id;
-			return;
-		}
+		if (link_peer && link_peer->peer_id == rxcb->peer_id)
+			return link_peer->link_id;
 	}
+
+	return -1;
 }
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h
index 1d3a4788a2dd9..d68489ff7b037 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h
+++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h
@@ -57,10 +57,10 @@ ath12k_wifi7_dp_rxdesc_mpdu_valid(struct ath12k_base *ab,
 				  struct hal_rx_desc *rx_desc);
 int ath12k_wifi7_dp_rx_tid_delete_handler(struct ath12k_base *ab,
 					  struct ath12k_dp_rx_tid_rxq *rx_tid);
-void ath12k_wifi7_dp_rx_set_link_id_qcn9274(struct ath12k_dp_peer *dp_peer,
-					    struct ath12k_skb_rxcb *rxcb,
-					    struct ieee80211_rx_status *status);
-void ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,
-					    struct ath12k_skb_rxcb *rxcb,
-					    struct ieee80211_rx_status *status);
+int ath12k_wifi7_dp_rx_get_link_id_qcn9274(struct ath12k_dp_peer *dp_peer,
+					   struct ath12k_skb_rxcb *rxcb,
+					   struct ieee80211_rx_status *status);
+int ath12k_wifi7_dp_rx_get_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,
+					   struct ath12k_skb_rxcb *rxcb,
+					   struct ieee80211_rx_status *status);
 #endif
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c
index 7b86f324b2e68..4ded807763b61 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c
@@ -158,7 +158,7 @@ static const struct ath12k_hw_ops qcn9274_ops = {
 	.get_ring_selector = ath12k_wifi7_hw_get_ring_selector_qcn9274,
 	.dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_qcn9274,
 	.is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_qcn9274,
-	.set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_qcn9274,
+	.get_rx_link_id = ath12k_wifi7_dp_rx_get_link_id_qcn9274,
 };
 
 static const struct ath12k_hw_ops wcn7850_ops = {
@@ -169,7 +169,7 @@ static const struct ath12k_hw_ops wcn7850_ops = {
 	.get_ring_selector = ath12k_wifi7_hw_get_ring_selector_wcn7850,
 	.dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_wcn7850,
 	.is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_wcn7850,
-	.set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_wcn7850,
+	.get_rx_link_id = ath12k_wifi7_dp_rx_get_link_id_wcn7850,
 };
 
 static const struct ath12k_hw_ops qcc2072_ops = {
@@ -180,7 +180,7 @@ static const struct ath12k_hw_ops qcc2072_ops = {
 	.get_ring_selector = ath12k_wifi7_hw_get_ring_selector_wcn7850,
 	.dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_wcn7850,
 	.is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_wcn7850,
-	.set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_wcn7850,
+	.get_rx_link_id = ath12k_wifi7_dp_rx_get_link_id_wcn7850,
 };
 
 #define ATH12K_TX_RING_MASK_0 0x1
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index d5160af60e00d..7e3655966f02d 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -7271,7 +7271,6 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb)
 	struct ath12k_wmi_mgmt_rx_arg rx_ev = {};
 	struct ath12k *ar;
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-	struct ieee80211_sta *pubsta = NULL;
 	struct ath12k_dp_link_peer *peer;
 	struct ieee80211_hdr *hdr;
 	bool is_4addr_null_pkt;
@@ -7373,11 +7372,6 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb)
 			dev_kfree_skb(skb);
 			goto exit;
 		}
-		pubsta = peer->sta;
-		if (pubsta && pubsta->valid_links) {
-			status->link_valid = 1;
-			status->link_id = peer->link_id;
-		}
 		spin_unlock_bh(&dp->dp_lock);
 		goto send_rx;
 	}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/agg.c
index 96102d2af2cf9..c45c47337509e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/agg.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c
@@ -7,7 +7,8 @@
 #include "hcmd.h"
 
 static void
-iwl_mld_reorder_release_frames(struct iwl_mld *mld, struct ieee80211_sta *sta,
+iwl_mld_reorder_release_frames(struct iwl_mld *mld,
+			       struct ieee80211_link_sta *link_sta,
 			       struct napi_struct *napi,
 			       struct iwl_mld_baid_data *baid_data,
 			       struct iwl_mld_reorder_buffer *reorder_buf,
@@ -32,7 +33,7 @@ iwl_mld_reorder_release_frames(struct iwl_mld *mld, struct ieee80211_sta *sta,
 		while ((skb = __skb_dequeue(skb_list))) {
 			iwl_mld_pass_packet_to_mac80211(mld, napi, skb,
 							reorder_buf->queue,
-							sta);
+							link_sta);
 			reorder_buf->num_stored--;
 		}
 	}
@@ -74,7 +75,7 @@ static void iwl_mld_release_frames_from_notif(struct iwl_mld *mld,
 
 	reorder_buf = &ba_data->reorder_buf[queue];
 
-	iwl_mld_reorder_release_frames(mld, link_sta->sta, napi, ba_data,
+	iwl_mld_reorder_release_frames(mld, link_sta, napi, ba_data,
 				       reorder_buf, nssn);
 out_unlock:
 	rcu_read_unlock();
@@ -180,7 +181,7 @@ void iwl_mld_del_ba(struct iwl_mld *mld, int queue,
 	reorder_buf = &ba_data->reorder_buf[queue];
 
 	/* release all frames that are in the reorder buffer to the stack */
-	iwl_mld_reorder_release_frames(mld, link_sta->sta, NULL,
+	iwl_mld_reorder_release_frames(mld, link_sta, NULL,
 				       ba_data, reorder_buf,
 				       ieee80211_sn_add(reorder_buf->head_sn,
 							ba_data->buf_size));
@@ -193,7 +194,7 @@ void iwl_mld_del_ba(struct iwl_mld *mld, int queue,
  */
 enum iwl_mld_reorder_result
 iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
-		int queue, struct ieee80211_sta *sta,
+		int queue, struct ieee80211_link_sta *link_sta,
 		struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc)
 {
 	struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb);
@@ -228,12 +229,12 @@ iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
 		return IWL_MLD_PASS_SKB;
 
 	/* no sta yet */
-	if (IWL_FW_CHECK(mld, !sta,
+	if (IWL_FW_CHECK(mld, !link_sta,
 			 "Got valid BAID without a valid station assigned - %d\n",
 			 baid))
 		return IWL_MLD_PASS_SKB;
 
-	mld_sta = iwl_mld_sta_from_mac80211(sta);
+	mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
 
 	/* not a data packet */
 	if (!ieee80211_is_data_qos(hdr->frame_control) ||
@@ -324,7 +325,7 @@ iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
 	 * will be released when the frame release notification arrives.
 	 */
 	if (!amsdu || last_subframe)
-		iwl_mld_reorder_release_frames(mld, sta, napi, baid_data,
+		iwl_mld_reorder_release_frames(mld, link_sta, napi, baid_data,
 					       buffer, nssn);
 	else if (buffer->num_stored == 1)
 		buffer->head_sn = nssn;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.h b/drivers/net/wireless/intel/iwlwifi/mld/agg.h
index 651c80d1c7cda..c6cd5fa219bed 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/agg.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.h
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
 /*
- * Copyright (C) 2024 Intel Corporation
+ * Copyright (C) 2024, 2026 Intel Corporation
  */
 #ifndef __iwl_agg_h__
 #define __iwl_agg_h__
@@ -106,7 +106,7 @@ int iwl_mld_ampdu_rx_stop(struct iwl_mld *mld, struct ieee80211_sta *sta,
 
 enum iwl_mld_reorder_result
 iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
-		int queue, struct ieee80211_sta *sta,
+		int queue, struct ieee80211_link_sta *link_sta,
 		struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc);
 
 void iwl_mld_handle_frame_release_notif(struct iwl_mld *mld,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
index 269439d789f46..02bdf6ccbcdc9 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
@@ -47,7 +47,8 @@ iwl_mld_fill_phy_data_from_mpdu(struct iwl_mld *mld,
 }
 
 static inline int iwl_mld_check_pn(struct iwl_mld *mld, struct sk_buff *skb,
-				   int queue, struct ieee80211_sta *sta)
+				   int queue,
+				   struct ieee80211_link_sta *link_sta)
 {
 	struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb);
 	struct ieee80211_rx_status *stats = IEEE80211_SKB_RXCB(skb);
@@ -71,13 +72,13 @@ static inline int iwl_mld_check_pn(struct iwl_mld *mld, struct sk_buff *skb,
 		return 0;
 
 	/* if we are here - this for sure is either CCMP or GCMP */
-	if (!sta) {
+	if (!link_sta) {
 		IWL_DEBUG_DROP(mld,
 			       "expected hw-decrypted unicast frame for station\n");
 		return -1;
 	}
 
-	mld_sta = iwl_mld_sta_from_mac80211(sta);
+	mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
 
 	extiv = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control);
 	keyidx = extiv[3] >> 6;
@@ -119,17 +120,17 @@ static inline int iwl_mld_check_pn(struct iwl_mld *mld, struct sk_buff *skb,
 void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
 				     struct napi_struct *napi,
 				     struct sk_buff *skb, int queue,
-				     struct ieee80211_sta *sta)
+				     struct ieee80211_link_sta *link_sta)
 {
 	KUNIT_STATIC_STUB_REDIRECT(iwl_mld_pass_packet_to_mac80211,
-				   mld, napi, skb, queue, sta);
+				   mld, napi, skb, queue, link_sta);
 
-	if (unlikely(iwl_mld_check_pn(mld, skb, queue, sta))) {
+	if (unlikely(iwl_mld_check_pn(mld, skb, queue, link_sta))) {
 		kfree_skb(skb);
 		return;
 	}
 
-	ieee80211_rx_napi(mld->hw, sta, skb, napi);
+	ieee80211_rx_napi(mld->hw, link_sta, skb, napi);
 }
 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_pass_packet_to_mac80211);
 
@@ -1728,7 +1729,7 @@ static void iwl_mld_update_last_rx_timestamp(struct iwl_mld *mld, u8 baid)
  * Sets *drop to true if the packet should be dropped.
  * Returns the station if found, or NULL otherwise.
  */
-static struct ieee80211_sta *
+static struct ieee80211_link_sta *
 iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr,
 		    struct sk_buff *skb,
 		    const struct iwl_rx_mpdu_desc *mpdu_desc,
@@ -1764,11 +1765,6 @@ iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr,
 
 	rx_status = IEEE80211_SKB_RXCB(skb);
 
-	if (link_sta && sta->valid_links) {
-		rx_status->link_valid = true;
-		rx_status->link_id = link_sta->link_id;
-	}
-
 	/* fill checksum */
 	if (ieee80211_is_data(hdr->frame_control) &&
 	    pkt->len_n_flags & cpu_to_le32(FH_RSCSR_RPA_EN)) {
@@ -1803,10 +1799,10 @@ iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr,
 							    queue);
 	}
 
-	return sta;
+	return link_sta;
 }
 
-static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
+static int iwl_mld_rx_mgmt_prot(struct ieee80211_link_sta *link_sta,
 				struct ieee80211_hdr *hdr,
 				struct ieee80211_rx_status *rx_status,
 				u32 mpdu_status,
@@ -1820,7 +1816,6 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
 	struct ieee80211_key_conf *key;
 	const u8 *frame = (void *)hdr;
 	const u8 *mmie;
-	u8 link_id;
 
 	if ((mpdu_status & IWL_RX_MPDU_STATUS_SEC_MASK) ==
 	     IWL_RX_MPDU_STATUS_SEC_NONE)
@@ -1836,10 +1831,10 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
 	if (!ieee80211_is_beacon(hdr->frame_control))
 		return 0;
 
-	if (!sta)
+	if (!link_sta)
 		return -1;
 
-	mld_sta = iwl_mld_sta_from_mac80211(sta);
+	mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
 	mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif);
 
 	/* key mismatch - will also report !MIC_OK but we shouldn't count it */
@@ -1853,8 +1848,7 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
 		return 0;
 	}
 
-	link_id = rx_status->link_valid ? rx_status->link_id : 0;
-	link = rcu_dereference(mld_vif->link[link_id]);
+	link = rcu_dereference(mld_vif->link[link_sta->link_id]);
 	if (WARN_ON_ONCE(!link))
 		return -1;
 
@@ -1905,7 +1899,7 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
 }
 
 static int iwl_mld_rx_crypto(struct iwl_mld *mld,
-			     struct ieee80211_sta *sta,
+			     struct ieee80211_link_sta *link_sta,
 			     struct ieee80211_hdr *hdr,
 			     struct ieee80211_rx_status *rx_status,
 			     struct iwl_rx_mpdu_desc *desc, int queue,
@@ -1915,7 +1909,7 @@ static int iwl_mld_rx_crypto(struct iwl_mld *mld,
 
 	if (unlikely(ieee80211_is_mgmt(hdr->frame_control) &&
 		     !ieee80211_has_protected(hdr->frame_control)))
-		return iwl_mld_rx_mgmt_prot(sta, hdr, rx_status, status,
+		return iwl_mld_rx_mgmt_prot(link_sta, hdr, rx_status, status,
 					    le16_to_cpu(desc->mpdu_len));
 
 	if (!ieee80211_has_protected(hdr->frame_control) ||
@@ -2023,7 +2017,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
 	struct iwl_mld_rx_phy_data phy_data = {};
 	struct iwl_rx_mpdu_desc *mpdu_desc = (void *)pkt->data;
-	struct ieee80211_sta *sta;
+	struct ieee80211_link_sta *link_sta;
 	struct ieee80211_hdr *hdr;
 	struct sk_buff *skb;
 	size_t mpdu_desc_size = sizeof(*mpdu_desc);
@@ -2086,7 +2080,8 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
 
 	rcu_read_lock();
 
-	sta = iwl_mld_rx_with_sta(mld, hdr, skb, mpdu_desc, pkt, queue, &drop);
+	link_sta = iwl_mld_rx_with_sta(mld, hdr, skb, mpdu_desc, pkt, queue,
+				       &drop);
 	if (drop)
 		goto drop;
 
@@ -2127,7 +2122,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
 
 	iwl_mld_rx_fill_status(mld, link_id, hdr, skb, &phy_data);
 
-	if (iwl_mld_rx_crypto(mld, sta, hdr, rx_status, mpdu_desc, queue,
+	if (iwl_mld_rx_crypto(mld, link_sta, hdr, rx_status, mpdu_desc, queue,
 			      le32_to_cpu(pkt->len_n_flags), &crypto_len))
 		goto drop;
 
@@ -2140,7 +2135,8 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
 	if (iwl_mld_time_sync_frame(mld, skb, hdr->addr2))
 		goto out;
 
-	reorder_res = iwl_mld_reorder(mld, napi, queue, sta, skb, mpdu_desc);
+	reorder_res = iwl_mld_reorder(mld, napi, queue, link_sta, skb,
+				      mpdu_desc);
 	switch (reorder_res) {
 	case IWL_MLD_PASS_SKB:
 		break;
@@ -2153,7 +2149,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
 		goto drop;
 	}
 
-	iwl_mld_pass_packet_to_mac80211(mld, napi, skb, queue, sta);
+	iwl_mld_pass_packet_to_mac80211(mld, napi, skb, queue, link_sta);
 
 	goto out;
 
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.h b/drivers/net/wireless/intel/iwlwifi/mld/rx.h
index 573b89c3c9c61..b08c5ade3db8f 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.h
@@ -69,7 +69,7 @@ void iwl_mld_handle_rsc_notif(struct iwl_mld *mld,
 void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
 				     struct napi_struct *napi,
 				     struct sk_buff *skb, int queue,
-				     struct ieee80211_sta *sta);
+				     struct ieee80211_link_sta *link_sta);
 
 void iwl_mld_handle_phy_air_sniffer_notif(struct iwl_mld *mld,
 					  struct napi_struct *napi,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c
index 29b0248cec3df..e9efe2996f071 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c
@@ -2,7 +2,7 @@
 /*
  * KUnit tests for channel helper functions
  *
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
  */
 #include <kunit/test.h>
 #include <kunit/static_stub.h>
@@ -441,7 +441,7 @@ static void
 fake_iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
 				     struct napi_struct *napi,
 				     struct sk_buff *skb, int queue,
-				     struct ieee80211_sta *sta)
+				     struct ieee80211_link_sta *link_sta)
 {
 	__skb_queue_tail(&g_released_skbs, skb);
 	g_num_released_skbs++;
@@ -630,7 +630,8 @@ static void test_reorder_buffer(struct kunit *test)
 	mpdu_desc = setup_mpdu_desc();
 
 	rcu_read_lock();
-	reorder_res = iwl_mld_reorder(mld, NULL, QUEUE, sta, skb, mpdu_desc);
+	reorder_res = iwl_mld_reorder(mld, NULL, QUEUE, &sta->deflink, skb,
+				      mpdu_desc);
 	rcu_read_unlock();
 
 	KUNIT_ASSERT_EQ(test, reorder_res, param->expected.reorder_res);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
index ab1eb2eb0c3cd..8c197a090c2b1 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
@@ -90,7 +90,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
 				fraglen, rxb->truesize);
 	}
 
-	ieee80211_rx_napi(mvm->hw, sta, skb, napi);
+	ieee80211_rx_napi(mvm->hw, sta ? &sta->deflink : NULL, skb, napi);
 }
 
 /*
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
index 7f0b4f5daa214..8c4009d3a00ca 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 /*
- * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2026 Intel Corporation
  * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
  * Copyright (C) 2015-2017 Intel Deutschland GmbH
  */
@@ -243,7 +243,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
 		return;
 	}
 
-	ieee80211_rx_napi(mvm->hw, sta, skb, napi);
+	ieee80211_rx_napi(mvm->hw, sta ? &sta->deflink : NULL, skb, napi);
 }
 
 static bool iwl_mvm_used_average_energy(struct iwl_mvm *mvm,
@@ -2528,7 +2528,7 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi,
 	}
 
 	rcu_read_lock();
-	ieee80211_rx_napi(mvm->hw, sta, skb, napi);
+	ieee80211_rx_napi(mvm->hw, sta ? &sta->deflink : NULL, skb, napi);
 	rcu_read_unlock();
 }
 
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index abbe65cbcd89e..cda5c5d51aab0 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -1255,11 +1255,12 @@ EXPORT_SYMBOL(mt76_rx_signal);
 static void
 mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
 		struct ieee80211_hw **hw,
-		struct ieee80211_sta **sta)
+		struct ieee80211_link_sta **link_sta)
 {
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);
 	struct mt76_rx_status mstat;
+	struct ieee80211_sta *sta;
 
 	mstat = *((struct mt76_rx_status *)skb->cb);
 	memset(status, 0, sizeof(*status));
@@ -1301,12 +1302,14 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
 	memcpy(status->chain_signal, mstat.chain_signal,
 	       sizeof(mstat.chain_signal));
 
-	if (mstat.wcid) {
-		status->link_valid = mstat.wcid->link_valid;
-		status->link_id = mstat.wcid->link_id;
-	}
+	sta = wcid_to_sta(mstat.wcid);
+	if (!sta)
+		*link_sta = NULL;
+	else if (mstat.wcid->link_valid)
+		*link_sta = rcu_dereference(sta->link[mstat.wcid->link_id]);
+	else
+		*link_sta = &sta->deflink;
 
-	*sta = wcid_to_sta(mstat.wcid);
 	*hw = mt76_phy_hw(dev, mstat.phy_idx);
 }
 
@@ -1530,7 +1533,7 @@ mt76_check_sta(struct mt76_dev *dev, struct sk_buff *skb)
 void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
 		      struct napi_struct *napi)
 {
-	struct ieee80211_sta *sta;
+	struct ieee80211_link_sta *link_sta;
 	struct ieee80211_hw *hw;
 	struct sk_buff *skb, *tmp;
 	LIST_HEAD(list);
@@ -1541,8 +1544,8 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
 
 		mt76_check_ccmp_pn(skb);
 		skb_shinfo(skb)->frag_list = NULL;
-		mt76_rx_convert(dev, skb, &hw, &sta);
-		ieee80211_rx_list(hw, sta, skb, &list);
+		mt76_rx_convert(dev, skb, &hw, &link_sta);
+		ieee80211_rx_list(hw, link_sta, skb, &list);
 
 		/* subsequent amsdu frames */
 		while (nskb) {
@@ -1550,8 +1553,8 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
 			nskb = nskb->next;
 			skb->next = NULL;
 
-			mt76_rx_convert(dev, skb, &hw, &sta);
-			ieee80211_rx_list(hw, sta, skb, &list);
+			mt76_rx_convert(dev, skb, &hw, &link_sta);
+			ieee80211_rx_list(hw, link_sta, skb, &list);
 		}
 	}
 	spin_unlock(&dev->rx_lock);
diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 397ebbfcac09a..454d876aeb886 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -3073,10 +3073,8 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,
 	struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
 	struct rtw89_rx_desc_info *desc_info = iter_data->desc_info;
 	struct sk_buff *skb = iter_data->skb;
-	struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct rtw89_rx_phy_ppdu *phy_ppdu = iter_data->phy_ppdu;
-	bool is_mld = ieee80211_vif_is_mld(vif);
 	struct ieee80211_bss_conf *bss_conf;
 	struct rtw89_vif_link *rtwvif_link;
 	const u8 *bssid = iter_data->bssid;
@@ -3110,11 +3108,6 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,
 	if (!ether_addr_equal(target_bssid, bssid))
 		goto out;
 
-	if (is_mld) {
-		rx_status->link_valid = true;
-		rx_status->link_id = rtwvif_link->link_id;
-	}
-
 	bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx);
 	pkt_stat = &bb->cur_pkt_stat;
 
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index 9f895ad718e9b..1a12261f3e8e5 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -1883,9 +1883,6 @@ static void mac80211_hwsim_rx(struct mac80211_hwsim_data *data,
 				sp->active_links_rx &= ~BIT(link_id);
 			else
 				sp->active_links_rx |= BIT(link_id);
-
-			rx_status->link_valid = true;
-			rx_status->link_id = link_id;
 		}
 		rcu_read_unlock();
 	}
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 6461a7bd0169d..9aa7d2d4a1841 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -4009,6 +4009,7 @@ struct cfg80211_update_ft_ies_params {
  * @link_id: for MLO, the link ID to transmit on, -1 if not given; note
  *	that the link ID isn't validated (much), it's in range but the
  *	link might not exist (or be used by the receiver STA)
+ * @no_sta: set if the frame should not be transmitted using an existing STA
  */
 struct cfg80211_mgmt_tx_params {
 	struct ieee80211_channel *chan;
@@ -4021,6 +4022,7 @@ struct cfg80211_mgmt_tx_params {
 	int n_csa_offsets;
 	const u16 *csa_offsets;
 	int link_id;
+	bool no_sta;
 };
 
 /**
@@ -9501,6 +9503,7 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
  * @flags: flags, as defined in &enum nl80211_rxmgmt_flags
  * @rx_tstamp: Hardware timestamp of frame RX in nanoseconds
  * @ack_tstamp: Hardware timestamp of ack TX in nanoseconds
+ * @no_sta: set if no station is known for the frame (relevant for MLD)
  */
 struct cfg80211_rx_info {
 	int freq;
@@ -9512,6 +9515,7 @@ struct cfg80211_rx_info {
 	u32 flags;
 	u64 rx_tstamp;
 	u64 ack_tstamp;
+	bool no_sta;
 };
 
 /**
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 7bb4618065b67..1172d7b83ed18 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1770,10 +1770,9 @@ enum mac80211_rx_encoding {
  * @ampdu_reference: A-MPDU reference number, must be a different value for
  *	each A-MPDU but the same for each subframe within one A-MPDU
  * @zero_length_psdu_type: radiotap type of the 0-length PSDU
- * @link_valid: if the link which is identified by @link_id is valid. This flag
- *	is set only when connection is MLO.
- * @link_id: id of the link used to receive the packet. This is used along with
- *	@link_valid.
+ * @link_id: id of the link used to receive the packet. Set and used by
+ *	mac80211 internally, it uses @freq set by the driver to identify the
+ *	correct link per vif.
  */
 struct ieee80211_rx_status {
 	u64 mactime;
@@ -1813,7 +1812,7 @@ struct ieee80211_rx_status {
 	u8 chains;
 	s8 chain_signal[IEEE80211_MAX_CHAINS];
 	u8 zero_length_psdu_type;
-	u8 link_valid:1, link_id:4;
+	u8 link_id:4;
 };
 
 static_assert(sizeof(struct ieee80211_rx_status) <= sizeof_field(struct sk_buff, cb));
@@ -5389,14 +5388,18 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw);
  * mixed for a single hardware. Must not run concurrently with
  * ieee80211_tx_status_skb() or ieee80211_tx_status_ni().
  *
+ * For data frames, when hardware has done address translation, a link station
+ * has to be provided and the frequency information may be skipped.
+ *
  * This function must be called with BHs disabled and RCU read lock
  *
  * @hw: the hardware this frame came in on
- * @sta: the station the frame was received from, or %NULL
+ * @link_sta: the link station the data frame was received from, or %NULL
  * @skb: the buffer to receive, owned by mac80211 after this call
  * @list: the destination list
  */
-void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+void ieee80211_rx_list(struct ieee80211_hw *hw,
+		       struct ieee80211_link_sta *link_sta,
 		       struct sk_buff *skb, struct list_head *list);
 
 /**
@@ -5414,14 +5417,18 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
  * mixed for a single hardware. Must not run concurrently with
  * ieee80211_tx_status_skb() or ieee80211_tx_status_ni().
  *
+ * For data frames, when hardware has done address translation, a link station
+ * has to be provided and the frequency information may be skipped.
+ *
  * This function must be called with BHs disabled.
  *
  * @hw: the hardware this frame came in on
- * @sta: the station the frame was received from, or %NULL
+ * @link_sta: the link station the data frame was received from, or %NULL
  * @skb: the buffer to receive, owned by mac80211 after this call
  * @napi: the NAPI context
  */
-void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+void ieee80211_rx_napi(struct ieee80211_hw *hw,
+		       struct ieee80211_link_sta *link_sta,
 		       struct sk_buff *skb, struct napi_struct *napi);
 
 /**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 32553cacf9889..510102febc269 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -3185,6 +3185,11 @@ enum nl80211_commands {
  *	The aggregated message always precedes the per-link messages for the
  *	same station within a dump sequence.
  *
+ * @NL80211_ATTR_FRAME_NO_STA: Valid for @NL80211_CMD_FRAME to denote that
+ *	the kernel had no station for a received frame or should not use a
+ *	known station to transmit a frame. This is relevant to know whether
+ *	MLD address translation happened or to disable it when sending a frame.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3785,6 +3790,8 @@ enum nl80211_attrs {
 
 	NL80211_ATTR_STA_DUMP_LINK_STATS,
 
+	NL80211_ATTR_FRAME_NO_STA,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 0832213430f43..ce9fb19d8c18c 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -98,7 +98,7 @@ static void ieee80211_send_addba_request(struct sta_info *sta, u16 tid,
 	if (sta->sta.deflink.he_cap.has_he)
 		ieee80211_add_addbaext(skb, 0, agg_size);
 
-	ieee80211_tx_skb_tid(sdata, skb, tid, -1);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, tid, -1);
 }
 
 void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)
@@ -127,7 +127,7 @@ void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)
 
 	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
 					IEEE80211_TX_CTL_REQ_TX_STATUS;
-	ieee80211_tx_skb_tid(sdata, skb, tid, -1);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, tid, -1);
 }
 EXPORT_SYMBOL(ieee80211_send_bar);
 
diff --git a/net/mac80211/ap.c b/net/mac80211/ap.c
index e7ac99c5a22eb..fe255891c35f0 100644
--- a/net/mac80211/ap.c
+++ b/net/mac80211/ap.c
@@ -82,9 +82,6 @@ ieee80211_rx_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata,
 	if (!ift_ext_capa)
 		return;
 
-	if (!status->link_valid)
-		return;
-
 	sta = sta_info_get_bss(sdata, mgmt->sa);
 	if (!sta)
 		return;
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index e1e1b7f82f3fe..c5c2113fa4f79 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -545,7 +545,7 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
 	info->status_data = IEEE80211_STATUS_TYPE_SMPS |
 			    u16_encode_bits(status_link_id << 2 | smps,
 					    IEEE80211_STATUS_SUBDATA_MASK);
-	ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, 7, link_id);
 
 	return 0;
 }
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 65311340db406..9514f01778beb 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -2471,7 +2471,8 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
 		    struct sta_info *sta, struct sk_buff *skb);
 
 void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
-				 struct sk_buff *skb, int tid, int link_id,
+				 struct sk_buff *skb, struct sta_info *sta,
+				 int tid, int link_id,
 				 enum nl80211_band band);
 
 static inline bool ieee80211_require_encrypted_assoc(__le16 fc,
@@ -2486,18 +2487,20 @@ ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
 			  enum nl80211_band band)
 {
 	rcu_read_lock();
-	__ieee80211_tx_skb_tid_band(sdata, skb, tid, -1, band);
+	__ieee80211_tx_skb_tid_band(sdata, skb, ERR_PTR(-ENOENT),
+				    tid, -1, band);
 	rcu_read_unlock();
 }
 
 void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
-			  struct sk_buff *skb, int tid, int link_id);
+			  struct sk_buff *skb, struct sta_info *sta,
+			  int tid, int link_id);
 
 static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
 				    struct sk_buff *skb)
 {
 	/* Send all internal mgmt frames on VO. Accordingly set TID to 7. */
-	ieee80211_tx_skb_tid(sdata, skb, 7, -1);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, 7, -1);
 }
 
 /**
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 18e62c5ff0fd8..29d532183814d 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1674,11 +1674,8 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local,
 				break;
 
 			status = IEEE80211_SKB_RXCB(skb);
-			if (!status->link_valid)
-				link_sta = &sta->deflink;
-			else
-				link_sta = rcu_dereference_protected(sta->link[status->link_id],
-							lockdep_is_held(&local->hw.wiphy->mtx));
+			link_sta = wiphy_dereference(local->hw.wiphy,
+						     sta->link[status->link_id]);
 			if (link_sta)
 				ieee80211_ht_handle_chanwidth_notif(local, sdata, sta,
 								    link_sta, chanwidth,
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 47ec703f10949..189dd3467f764 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -11827,12 +11827,10 @@ void ieee80211_sta_rx_queued_frame(struct ieee80211_sub_if_data *sdata,
 	rx_status = (struct ieee80211_rx_status *) skb->cb;
 	fc = le16_to_cpu(mgmt->frame_control);
 
-	if (rx_status->link_valid) {
-		link = sdata_dereference(sdata->link[rx_status->link_id],
-					 sdata);
-		if (!link)
-			return;
-	}
+	link = sdata_dereference(sdata->link[rx_status->link_id],
+				 sdata);
+	if (!link)
+		return;
 
 	switch (fc & IEEE80211_FCTL_STYPE) {
 	case IEEE80211_STYPE_BEACON:
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 7acef80d5f1fb..ddd7a96a2f817 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -854,8 +854,10 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 			need_offchan = true;
 
 		rcu_read_lock();
-		sta = sta_info_get_bss(sdata, mgmt->da);
-		mlo_sta = sta && sta->sta.mlo;
+		if (!params->no_sta) {
+			sta = sta_info_get_bss(sdata, mgmt->da);
+			mlo_sta = sta && sta->sta.mlo;
+		}
 
 		if (!ieee80211_is_action(mgmt->frame_control) ||
 		    mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
@@ -884,7 +886,8 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 		     local->ops->remain_on_channel &&
 		     memcmp(sdata->vif.cfg.ap_addr, mgmt->bssid, ETH_ALEN))) {
 			need_offchan = true;
-		} else if (sdata->u.mgd.associated &&
+		} else if (!params->no_sta &&
+			   sdata->u.mgd.associated &&
 			   ether_addr_equal(sdata->vif.cfg.ap_addr, mgmt->da)) {
 			sta = sta_info_get_bss(sdata, mgmt->da);
 			mlo_sta = sta && sta->sta.mlo;
@@ -1022,7 +1025,9 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 	}
 
 	if (!need_offchan) {
-		ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
+		ieee80211_tx_skb_tid(sdata, skb,
+				     params->no_sta ? ERR_PTR(-ENOENT) : sta,
+				     7, link_id);
 		ret = 0;
 		goto out_unlock;
 	}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 31dc6f83781b1..d6755a4c7d3c9 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -222,43 +222,21 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
 }
 
 static void __ieee80211_queue_skb_to_iface(struct ieee80211_sub_if_data *sdata,
-					   int link_id,
-					   struct sta_info *sta,
+					   struct link_sta_info *link_sta,
 					   struct sk_buff *skb)
 {
-	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-
-	if (link_id >= 0) {
-		status->link_valid = 1;
-		status->link_id = link_id;
-	} else {
-		status->link_valid = 0;
-	}
-
 	skb_queue_tail(&sdata->skb_queue, skb);
 	wiphy_work_queue(sdata->local->hw.wiphy, &sdata->work);
-	if (sta) {
-		struct link_sta_info *link_sta_info;
-
-		if (link_id >= 0) {
-			link_sta_info = rcu_dereference(sta->link[link_id]);
-			if (!link_sta_info)
-				return;
-		} else {
-			link_sta_info = &sta->deflink;
-		}
-
-		link_sta_info->rx_stats.packets++;
-	}
+	if (link_sta)
+		link_sta->rx_stats.packets++;
 }
 
 static void ieee80211_queue_skb_to_iface(struct ieee80211_sub_if_data *sdata,
-					 int link_id,
-					 struct sta_info *sta,
+					 struct link_sta_info *link_sta,
 					 struct sk_buff *skb)
 {
 	skb->protocol = 0;
-	__ieee80211_queue_skb_to_iface(sdata, link_id, sta, skb);
+	__ieee80211_queue_skb_to_iface(sdata, link_sta, skb);
 }
 
 static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata,
@@ -301,7 +279,7 @@ static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata,
 	if (!skb)
 		return;
 
-	ieee80211_queue_skb_to_iface(sdata, -1, NULL, skb);
+	ieee80211_queue_skb_to_iface(sdata, NULL, skb);
 }
 
 /*
@@ -1500,7 +1478,7 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
 	/* if this mpdu is fragmented - terminate rx aggregation session */
 	sc = le16_to_cpu(hdr->seq_ctrl);
 	if (sc & IEEE80211_SCTL_FRAG) {
-		ieee80211_queue_skb_to_iface(rx->sdata, rx->link_id, NULL, skb);
+		ieee80211_queue_skb_to_iface(rx->sdata, NULL, skb);
 		return;
 	}
 
@@ -2524,7 +2502,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
 
  out:
 	ieee80211_led_rx(rx->local);
-	if (rx->sta)
+	if (rx->link_sta)
 		rx->link_sta->rx_stats.packets++;
 	return RX_CONTINUE;
 }
@@ -3335,8 +3313,8 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
 		    (tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
 		     tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
 			rx->skb->protocol = cpu_to_be16(ETH_P_TDLS);
-			__ieee80211_queue_skb_to_iface(sdata, rx->link_id,
-						       rx->sta, rx->skb);
+			__ieee80211_queue_skb_to_iface(sdata, rx->link_sta,
+						       rx->skb);
 			return RX_QUEUED;
 		}
 	}
@@ -3991,7 +3969,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
 	return RX_QUEUED;
 
  queue:
-	ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb);
+	ieee80211_queue_skb_to_iface(sdata, rx->link_sta, rx->skb);
 	return RX_QUEUED;
 }
 
@@ -4005,6 +3983,7 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
 		.len = rx->skb->len,
 		.link_id = rx->link_id,
 		.have_link_id = rx->link_id >= 0,
+		.no_sta = !rx->sta,
 	};
 
 	/* skip known-bad action frames and return them in the next handler */
@@ -4127,7 +4106,7 @@ ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx)
 					local->hw.offchannel_tx_hw_queue;
 		}
 
-		__ieee80211_tx_skb_tid_band(rx->sdata, nskb, 7, -1,
+		__ieee80211_tx_skb_tid_band(rx->sdata, nskb, rx->sta, 7, -1,
 					    status->band);
 	}
 
@@ -4147,7 +4126,7 @@ ieee80211_rx_h_ext(struct ieee80211_rx_data *rx)
 		return RX_DROP_U_UNEXPECTED_EXT_FRAME;
 
 	/* for now only beacons are ext, so queue them */
-	ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb);
+	ieee80211_queue_skb_to_iface(sdata, rx->link_sta, rx->skb);
 
 	return RX_QUEUED;
 }
@@ -4204,7 +4183,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
 		return RX_DROP_U_UNHANDLED_MGMT_STYPE;
 	}
 
-	ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb);
+	ieee80211_queue_skb_to_iface(sdata, rx->link_sta, rx->skb);
 
 	return RX_QUEUED;
 }
@@ -4248,12 +4227,28 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,
 	spin_lock_bh(&rx->local->rx_path_lock);
 
 	while ((skb = __skb_dequeue(frames))) {
+		struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+
 		/*
-		 * all the other fields are valid across frames
-		 * that belong to an aMPDU since they are on the
-		 * same TID from the same station
+		 * Most fields are valid across frames. However,
+		 * in the case of reordering frames may have arrived
+		 * on different links, so adjust the link_id, link
+		 * and link_sta accordingly.
 		 */
 		rx->skb = skb;
+		if (ieee80211_vif_is_mld(&rx->sdata->vif) &&
+		    unlikely(rx->link_id != status->link_id)) {
+			if (rx->sta) {
+				rx->link_sta =
+					rcu_dereference(rx->sta->link[rx->link_id]);
+
+				/* Link got disabled, just use deflink */
+				if (!rx->link_sta)
+					rx->link_sta = &rx->sta->deflink;
+			}
+			rx->link_id = status->link_id;
+			rx->link = rcu_dereference(rx->sdata->link[status->link_id]);
+		}
 
 		if (WARN_ON_ONCE(!rx->link)) {
 			res = RX_DROP_U_NO_LINK;
@@ -4323,7 +4318,34 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
 static bool
 ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id)
 {
-	return !!(sta->valid_links & BIT(link_id));
+	if (sta->mlo)
+		return sta->valid_links & BIT(link_id);
+
+	return sta->deflink.link_id == link_id;
+}
+
+static bool ieee80211_rx_data_set_link_sta(struct ieee80211_rx_data *rx,
+					   struct link_sta_info *link_sta)
+{
+	struct sta_info *sta;
+
+	if (WARN_ON_ONCE(!link_sta) ||
+	    !ieee80211_rx_is_valid_sta_link_id(&link_sta->sta->sta,
+					       link_sta->link_id))
+		return false;
+
+	sta = link_sta->sta;
+
+	rx->sta = sta;
+	rx->local = sta->sdata->local;
+	rx->link = rcu_dereference(sta->sdata->link[link_sta->link_id]);
+	if (!rx->link)
+		return false;
+
+	rx->link_id = rx->link->link_id;
+	rx->link_sta = link_sta;
+
+	return true;
 }
 
 static bool ieee80211_rx_data_set_link(struct ieee80211_rx_data *rx,
@@ -4360,11 +4382,13 @@ static bool ieee80211_rx_data_set_sta(struct ieee80211_rx_data *rx,
 
 	if (link_id < 0) {
 		if (ieee80211_vif_is_mld(&rx->sdata->vif) &&
-		    sta && !sta->sta.valid_links)
+		    sta && !sta->sta.valid_links) {
 			rx->link =
 				rcu_dereference(rx->sdata->link[sta->deflink.link_id]);
-		else
+			rx->link_sta = &sta->deflink;
+		} else {
 			rx->link = &rx->sdata->deflink;
+		}
 	} else if (!ieee80211_rx_data_set_link(rx, link_id)) {
 		return false;
 	}
@@ -4387,7 +4411,7 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
 	struct tid_ampdu_rx *tid_agg_rx;
 	int link_id = -1;
 
-	/* FIXME: statistics won't be right with this */
+	/* NOTE: the correct link STA for the frame is set later */
 	if (sta->sta.valid_links)
 		link_id = ffs(sta->sta.valid_links) - 1;
 
@@ -4914,20 +4938,14 @@ static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
 {
 	struct ieee80211_sta_rx_stats *stats;
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
-	struct sta_info *sta = rx->sta;
-	struct link_sta_info *link_sta;
+	struct link_sta_info *link_sta = rx->link_sta;
 	struct sk_buff *skb = rx->skb;
 	void *sa = skb->data + ETH_ALEN;
 	void *da = skb->data;
 
-	if (rx->link_id >= 0) {
-		link_sta = rcu_dereference(sta->link[rx->link_id]);
-		if (WARN_ON_ONCE(!link_sta)) {
-			dev_kfree_skb(rx->skb);
-			return;
-		}
-	} else {
-		link_sta = &sta->deflink;
+	if (WARN_ON_ONCE(!link_sta)) {
+		dev_kfree_skb(rx->skb);
+		return;
 	}
 
 	stats = &link_sta->rx_stats;
@@ -5154,6 +5172,9 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
 		goto drop;
 	}
 
+	status = IEEE80211_SKB_RXCB(skb);
+	status->link_id = rx->link_id < 0 ? 0 : rx->link_id;
+
 	ieee80211_rx_8023(rx, fast_rx, orig_len);
 
 	return true;
@@ -5178,6 +5199,7 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
 	struct ieee80211_hdr *hdr = (void *)skb->data;
 	struct link_sta_info *link_sta = rx->link_sta;
 	struct ieee80211_link_data *link = rx->link;
+	struct ieee80211_rx_status *status;
 
 	rx->skb = skb;
 
@@ -5221,6 +5243,9 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
 		hdr = (struct ieee80211_hdr *)rx->skb->data;
 	}
 
+	status = IEEE80211_SKB_RXCB(rx->skb);
+	status->link_id = rx->link_id < 0 ? 0 : rx->link_id;
+
 	if (ieee80211_is_s1g_beacon(hdr->frame_control)) {
 		ieee80211_invoke_rx_handlers(rx);
 		return true;
@@ -5256,22 +5281,19 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
 }
 
 static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,
-				       struct ieee80211_sta *pubsta,
+				       struct ieee80211_link_sta *link_pubsta,
 				       struct sk_buff *skb,
 				       struct list_head *list)
 {
 	struct ieee80211_local *local = hw_to_local(hw);
-	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_fast_rx *fast_rx;
 	struct ieee80211_rx_data rx;
 	struct sta_info *sta;
-	int link_id = -1;
 
 	memset(&rx, 0, sizeof(rx));
 	rx.skb = skb;
 	rx.local = local;
 	rx.list = list;
-	rx.link_id = -1;
 
 	I802_DEBUG_INC(local->dot11ReceivedFragmentCount);
 
@@ -5279,23 +5301,15 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,
 	if (skb->len < sizeof(struct ethhdr))
 		goto drop;
 
-	if (!pubsta)
+	if (!link_pubsta)
 		goto drop;
 
-	if (status->link_valid)
-		link_id = status->link_id;
-
-	/*
-	 * TODO: Should the frame be dropped if the right link_id is not
-	 * available? Or may be it is fine in the current form to proceed with
-	 * the frame processing because with frame being in 802.3 format,
-	 * link_id is used only for stats purpose and updating the stats on
-	 * the deflink is fine?
-	 */
-	sta = container_of(pubsta, struct sta_info, sta);
-	if (!ieee80211_rx_data_set_sta(&rx, sta, link_id))
+	sta = container_of(link_pubsta->sta, struct sta_info, sta);
+	if (!ieee80211_rx_data_set_sta(&rx, sta, link_pubsta->link_id))
 		goto drop;
 
+	rx.link_id = link_pubsta->link_id;
+
 	fast_rx = rcu_dereference(rx.sta->fast_rx);
 	if (!fast_rx)
 		goto drop;
@@ -5307,59 +5321,19 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,
 	dev_kfree_skb(skb);
 }
 
-static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx,
-				       struct sk_buff *skb, bool consume)
+static bool ieee80211_rx_valid_freq(int freq, struct ieee80211_link_data *link)
 {
-	struct link_sta_info *link_sta;
-	struct ieee80211_hdr *hdr = (void *)skb->data;
-	struct sta_info *sta;
-	int link_id = -1;
-
-	if (ieee80211_is_s1g_beacon(hdr->frame_control)) {
-		if (!ieee80211_rx_data_set_sta(rx, NULL, -1))
-			return false;
-
-		return ieee80211_prepare_and_rx_handle(rx, skb, consume);
-	}
+	struct ieee80211_chanctx_conf *conf;
 
-	/*
-	 * Look up link station first, in case there's a
-	 * chance that they might have a link address that
-	 * is identical to the MLD address, that way we'll
-	 * have the link information if needed.
-	 */
-	link_sta = link_sta_info_get_bss(rx->sdata, hdr->addr2);
-	if (link_sta) {
-		sta = link_sta->sta;
-		link_id = link_sta->link_id;
-	} else {
-		struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-
-		sta = sta_info_get_bss(rx->sdata, hdr->addr2);
-		if (status->link_valid) {
-			link_id = status->link_id;
-		} else if (ieee80211_vif_is_mld(&rx->sdata->vif) &&
-			   status->freq) {
-			struct ieee80211_link_data *link;
-			struct ieee80211_chanctx_conf *conf;
-
-			for_each_link_data_rcu(rx->sdata, link) {
-				conf = rcu_dereference(link->conf->chanctx_conf);
-				if (!conf || !conf->def.chan)
-					continue;
-
-				if (status->freq == conf->def.chan->center_freq) {
-					link_id = link->link_id;
-					break;
-				}
-			}
-		}
-	}
+	if (!freq || link->sdata->vif.type == NL80211_IFTYPE_NAN ||
+	    link->sdata->vif.type == NL80211_IFTYPE_NAN_DATA)
+		return true;
 
-	if (!ieee80211_rx_data_set_sta(rx, sta, link_id))
+	conf = rcu_dereference(link->conf->chanctx_conf);
+	if (!conf || !conf->def.chan)
 		return false;
 
-	return ieee80211_prepare_and_rx_handle(rx, skb, consume);
+	return freq == conf->def.chan->center_freq;
 }
 
 /*
@@ -5367,18 +5341,20 @@ static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx,
  * be called with rcu_read_lock protection.
  */
 static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
-					 struct ieee80211_sta *pubsta,
+					 struct ieee80211_link_sta *link_pubsta,
 					 struct sk_buff *skb,
 					 struct list_head *list)
 {
 	struct ieee80211_local *local = hw_to_local(hw);
-	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_sub_if_data *sdata;
+	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_hdr *hdr;
+	struct link_sta_info *link_sta;
+	struct sta_info *sta;
 	__le16 fc;
 	struct ieee80211_rx_data rx;
-	struct ieee80211_sub_if_data *prev;
 	struct rhlist_head *tmp;
+	bool rx_data_pending;
 	int err = 0;
 
 	fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
@@ -5386,7 +5362,6 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 	rx.skb = skb;
 	rx.local = local;
 	rx.list = list;
-	rx.link_id = -1;
 
 	if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc))
 		I802_DEBUG_INC(local->dot11ReceivedFragmentCount);
@@ -5426,97 +5401,102 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 		     ieee80211_is_s1g_beacon(hdr->frame_control)))
 		ieee80211_scan_rx(local, skb);
 
+	/*
+	 * RX of a data frame should only happen to existing stations.
+	 * It is therefore more efficient to use the one provided by the driver
+	 * or search based on the station's address.
+	 *
+	 * Note we will fall through and handle a spurious data frame in the
+	 * same way as a management frame.
+	 */
 	if (ieee80211_is_data(fc)) {
-		struct sta_info *sta, *prev_sta;
-		int link_id = -1;
-
-		if (status->link_valid)
-			link_id = status->link_id;
-
-		if (pubsta) {
-			sta = container_of(pubsta, struct sta_info, sta);
-			if (!ieee80211_rx_data_set_sta(&rx, sta, link_id))
-				goto out;
-
-			/*
-			 * In MLO connection, fetch the link_id using addr2
-			 * when the driver does not pass link_id in status.
-			 * When the address translation is already performed by
-			 * driver/hw, the valid link_id must be passed in
-			 * status.
-			 */
-
-			if (!status->link_valid && pubsta->mlo) {
-				struct link_sta_info *link_sta;
-
-				link_sta = link_sta_info_get_bss(rx.sdata,
-								 hdr->addr2);
-				if (!link_sta)
-					goto out;
-
-				if (!ieee80211_rx_data_set_link(&rx,
-								link_sta->link_id))
-					goto out;
-			}
-
-			if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
+		if (link_pubsta) {
+			sta = container_of(link_pubsta->sta, struct sta_info,
+					   sta);
+			link_sta = rcu_dereference(sta->link[link_pubsta->link_id]);
+
+			rx.sdata = sta->sdata;
+			if (ieee80211_rx_data_set_link_sta(&rx, link_sta) &&
+			    ieee80211_prepare_and_rx_handle(&rx, skb, true))
 				return;
+
 			goto out;
 		}
 
-		prev_sta = NULL;
+		rx_data_pending = false;
 
+		/* Search for stations on non-MLD interfaces */
 		for_each_sta_info(local, hdr->addr2, sta, tmp) {
-			if (!prev_sta) {
-				prev_sta = sta;
-				continue;
-			}
+			struct ieee80211_link_data *link;
 
-			rx.sdata = prev_sta->sdata;
-			if (!status->link_valid && prev_sta->sta.mlo) {
-				struct link_sta_info *link_sta;
+			if (ieee80211_vif_is_mld(&sta->sdata->vif))
+				continue;
 
-				link_sta = link_sta_info_get_bss(rx.sdata,
-								 hdr->addr2);
-				if (!link_sta)
-					continue;
+			link = &sta->sdata->deflink;
+			if (!ieee80211_rx_valid_freq(status->freq, link))
+				continue;
 
-				link_id = link_sta->link_id;
+			if (rx_data_pending) {
+				ieee80211_prepare_and_rx_handle(&rx, skb,
+								false);
+				rx_data_pending = false;
 			}
 
-			if (!ieee80211_rx_data_set_sta(&rx, prev_sta, link_id))
-				goto out;
-
-			ieee80211_prepare_and_rx_handle(&rx, skb, false);
+			rx.sdata = sta->sdata;
+			if (!ieee80211_rx_data_set_link_sta(&rx, &sta->deflink))
+				continue;
 
-			prev_sta = sta;
+			rx_data_pending = true;
 		}
 
-		if (prev_sta) {
-			rx.sdata = prev_sta->sdata;
-			if (!status->link_valid && prev_sta->sta.mlo) {
-				struct link_sta_info *link_sta;
+		/* And search stations on MLD interfaces */
+		for_each_link_sta_info(local, hdr->addr2, link_sta, tmp) {
+			struct ieee80211_link_data *link;
 
-				link_sta = link_sta_info_get_bss(rx.sdata,
-								 hdr->addr2);
-				if (!link_sta)
-					goto out;
+			sta = link_sta->sta;
+			sdata =	sta->sdata;
+			link = rcu_dereference(sdata->link[link_sta->link_id]);
 
-				link_id = link_sta->link_id;
+			if (!ieee80211_rx_valid_freq(status->freq, link))
+				continue;
+
+			if (rx_data_pending) {
+				ieee80211_prepare_and_rx_handle(&rx, skb,
+								false);
+				rx_data_pending = false;
 			}
 
-			if (!ieee80211_rx_data_set_sta(&rx, prev_sta, link_id))
-				goto out;
+			rx.sdata = sta->sdata;
+			if (!ieee80211_rx_data_set_link_sta(&rx, link_sta))
+				continue;
+
+			rx_data_pending = true;
+		}
 
+		if (rx_data_pending) {
 			if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
 				return;
+
 			goto out;
 		}
+
+		/* fall through, e.g. for spurious frame notification */
 	}
 
-	prev = NULL;
+	/*
+	 * This is a management frame (or a data frame without a station) and
+	 * it will be delivered independent of whether a station exists,
+	 * so iterate the interfaces.
+	 */
+	rx_data_pending = false;
+
+	/* We expect the driver to provide frequency information */
+	if (WARN_ON_ONCE(!local->emulate_chanctx && !status->freq))
+		goto out;
 
 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+		struct ieee80211_link_data *link = NULL;
+
 		if (!ieee80211_sdata_running(sdata))
 			continue;
 
@@ -5524,30 +5504,85 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 		    sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
 			continue;
 
+		link_sta = NULL;
+		link = NULL;
+
+		/* Try to resolve the station */
+		if (!ieee80211_vif_is_mld(&sdata->vif)) {
+			sta = sta_info_get_bss(sdata, hdr->addr2);
+
+			if (sta) {
+				link_sta = &sta->deflink;
+				link = &sdata->deflink;
+			}
+		} else {
+			link_sta = link_sta_info_get_bss(sdata, hdr->addr2);
+
+			if (link_sta)
+				link = rcu_dereference(sdata->link[link_sta->link_id]);
+		}
+
 		/*
-		 * frame is destined for this interface, but if it's
-		 * not also for the previous one we handle that after
-		 * the loop to avoid copying the SKB once too much
+		 * If we found a STA and it has a valid link information, then
+		 * RX using the station.
 		 */
+		if (link_sta && link &&
+		    ieee80211_rx_valid_freq(status->freq, link)) {
+			if (rx_data_pending) {
+				ieee80211_prepare_and_rx_handle(&rx, skb, false);
+				rx_data_pending = false;
+			}
+
+			/* No valid_links check as we need to RX beacons */
+
+			rx.sdata = sdata;
+			if (ieee80211_rx_data_set_link_sta(&rx, link_sta))
+				rx_data_pending = true;
 
-		if (!prev) {
-			prev = sdata;
 			continue;
 		}
 
-		rx.sdata = prev;
-		ieee80211_rx_for_interface(&rx, skb, false);
+		/* No station, try to resolve the link and RX */
+		if (ieee80211_vif_is_mld(&sdata->vif)) {
+			struct ieee80211_chanctx_conf *conf;
+			bool found = false;
 
-		prev = sdata;
-	}
+			for_each_link_data_rcu(sdata, link) {
+				conf = rcu_dereference(link->conf->chanctx_conf);
+				if (!conf || !conf->def.chan)
+					continue;
 
-	if (prev) {
-		rx.sdata = prev;
+				if (status->freq == conf->def.chan->center_freq) {
+					found = true;
+					break;
+				}
+			}
 
-		if (ieee80211_rx_for_interface(&rx, skb, true))
-			return;
+			if (!found)
+				link = &sdata->deflink;
+		} else {
+			link = &sdata->deflink;
+		}
+
+		if (rx_data_pending) {
+			ieee80211_prepare_and_rx_handle(&rx, skb, false);
+			rx_data_pending = false;
+		}
+
+		rx.sdata = sdata;
+		rx.local = sdata->local;
+		rx.link = link;
+		rx.link_id = link->link_id;
+		rx.sta = NULL;
+		rx.link_sta = NULL;
+
+		rx_data_pending = true;
 	}
 
+	if (rx_data_pending &&
+	    ieee80211_prepare_and_rx_handle(&rx, skb, true))
+		return;
+
  out:
 	dev_kfree_skb(skb);
 }
@@ -5556,7 +5591,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
  * This is the receive path handler. It is called by a low level driver when an
  * 802.11 MPDU is received from the hardware.
  */
-void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
+void ieee80211_rx_list(struct ieee80211_hw *hw,
+		       struct ieee80211_link_sta *link_pubsta,
 		       struct sk_buff *skb, struct list_head *list)
 {
 	struct ieee80211_local *local = hw_to_local(hw);
@@ -5694,9 +5730,6 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
 		}
 	}
 
-	if (WARN_ON_ONCE(status->link_id >= IEEE80211_LINK_UNSPECIFIED))
-		goto drop;
-
 	status->rx_flags = 0;
 
 	kcov_remote_start_common(skb_get_kcov_handle(skb));
@@ -5715,9 +5748,10 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
 			ieee80211_tpt_led_trig_rx(local, skb->len);
 
 		if (status->flag & RX_FLAG_8023)
-			__ieee80211_rx_handle_8023(hw, pubsta, skb, list);
+			__ieee80211_rx_handle_8023(hw, link_pubsta, skb, list);
 		else
-			__ieee80211_rx_handle_packet(hw, pubsta, skb, list);
+			__ieee80211_rx_handle_packet(hw, link_pubsta, skb,
+						     list);
 	}
 
 	kcov_remote_stop();
@@ -5727,7 +5761,8 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
 }
 EXPORT_SYMBOL(ieee80211_rx_list);
 
-void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
+void ieee80211_rx_napi(struct ieee80211_hw *hw,
+		       struct ieee80211_link_sta *link_pubsta,
 		       struct sk_buff *skb, struct napi_struct *napi)
 {
 	struct sk_buff *tmp;
@@ -5740,7 +5775,7 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
 	 * receive processing
 	 */
 	rcu_read_lock();
-	ieee80211_rx_list(hw, pubsta, skb, &list);
+	ieee80211_rx_list(hw, link_pubsta, skb, &list);
 	rcu_read_unlock();
 
 	if (!napi) {
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index eeff230bd909f..324c2b6d7ce1b 100644
--- 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-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
  */
 
 #include <linux/if_arp.h>
@@ -204,12 +204,10 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
 		 * an indication on which of the links the frame was received
 		 */
 		if (ieee80211_vif_is_mld(&scan_sdata->vif)) {
-			if (rx_status->link_valid) {
-				s8 link_id = rx_status->link_id;
+			s8 link_id = rx_status->link_id;
 
-				link_conf =
-					rcu_dereference(scan_sdata->vif.link_conf[link_id]);
-			}
+			link_conf =
+				rcu_dereference(scan_sdata->vif.link_conf[link_id]);
 		} else {
 			link_conf = &scan_sdata->vif.bss_conf;
 		}
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 4f4cae9fd274c..7ec33885a3e18 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -1066,7 +1066,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
 	}
 
 	if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
-		ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
+		ieee80211_tx_skb_tid(sdata, skb, sta, 7, link_id);
 		return 0;
 	}
 
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 06c50183f064d..73affc540f0c7 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -6448,7 +6448,8 @@ void ieee80211_unreserve_tid(struct ieee80211_sta *pubsta, u8 tid)
 EXPORT_SYMBOL(ieee80211_unreserve_tid);
 
 void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
-				 struct sk_buff *skb, int tid, int link_id,
+				 struct sk_buff *skb, struct sta_info *sta,
+				 int tid, int link_id,
 				 enum nl80211_band band)
 {
 	const struct ieee80211_hdr *hdr = (void *)skb->data;
@@ -6500,12 +6501,13 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
 	 */
 	local_bh_disable();
 	IEEE80211_SKB_CB(skb)->band = band;
-	ieee80211_xmit(sdata, NULL, skb);
+	ieee80211_xmit(sdata, sta, skb);
 	local_bh_enable();
 }
 
 void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
-			  struct sk_buff *skb, int tid, int link_id)
+			  struct sk_buff *skb, struct sta_info *sta,
+			  int tid, int link_id)
 {
 	struct ieee80211_chanctx_conf *chanctx_conf;
 	enum nl80211_band band;
@@ -6531,7 +6533,7 @@ void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
 		band = 0;
 	}
 
-	__ieee80211_tx_skb_tid_band(sdata, skb, tid, link_id, band);
+	__ieee80211_tx_skb_tid_band(sdata, skb, sta, tid, link_id, band);
 	rcu_read_unlock();
 }
 
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index b081eeaf70162..e77a4c81d0317 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1097,6 +1097,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
 	[NL80211_ATTR_NPCA_PUNCT_BITMAP] =
 		NLA_POLICY_FULL_RANGE(NLA_U32, &nl80211_punct_bitmap_range),
 	[NL80211_ATTR_STA_DUMP_LINK_STATS] = { .type = NLA_FLAG },
+	[NL80211_ATTR_FRAME_NO_STA] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -14873,6 +14874,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
 	    !(wdev->valid_links & BIT(params.link_id)))
 		return -EINVAL;
 
+	params.no_sta =
+		nla_get_flag(info->attrs[NL80211_ATTR_FRAME_NO_STA]);
+
 	params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
 	params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
 
@@ -22042,7 +22046,9 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
 	    (info->ack_tstamp && nla_put_u64_64bit(msg,
 						   NL80211_ATTR_TX_HW_TIMESTAMP,
 						   info->ack_tstamp,
-						   NL80211_ATTR_PAD)))
+						   NL80211_ATTR_PAD)) ||
+	    (info->no_sta &&
+	     nla_put_flag(msg, NL80211_ATTR_FRAME_NO_STA)))
 		goto nla_put_failure;
 
 	genlmsg_end(msg, hdr);

53/3 2026/09/15 18:16 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 33301
							output: 48
							thoughts: 2069
						

Thoughts:
**KMSAN Needs Analysis: Examining the Wi-Fi Driver Patch**

Alright, let's break this down. The question before us is whether this Wi-Fi driver patch warrants a dedicated KMSAN fuzzing session, in addition to our standard KASAN fuzzing on amd64.  We need to carefully assess whether this patch introduces any risk of uninitialized memory usage, particularly in areas that interact with userspace.

The established ruleset is clear: KMSAN is needed if the patch touches kernel structures sent to userspace where fields *might* be uninitialized or introduces control flow dependent on potentially uninitialized variables. We also look for the allocation of complex data structures where uninitialized fields could be read later. Conversely, we *don't* need KMSAN if the primary risks are traditional memory errors like out-of-bounds access, or if all structures are fully zeroed/initialized before use.

Now, let's dive into the patch's changes:

First, there's a parameter change in `ath11k/dp_rx.c` from `struct ieee80211_sta *sta` to `struct ieee80211_link_sta *link_sta`.  This seems to be a structural change, and we need to verify that `link_sta` is properly initialized before being passed.

We see that `status->link_valid` is removed from `struct ieee80211_rx_status` in `ath12k/dp_mon.c` and in several other places. This removal is crucial!  We must trace the usage of `status->link_valid` and its dependency on initialization.  We see that `status->link_id` is also frequently removed.

In `ath12k/dp_rx.c`, the logic handling `pubsta->valid_links` and retrieval of the `link_pubsta` is now more complex. We need to ensure that the code path that uses `link_pubsta` doesn't read any uninitialized memory.

The change from `set_rx_link_id` to `get_rx_link_id`, returning an `int` rather than modifying `status` fields, is also part of the removal. Again, we must trace any dependencies and make sure data is initialized before use.

The pervasive updates throughout `iwlwifi`, `mt76`, `rtw89`, and `mac80211_hwsim` primarily involve adapting to the `link_sta` structure and removing `link_valid/link_id` settings, which is good.  This suggests a shift in how link information is managed. We have to verify this across all relevant code paths.

Crucially, in `include/net/mac80211.h`, the `struct ieee80211_rx_status` structure is modified by removing the `link_valid` bitfield. This means all references to `link_valid` should now be eliminated, and any calculations based on `link_valid` should be removed.

The addition of `bool no_sta` to `struct cfg80211_mgmt_tx_params` and `struct cfg80211_rx_info` and the corresponding `NL80211_ATTR_FRAME_NO_STA` attribute in netlink is interesting. It doesn't *seem* inherently risky, as it's a flag, but we need to see how this flag is used, if it is not properly set to 0, if the structure's `info` is not fully initialized. We have to ensure that `nl80211_send_mgmt` doesn't leak uninitialized memory through this new attribute. We have to verify how `info->no_sta` and `params.no_sta` are set and initialized.

After looking at the source code, at `net/mac80211/rx.c`, at line 4005, we see the `struct cfg80211_rx_info info = { ... };` is designated-initialized. Thus we have good initialization to userspace.

Given this analysis, and considering that the most common operations that send data to user space are designated-initialized, it seems unlikely that this patch *introduces* a significant risk of uninitialized memory usage that wouldn't be caught by KASAN. We are looking primarily for info leaks here, so we have to continue looking at changes to the data structures.

Therefore, for this patch, I believe that we **do not need** a dedicated KMSAN session.



54/3 2026/09/15 18:16 tool grepper
0m Args:
{
  "Expression": "ieee80211_rx_h_userspace_mgmt",
  "PathPrefix": "net/mac80211/rx.c"
}

Results:
{
  "Output": "net/mac80211/rx.c=3976=static ieee80211_rx_result debug_noinline\nnet/mac80211/rx.c:3977:ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)\nnet/mac80211/rx.c-3978-{\n--\nnet/mac80211/rx.c=4208=static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,\n--\nnet/mac80211/rx.c-4274-\t\tCALL_RXH(ieee80211_rx_h_action);\nnet/mac80211/rx.c:4275:\t\tCALL_RXH(ieee80211_rx_h_userspace_mgmt);\nnet/mac80211/rx.c-4276-\t\tCALL_RXH(ieee80211_rx_h_action_post_userspace);\n"
}

55/3 2026/09/15 18:16 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 36704
							output: 41
							thoughts: 30
						

Thoughts:
Here's a summary of my thought process, as if I were reading the provided text:

**Diving into the `net/mac80211/rx.c` Code**

Alright, let's get down to brass tacks. I need to understand the details of the receive path in mac80211, and the best way to do that is to dive into the code itself. I'm focusing on lines 3977 through 4020 of `net/mac80211/rx.c` -- I've got a specific question or issue in mind that I'm hoping these lines will illuminate. I'm expecting this section to deal with... well, based on the context of `rx.c`, it's likely involved in packet processing, potentially handling specific frame types or perhaps related to the handling of rate selection and error correction as the radio interface receives data, something like that. I'm going to look closely for any calls to other functions here, especially those related to frame parsing or hardware interaction. Let's see what we find. I'll be looking for clues on how the driver handles data, including things like checking the FCS and what it does after a packet's been determined to be valid. And then of course, how it interacts with the upper layers of the network stack. I'm ready to dig in and deconstruct this segment and work through it line by line. Let's start.



56/3 2026/09/15 18:16 tool read-file
0m Args:
{
  "File": "net/mac80211/rx.c",
  "FirstLine": 3977,
  "LineCount": 45
}

Results:
{
  "Contents": "3977:\tieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)\n3978:\t{\n3979:\t\tstruct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx-\u003eskb);\n3980:\t\tstruct cfg80211_rx_info info = {\n3981:\t\t\t.freq = ieee80211_rx_status_to_khz(status),\n3982:\t\t\t.buf = rx-\u003eskb-\u003edata,\n3983:\t\t\t.len = rx-\u003eskb-\u003elen,\n3984:\t\t\t.link_id = rx-\u003elink_id,\n3985:\t\t\t.have_link_id = rx-\u003elink_id \u003e= 0,\n3986:\t\t\t.no_sta = !rx-\u003esta,\n3987:\t\t};\n3988:\t\n3989:\t\t/* skip known-bad action frames and return them in the next handler */\n3990:\t\tif (status-\u003erx_flags \u0026 IEEE80211_RX_MALFORMED_ACTION_FRM)\n3991:\t\t\treturn RX_CONTINUE;\n3992:\t\n3993:\t\t/*\n3994:\t\t * Getting here means the kernel doesn't know how to handle\n3995:\t\t * it, but maybe userspace does ... include returned frames\n3996:\t\t * so userspace can register for those to know whether ones\n3997:\t\t * it transmitted were processed or returned.\n3998:\t\t */\n3999:\t\n4000:\t\tif (ieee80211_hw_check(\u0026rx-\u003elocal-\u003ehw, SIGNAL_DBM) \u0026\u0026\n4001:\t\t    !(status-\u003eflag \u0026 RX_FLAG_NO_SIGNAL_VAL))\n4002:\t\t\tinfo.sig_dbm = status-\u003esignal;\n4003:\t\n4004:\t\tif (ieee80211_is_timing_measurement(rx-\u003eskb) ||\n4005:\t\t    ieee80211_is_ftm(rx-\u003eskb)) {\n4006:\t\t\tinfo.rx_tstamp = ktime_to_ns(skb_hwtstamps(rx-\u003eskb)-\u003ehwtstamp);\n4007:\t\t\tinfo.ack_tstamp = ktime_to_ns(status-\u003eack_tx_hwtstamp);\n4008:\t\t}\n4009:\t\n4010:\t\tif (cfg80211_rx_mgmt_ext(\u0026rx-\u003esdata-\u003ewdev, \u0026info)) {\n4011:\t\t\tif (rx-\u003esta)\n4012:\t\t\t\trx-\u003elink_sta-\u003erx_stats.packets++;\n4013:\t\t\tdev_kfree_skb(rx-\u003eskb);\n4014:\t\t\treturn RX_QUEUED;\n4015:\t\t}\n4016:\t\n4017:\t\treturn RX_CONTINUE;\n4018:\t}\n4019:\t\n4020:\tstatic ieee80211_rx_result debug_noinline\n4021:\tieee80211_rx_h_action_post_userspace(struct ieee80211_rx_data *rx)\n"
}

57/3 2026/09/15 18:16 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 37941
							output: 25
							thoughts: 51
						

Thoughts:
Okay, let's dive into this. I need to get a handle on the broader context surrounding `cfg80211_rx_info`. My initial thought is, besides this specific location, are there any *other* places where this structure is both initialized and, crucially, utilized within the kernel's codebase? It's not enough to just know where it's declared, I need to know the entire lifecycle.

So, the next logical step is to perform a thorough search. I'll execute a `grep` command, targeting `struct cfg80211_rx_info` directly within the kernel source tree. This should give me a comprehensive list of all instances, letting me quickly identify those initializations and usages that I'm looking for.



58/3 2026/09/15 18:16 tool grepper
0m Args:
{
  "Expression": "struct cfg80211_rx_info"
}

Results:
{
  "Output": "include/net/cfg80211.h=9489=void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,\n--\ninclude/net/cfg80211.h-9493-/**\ninclude/net/cfg80211.h:9494: * struct cfg80211_rx_info - received management frame info\ninclude/net/cfg80211.h-9495- *\n--\ninclude/net/cfg80211.h-9507- */\ninclude/net/cfg80211.h:9508:struct cfg80211_rx_info {\ninclude/net/cfg80211.h-9509-\tint freq;\n--\ninclude/net/cfg80211.h-9523- * @wdev: wireless device receiving the frame\ninclude/net/cfg80211.h:9524: * @info: RX info as defined in struct cfg80211_rx_info\ninclude/net/cfg80211.h-9525- *\n--\ninclude/net/cfg80211.h=9534=bool cfg80211_rx_mgmt_ext(struct wireless_dev *wdev,\ninclude/net/cfg80211.h:9535:\t\t\t  struct cfg80211_rx_info *info);\ninclude/net/cfg80211.h-9536-\n--\ninclude/net/cfg80211.h=9554=static inline bool cfg80211_rx_mgmt_khz(struct wireless_dev *wdev, int freq,\n--\ninclude/net/cfg80211.h-9557-{\ninclude/net/cfg80211.h:9558:\tstruct cfg80211_rx_info info = {\ninclude/net/cfg80211.h-9559-\t\t.freq = freq,\n--\ninclude/net/cfg80211.h=9586=static inline bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq,\n--\ninclude/net/cfg80211.h-9589-{\ninclude/net/cfg80211.h:9590:\tstruct cfg80211_rx_info info = {\ninclude/net/cfg80211.h-9591-\t\t.freq = MHZ_TO_KHZ(freq),\n--\nnet/mac80211/rx.c=3977=ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)\n--\nnet/mac80211/rx.c-3979-\tstruct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx-\u003eskb);\nnet/mac80211/rx.c:3980:\tstruct cfg80211_rx_info info = {\nnet/mac80211/rx.c-3981-\t\t.freq = ieee80211_rx_status_to_khz(status),\n--\nnet/wireless/mlme.c=1010=bool cfg80211_rx_mgmt_ext(struct wireless_dev *wdev,\nnet/wireless/mlme.c:1011:\t\t\t  struct cfg80211_rx_info *info)\nnet/wireless/mlme.c-1012-{\n--\nnet/wireless/nl80211.c=22010=int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,\nnet/wireless/nl80211.c-22011-\t\t      struct wireless_dev *wdev, u32 nlportid,\nnet/wireless/nl80211.c:22012:\t\t      struct cfg80211_rx_info *info, gfp_t gfp)\nnet/wireless/nl80211.c-22013-{\n--\nnet/wireless/nl80211.h=112=int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,\nnet/wireless/nl80211.h-113-\t\t      struct wireless_dev *wdev, u32 nlpid,\nnet/wireless/nl80211.h:114:\t\t      struct cfg80211_rx_info *info, gfp_t gfp);\nnet/wireless/nl80211.h-115-\n--\nnet/wireless/trace.h=3472=TRACE_EVENT(cfg80211_rx_mgmt,\nnet/wireless/trace.h:3473:\tTP_PROTO(struct wireless_dev *wdev, struct cfg80211_rx_info *info),\nnet/wireless/trace.h-3474-\tTP_ARGS(wdev, info),\n"
}

59/3 2026/09/15 18:16 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 39495
							output: 27
							thoughts: 212
						

Thoughts:
**Analyzing Management Frame Transmission in cfg80211**

Okay, so I'm looking at how management frames are handled, specifically in the `cfg80211` subsystem. I see a pattern in `cfg80211_rx_mgmt_khz` and `cfg80211_rx_mgmt`. These functions are using designated initializers, like `struct cfg80211_rx_info info = { .freq = ... }`, which is clever. This ensures that any fields *not* explicitly initialized are zeroed out. This is a good practice, preventing potential issues with uninitialized memory. That includes a field called `.no_sta`.

Now, moving over to `nl80211_send_mgmt`, I see that the code checks `info->no_sta` as a boolean. If it's true, which it *would* be if uninitialized, it calls `nla_put_flag(msg, NL80211_ATTR_FRAME_NO_STA)`. The good news is that `nla_put_flag` is a flag attribute, so there's no data copying or worries about uninitialized data there.

Hmm, I wonder about the transmission side... Let's check `cfg80211_mgmt_tx_params`. I need to figure out how *that* struct is allocated and used. Let me run a `grep` for `struct cfg80211_mgmt_tx_params` to understand the bigger picture. This will help me trace the data flow through the management frame transmission process.



60/3 2026/09/15 18:16 tool grepper
0m Args:
{
  "Expression": "struct cfg80211_mgmt_tx_params"
}

Results:
{
  "Output": "drivers/net/wireless/ath/ath6kl/cfg80211.c=3193=static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,\ndrivers/net/wireless/ath/ath6kl/cfg80211.c:3194:\t\t\t  struct cfg80211_mgmt_tx_params *params, u64 cookie)\ndrivers/net/wireless/ath/ath6kl/cfg80211.c-3195-{\n--\ndrivers/net/wireless/ath/wil6210/cfg80211.c=1433=int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,\ndrivers/net/wireless/ath/wil6210/cfg80211.c:1434:\t\t\t struct cfg80211_mgmt_tx_params *params,\ndrivers/net/wireless/ath/wil6210/cfg80211.c-1435-\t\t\t u64 cookie)\n--\ndrivers/net/wireless/ath/wil6210/debugfs.c=966=static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf,\n--\ndrivers/net/wireless/ath/wil6210/debugfs.c-971-\tstruct wireless_dev *wdev = wil-\u003emain_ndev-\u003eieee80211_ptr;\ndrivers/net/wireless/ath/wil6210/debugfs.c:972:\tstruct cfg80211_mgmt_tx_params params;\ndrivers/net/wireless/ath/wil6210/debugfs.c-973-\tint rc;\n--\ndrivers/net/wireless/ath/wil6210/wil6210.h=1318=int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,\ndrivers/net/wireless/ath/wil6210/wil6210.h:1319:\t\t\t struct cfg80211_mgmt_tx_params *params,\ndrivers/net/wireless/ath/wil6210/wil6210.h-1320-\t\t\t u64 cookie);\n--\ndrivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c=5588=brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,\ndrivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c:5589:\t\t       struct cfg80211_mgmt_tx_params *params, u64 cookie)\ndrivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c-5590-{\n--\ndrivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h=502=int brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,\ndrivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h:503:\t\t\t   struct cfg80211_mgmt_tx_params *params, u64 cookie);\ndrivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h-504-\n--\ndrivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c=102=int brcmf_cyw_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,\ndrivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c:103:\t\t      struct cfg80211_mgmt_tx_params *params, u64 cookie)\ndrivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c-104-{\n--\ndrivers/net/wireless/marvell/mwifiex/cfg80211.c=198=mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,\ndrivers/net/wireless/marvell/mwifiex/cfg80211.c:199:\t\t\t struct cfg80211_mgmt_tx_params *params, u64 cookie)\ndrivers/net/wireless/marvell/mwifiex/cfg80211.c-200-{\n--\ndrivers/net/wireless/microchip/wilc1000/cfg80211.c=1147=static int mgmt_tx(struct wiphy *wiphy,\ndrivers/net/wireless/microchip/wilc1000/cfg80211.c-1148-\t\t   struct wireless_dev *wdev,\ndrivers/net/wireless/microchip/wilc1000/cfg80211.c:1149:\t\t   struct cfg80211_mgmt_tx_params *params,\ndrivers/net/wireless/microchip/wilc1000/cfg80211.c-1150-\t\t   u64 cookie)\n--\ndrivers/net/wireless/nxp/nxpwifi/cfg80211.c=152=nxpwifi_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,\ndrivers/net/wireless/nxp/nxpwifi/cfg80211.c:153:\t\t\t struct cfg80211_mgmt_tx_params *params, u64 cookie)\ndrivers/net/wireless/nxp/nxpwifi/cfg80211.c-154-{\n--\ndrivers/net/wireless/quantenna/qtnfmac/cfg80211.c=448=qtnf_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,\ndrivers/net/wireless/quantenna/qtnfmac/cfg80211.c:449:\t     struct cfg80211_mgmt_tx_params *params, u64 cookie)\ndrivers/net/wireless/quantenna/qtnfmac/cfg80211.c-450-{\n--\ndrivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c=2519=static int cfg80211_rtw_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,\ndrivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c:2520:\t\t\t\tstruct cfg80211_mgmt_tx_params *params,\ndrivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c-2521-\t\t\t\tu64 cookie)\n--\ninclude/net/cfg80211.h=3989=struct cfg80211_update_ft_ies_params {\n--\ninclude/net/cfg80211.h-3995-/**\ninclude/net/cfg80211.h:3996: * struct cfg80211_mgmt_tx_params - mgmt tx parameters\ninclude/net/cfg80211.h-3997- *\n--\ninclude/net/cfg80211.h-4013- */\ninclude/net/cfg80211.h:4014:struct cfg80211_mgmt_tx_params {\ninclude/net/cfg80211.h-4015-\tstruct ieee80211_channel *chan;\n--\ninclude/net/cfg80211.h=5282=struct cfg80211_ops {\n--\ninclude/net/cfg80211.h-5468-\tint\t(*mgmt_tx)(struct wiphy *wiphy, struct wireless_dev *wdev,\ninclude/net/cfg80211.h:5469:\t\t\t   struct cfg80211_mgmt_tx_params *params,\ninclude/net/cfg80211.h-5470-\t\t\t   u64 cookie);\n--\nnet/mac80211/ieee80211_i.h=2167=int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,\nnet/mac80211/ieee80211_i.h:2168:\t\t      struct cfg80211_mgmt_tx_params *params, u64 cookie);\nnet/mac80211/ieee80211_i.h-2169-int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,\n--\nnet/mac80211/offchannel.c=810=int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,\nnet/mac80211/offchannel.c:811:\t\t      struct cfg80211_mgmt_tx_params *params, u64 cookie)\nnet/mac80211/offchannel.c-812-{\n--\nnet/wireless/core.h=402=int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,\nnet/wireless/core.h-403-\t\t\t  struct wireless_dev *wdev,\nnet/wireless/core.h:404:\t\t\t  struct cfg80211_mgmt_tx_params *params,\nnet/wireless/core.h-405-\t\t\t  u64 cookie);\n--\nnet/wireless/mlme.c=895=int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,\nnet/wireless/mlme.c-896-\t\t\t  struct wireless_dev *wdev,\nnet/wireless/mlme.c:897:\t\t\t  struct cfg80211_mgmt_tx_params *params, u64 cookie)\nnet/wireless/mlme.c-898-{\n--\nnet/wireless/nl80211.c=14778=static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)\n--\nnet/wireless/nl80211.c-14786-\tstruct sk_buff *msg = NULL;\nnet/wireless/nl80211.c:14787:\tstruct cfg80211_mgmt_tx_params params = {\nnet/wireless/nl80211.c-14788-\t\t.dont_wait_for_ack =\n--\nnet/wireless/rdev-ops.h=761=static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev,\nnet/wireless/rdev-ops.h-762-\t\t\t       struct wireless_dev *wdev,\nnet/wireless/rdev-ops.h:763:\t\t\t       struct cfg80211_mgmt_tx_params *params,\nnet/wireless/rdev-ops.h-764-\t\t\t       u64 cookie)\n--\nnet/wireless/trace.h=2224=TRACE_EVENT(rdev_mgmt_tx,\nnet/wireless/trace.h-2225-\tTP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,\nnet/wireless/trace.h:2226:\t\t struct cfg80211_mgmt_tx_params *params),\nnet/wireless/trace.h-2227-\tTP_ARGS(wiphy, wdev, params),\n"
}

61/3 2026/09/15 18:16 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 43131
							output: 46
							thoughts: 142
						

Thoughts:
**Diving into the Wi-Fi Kernel Code**

Okay, so I'm looking into this `nl80211_tx_mgmt` function within `net/wireless/nl80211.c`. My immediate focus is on how the transmission parameters are set up, specifically the `struct cfg80211_mgmt_tx_params params`. The initialization using a designated initializer seems pretty standard, but I'm zeroing in on this `params.no_sta` field. It looks like it's being set directly with `nla_get_flag(info->attrs[NL80211_ATTR_FRAME_NO_STA])`. That's where I need to pay attention because it seems it directly maps from a Netlink attribute.

Now, I'm curious if this behavior is consistent. I have a hunch, based on the information I have, that there might be something related to this in `drivers/net/wireless/ath/wil6210/debugfs.c` around line 972. I should probably check that next. I need to understand how this is used at the driver level.



62/3 2026/09/15 18:17 tool read-file
0m Args:
{
  "File": "drivers/net/wireless/ath/wil6210/debugfs.c",
  "FirstLine": 970,
  "LineCount": 40
}

Results:
{
  "Contents": " 970:\t\tstruct wiphy *wiphy = wil_to_wiphy(wil);\n 971:\t\tstruct wireless_dev *wdev = wil-\u003emain_ndev-\u003eieee80211_ptr;\n 972:\t\tstruct cfg80211_mgmt_tx_params params;\n 973:\t\tint rc;\n 974:\t\tvoid *frame;\n 975:\t\n 976:\t\tmemset(\u0026params, 0, sizeof(params));\n 977:\t\n 978:\t\tif (!len)\n 979:\t\t\treturn -EINVAL;\n 980:\t\n 981:\t\tframe = memdup_user(buf, len);\n 982:\t\tif (IS_ERR(frame))\n 983:\t\t\treturn PTR_ERR(frame);\n 984:\t\n 985:\t\tparams.buf = frame;\n 986:\t\tparams.len = len;\n 987:\t\n 988:\t\trc = wil_cfg80211_mgmt_tx(wiphy, wdev, \u0026params, 0);\n 989:\t\n 990:\t\tkfree(frame);\n 991:\t\twil_info(wil, \"-\u003e %d\\n\", rc);\n 992:\t\n 993:\t\treturn len;\n 994:\t}\n 995:\t\n 996:\tstatic const struct file_operations fops_txmgmt = {\n 997:\t\t.write = wil_write_file_txmgmt,\n 998:\t\t.open  = simple_open,\n 999:\t};\n1000:\t\n1001:\t/* Write WMI command (w/o mbox header) to this file to send it\n1002:\t * WMI starts from wil6210_mbox_hdr_wmi header\n1003:\t */\n1004:\tstatic ssize_t wil_write_file_wmi(struct file *file, const char __user *buf,\n1005:\t\t\t\t\t  size_t len, loff_t *ppos)\n1006:\t{\n1007:\t\tstruct wil6210_priv *wil = file-\u003eprivate_data;\n1008:\t\tstruct wil6210_vif *vif = ndev_to_vif(wil-\u003emain_ndev);\n1009:\t\tstruct wmi_cmd_hdr *wmi;\n"
}

63/3 2026/09/15 18:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 44201
							output: 32
							thoughts: 263
						

Thoughts:
**Analyzing the IEEE 802.11 RX Status Structure**

Okay, so first things first, `memset` is used on `params` to ensure it's initialized to zero. Got that. Now, I need to dig into this `struct ieee80211_rx_status`. Specifically, I'm trying to understand the relationship between `status->link_valid` and `status->link_id`. I'm going through `include/net/mac80211.h`, and, hold on... what's this? The `link_valid` bit is no longer present! It looks like it was removed, or changed. This is crucial; I need to figure out how `struct ieee80211_rx_status` is now initialized within the skb's control block. Typically, `IEEE80211_SKB_RXCB(skb)` points to `skb->cb`.

I need to understand how this change affects the processing of received packets. I need to understand how the system *assumes* it has a valid link or not. How are checks being done now? I think the best thing to do is to track down all accesses to `status->link_id` within the kernel. I need to see how the code is interpreting this data now that the `link_valid` bit is gone. This change could have significant implications for the link's validity checks, and I need to understand the new logic.



64/3 2026/09/15 18:17 tool grepper
0m Args:
{
  "Expression": "link_id",
  "PathPrefix": "net/mac80211/"
}

Results:
{
  "Output": "\nFull output is too long, showing 500 out of 4020 lines.\nUse more precise expression if possible.\n\nnet/mac80211/ap.c=51=ieee80211_rx_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata,\n--\nnet/mac80211/ap.c-62-\tstruct ieee80211_eml_params eml_params = {\nnet/mac80211/ap.c:63:\t\t.link_id = status-\u003elink_id,\nnet/mac80211/ap.c-64-\t\t.control = control,\n--\nnet/mac80211/ap.c=188=ieee80211_rx_uhr_link_reconfig_req(struct ieee80211_sub_if_data *sdata,\n--\nnet/mac80211/ap.c-225-\t\tu16 control;\nnet/mac80211/ap.c:226:\t\tu8 link_id;\nnet/mac80211/ap.c-227-\n--\nnet/mac80211/ap.c-235-\t\tcontrol = le16_to_cpu(prof-\u003econtrol);\nnet/mac80211/ap.c:236:\t\tlink_id = control \u0026 IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID;\nnet/mac80211/ap.c-237-\nnet/mac80211/ap.c:238:\t\tif (link_id \u003e= IEEE80211_MLD_MAX_NUM_LINKS)\nnet/mac80211/ap.c-239-\t\t\treturn;\nnet/mac80211/ap.c-240-\nnet/mac80211/ap.c:241:\t\tlink = sdata_dereference(sdata-\u003elink[link_id], sdata);\nnet/mac80211/ap.c-242-\t\tif (!link)\n--\nnet/mac80211/ap.c-251-\nnet/mac80211/ap.c:252:\t\tlink_sta = sdata_dereference(sta-\u003elink[link_id], sdata);\nnet/mac80211/ap.c-253-\t\tif (!link_sta)\n--\nnet/mac80211/ap.c=321=void ieee80211_uhr_disable_dbe_all_stas(struct ieee80211_link_data *link)\n--\nnet/mac80211/ap.c-326-\tstruct ieee80211_chanctx *chanctx;\nnet/mac80211/ap.c:327:\tint link_id = link-\u003elink_id;\nnet/mac80211/ap.c-328-\tstruct sta_info *sta;\n--\nnet/mac80211/ap.c-340-\nnet/mac80211/ap.c:341:\t\tlink_sta = sdata_dereference(sta-\u003elink[link_id], sdata);\nnet/mac80211/ap.c-342-\t\tif (!link_sta)\n--\nnet/mac80211/cfg.c=26=static struct ieee80211_link_data *\nnet/mac80211/cfg.c:27:ieee80211_link_or_deflink(struct ieee80211_sub_if_data *sdata, int link_id,\nnet/mac80211/cfg.c-28-\t\t\t  bool require_valid)\n--\nnet/mac80211/cfg.c-31-\nnet/mac80211/cfg.c:32:\tif (link_id \u003c 0) {\nnet/mac80211/cfg.c-33-\t\t/*\n--\nnet/mac80211/cfg.c-43-\nnet/mac80211/cfg.c:44:\tlink = sdata_dereference(sdata-\u003elink[link_id], sdata);\nnet/mac80211/cfg.c-45-\tif (!link)\n--\nnet/mac80211/cfg.c=161=static int ieee80211_set_ap_mbssid_options(struct ieee80211_sub_if_data *sdata,\n--\nnet/mac80211/cfg.c-188-\nnet/mac80211/cfg.c:189:\t\ttx_bss_conf = sdata_dereference(tx_sdata-\u003evif.link_conf[params-\u003etx_link_id],\nnet/mac80211/cfg.c-190-\t\t\t\t\t\tsdata);\n--\nnet/mac80211/cfg.c=613=static int ieee80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,\nnet/mac80211/cfg.c:614:\t\t\t     int link_id, u8 key_idx, bool pairwise,\nnet/mac80211/cfg.c-615-\t\t\t     const u8 *mac_addr, struct key_params *params)\n--\nnet/mac80211/cfg.c-618-\tstruct ieee80211_link_data *link =\nnet/mac80211/cfg.c:619:\t\tieee80211_link_or_deflink(sdata, link_id, false);\nnet/mac80211/cfg.c-620-\tstruct ieee80211_local *local = sdata-\u003elocal;\n--\nnet/mac80211/cfg.c-632-\nnet/mac80211/cfg.c:633:\tif (WARN_ON(pairwise \u0026\u0026 link_id \u003e= 0))\nnet/mac80211/cfg.c-634-\t\treturn -EINVAL;\n--\nnet/mac80211/cfg.c-643-\tcase WLAN_CIPHER_SUITE_WEP104:\nnet/mac80211/cfg.c:644:\t\tif (link_id \u003e= 0)\nnet/mac80211/cfg.c-645-\t\t\treturn -EINVAL;\n--\nnet/mac80211/cfg.c-659-\t\tkey-\u003econf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;\nnet/mac80211/cfg.c:660:\t\tkey-\u003econf.link_id = -1;\nnet/mac80211/cfg.c-661-\t} else {\nnet/mac80211/cfg.c:662:\t\tkey-\u003econf.link_id = link-\u003elink_id;\nnet/mac80211/cfg.c-663-\t}\n--\nnet/mac80211/cfg.c=738=static struct ieee80211_key *\nnet/mac80211/cfg.c:739:ieee80211_lookup_key(struct ieee80211_sub_if_data *sdata, int link_id,\nnet/mac80211/cfg.c-740-\t\t     u8 key_idx, bool pairwise, const u8 *mac_addr)\n--\nnet/mac80211/cfg.c-745-\nnet/mac80211/cfg.c:746:\tif (link_id \u003e= 0) {\nnet/mac80211/cfg.c:747:\t\tlink = sdata_dereference(sdata-\u003elink[link_id], sdata);\nnet/mac80211/cfg.c-748-\t\tif (!link)\n--\nnet/mac80211/cfg.c-759-\nnet/mac80211/cfg.c:760:\t\tif (link_id \u003e= 0) {\nnet/mac80211/cfg.c:761:\t\t\tlink_sta = rcu_dereference_check(sta-\u003elink[link_id],\nnet/mac80211/cfg.c-762-\t\t\t\t\t\t\t lockdep_is_held(\u0026local-\u003ehw.wiphy-\u003emtx));\n--\nnet/mac80211/cfg.c=797=static int ieee80211_del_key(struct wiphy *wiphy, struct wireless_dev *wdev,\nnet/mac80211/cfg.c:798:\t\t\t     int link_id, u8 key_idx, bool pairwise,\nnet/mac80211/cfg.c-799-\t\t\t     const u8 *mac_addr)\n--\nnet/mac80211/cfg.c-806-\nnet/mac80211/cfg.c:807:\tkey = ieee80211_lookup_key(sdata, link_id, key_idx, pairwise, mac_addr);\nnet/mac80211/cfg.c-808-\tif (!key)\n--\nnet/mac80211/cfg.c=816=static int ieee80211_get_key(struct wiphy *wiphy, struct wireless_dev *wdev,\nnet/mac80211/cfg.c:817:\t\t\t     int link_id, u8 key_idx, bool pairwise,\nnet/mac80211/cfg.c-818-\t\t\t     const u8 *mac_addr, void *cookie,\n--\nnet/mac80211/cfg.c-835-\nnet/mac80211/cfg.c:836:\tkey = ieee80211_lookup_key(sdata, link_id, key_idx, pairwise, mac_addr);\nnet/mac80211/cfg.c-837-\tif (!key)\n--\nnet/mac80211/cfg.c=918=static int ieee80211_config_default_key(struct wiphy *wiphy,\nnet/mac80211/cfg.c-919-\t\t\t\t\tstruct net_device *dev,\nnet/mac80211/cfg.c:920:\t\t\t\t\tint link_id, u8 key_idx, bool uni,\nnet/mac80211/cfg.c-921-\t\t\t\t\tbool multi)\n--\nnet/mac80211/cfg.c-924-\tstruct ieee80211_link_data *link =\nnet/mac80211/cfg.c:925:\t\tieee80211_link_or_deflink(sdata, link_id, false);\nnet/mac80211/cfg.c-926-\n--\nnet/mac80211/cfg.c=935=static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy,\nnet/mac80211/cfg.c-936-\t\t\t\t\t     struct wireless_dev *wdev,\nnet/mac80211/cfg.c:937:\t\t\t\t\t     int link_id, u8 key_idx)\nnet/mac80211/cfg.c-938-{\n--\nnet/mac80211/cfg.c-940-\tstruct ieee80211_link_data *link =\nnet/mac80211/cfg.c:941:\t\tieee80211_link_or_deflink(sdata, link_id, true);\nnet/mac80211/cfg.c-942-\n--\nnet/mac80211/cfg.c=951=static int ieee80211_config_default_beacon_key(struct wiphy *wiphy,\nnet/mac80211/cfg.c-952-\t\t\t\t\t       struct wireless_dev *wdev,\nnet/mac80211/cfg.c:953:\t\t\t\t\t       int link_id, u8 key_idx)\nnet/mac80211/cfg.c-954-{\n--\nnet/mac80211/cfg.c-956-\tstruct ieee80211_link_data *link =\nnet/mac80211/cfg.c:957:\t\tieee80211_link_or_deflink(sdata, link_id, true);\nnet/mac80211/cfg.c-958-\n--\nnet/mac80211/cfg.c=1607=static u8 ieee80211_num_beaconing_links(struct ieee80211_sub_if_data *sdata)\n--\nnet/mac80211/cfg.c-1609-\tstruct ieee80211_link_data *link;\nnet/mac80211/cfg.c:1610:\tu8 link_id, num = 0;\nnet/mac80211/cfg.c-1611-\n--\nnet/mac80211/cfg.c-1615-\nnet/mac80211/cfg.c:1616:\t/* non-MLO mode of operation also uses link_id 0 in sdata so it is\nnet/mac80211/cfg.c-1617-\t * safe to directly proceed with the below loop\nnet/mac80211/cfg.c-1618-\t */\nnet/mac80211/cfg.c:1619:\tfor (link_id = 0; link_id \u003c IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {\nnet/mac80211/cfg.c:1620:\t\tlink = sdata_dereference(sdata-\u003elink[link_id], sdata);\nnet/mac80211/cfg.c-1621-\t\tif (!link)\n--\nnet/mac80211/cfg.c=1631=static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,\n--\nnet/mac80211/cfg.c-1645-\tint prev_beacon_int;\nnet/mac80211/cfg.c:1646:\tunsigned int link_id = params-\u003ebeacon.link_id;\nnet/mac80211/cfg.c-1647-\tstruct ieee80211_link_data *link;\n--\nnet/mac80211/cfg.c-1656-\nnet/mac80211/cfg.c:1657:\tlink = sdata_dereference(sdata-\u003elink[link_id], sdata);\nnet/mac80211/cfg.c-1658-\tif (!link)\n--\nnet/mac80211/cfg.c=1937=static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,\n--\nnet/mac80211/cfg.c-1950-\nnet/mac80211/cfg.c:1951:\tlink = sdata_dereference(sdata-\u003elink[beacon-\u003elink_id], sdata);\nnet/mac80211/cfg.c-1952-\tif (!link)\n--\nnet/mac80211/cfg.c=2011=static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,\nnet/mac80211/cfg.c:2012:\t\t\t     unsigned int link_id)\nnet/mac80211/cfg.c-2013-{\n--\nnet/mac80211/cfg.c-2023-\tstruct ieee80211_link_data *link =\nnet/mac80211/cfg.c:2024:\t\tsdata_dereference(sdata-\u003elink[link_id], sdata);\nnet/mac80211/cfg.c-2025-\tstruct ieee80211_bss_conf *link_conf = link-\u003econf;\n--\nnet/mac80211/cfg.c-2091-\nnet/mac80211/cfg.c:2092:\t__sta_info_flush(sdata, true, link_id, NULL);\nnet/mac80211/cfg.c-2093-\n--\nnet/mac80211/cfg.c-2109-\nnet/mac80211/cfg.c:2110:\tif (sdata-\u003ewdev.links[link_id].cac_started) {\nnet/mac80211/cfg.c-2111-\t\tchandef = link_conf-\u003echanreq.oper;\n--\nnet/mac80211/cfg.c-2114-\t\t\t\t   NL80211_RADAR_CAC_ABORTED,\nnet/mac80211/cfg.c:2115:\t\t\t\t   GFP_KERNEL, link_id);\nnet/mac80211/cfg.c-2116-\t}\n--\nnet/mac80211/cfg.c=2261=static int sta_link_apply_parameters(struct ieee80211_local *local,\n--\nnet/mac80211/cfg.c-2267-\tstruct ieee80211_sub_if_data *sdata = sta-\u003esdata;\nnet/mac80211/cfg.c:2268:\tu32 link_id = params-\u003elink_id \u003c 0 ? 0 : params-\u003elink_id;\nnet/mac80211/cfg.c-2269-\tstruct ieee80211_link_data *link =\nnet/mac80211/cfg.c:2270:\t\tsdata_dereference(sdata-\u003elink[link_id], sdata);\nnet/mac80211/cfg.c-2271-\tstruct link_sta_info *link_sta =\nnet/mac80211/cfg.c:2272:\t\trcu_dereference_protected(sta-\u003elink[link_id],\nnet/mac80211/cfg.c-2273-\t\t\t\t\t  lockdep_is_held(\u0026local-\u003ehw.wiphy-\u003emtx));\n--\nnet/mac80211/cfg.c-2295-\tcase STA_LINK_MODE_STA_MODIFY:\nnet/mac80211/cfg.c:2296:\t\tif (params-\u003elink_id \u003e= 0)\nnet/mac80211/cfg.c-2297-\t\t\tbreak;\n--\nnet/mac80211/cfg.c=2634=static int ieee80211_add_station(struct wiphy *wiphy, struct wireless_dev *wdev,\n--\nnet/mac80211/cfg.c-2669-\t */\nnet/mac80211/cfg.c:2670:\tif (params-\u003elink_sta_params.link_id \u003e= 0)\nnet/mac80211/cfg.c-2671-\t\tsta = sta_info_alloc_with_link(sdata, mac,\nnet/mac80211/cfg.c:2672:\t\t\t\t\t       params-\u003elink_sta_params.link_id,\nnet/mac80211/cfg.c-2673-\t\t\t\t\t       params-\u003elink_sta_params.link_mac ?: mac,\n--\nnet/mac80211/cfg.c=2714=static int ieee80211_del_station(struct wiphy *wiphy, struct wireless_dev *wdev,\n--\nnet/mac80211/cfg.c-2723-\nnet/mac80211/cfg.c:2724:\tsta_info_flush(sdata, params-\u003elink_id);\nnet/mac80211/cfg.c-2725-\treturn 0;\n--\nnet/mac80211/cfg.c=2728=static int ieee80211_set_sta_4addr(struct ieee80211_local *local,\n--\nnet/mac80211/cfg.c-2737-\tunsigned long master_iter;\nnet/mac80211/cfg.c:2738:\tint link_id;\nnet/mac80211/cfg.c-2739-\tint err;\n--\nnet/mac80211/cfg.c-2765-\nnet/mac80211/cfg.c:2766:\t\tfor_each_set_bit(link_id, \u0026master_iter,\nnet/mac80211/cfg.c-2767-\t\t\t\t IEEE80211_MLD_MAX_NUM_LINKS) {\nnet/mac80211/cfg.c:2768:\t\t\tif (!(sta_links \u0026 BIT(link_id))) {\nnet/mac80211/cfg.c:2769:\t\t\t\teth_zero_addr(wdev-\u003elinks[link_id].addr);\nnet/mac80211/cfg.c-2770-\t\t\t} else {\nnet/mac80211/cfg.c-2771-\t\t\t\tlink_conf = wiphy_dereference(wiphy,\nnet/mac80211/cfg.c:2772:\t\t\t\t\t\t\t      vif-\u003elink_conf[link_id]);\nnet/mac80211/cfg.c-2773-\nnet/mac80211/cfg.c:2774:\t\t\t\tether_addr_copy(wdev-\u003elinks[link_id].addr,\nnet/mac80211/cfg.c-2775-\t\t\t\t\t\tlink_conf-\u003ebssid);\n--\nnet/mac80211/cfg.c=3340=static int ieee80211_change_bss(struct wiphy *wiphy,\n--\nnet/mac80211/cfg.c-3348-\nnet/mac80211/cfg.c:3349:\tlink = ieee80211_link_or_deflink(sdata, params-\u003elink_id, true);\nnet/mac80211/cfg.c-3350-\tif (IS_ERR(link))\n--\nnet/mac80211/cfg.c=3427=static int ieee80211_set_txq_params(struct wiphy *wiphy,\n--\nnet/mac80211/cfg.c-3433-\tstruct ieee80211_link_data *link =\nnet/mac80211/cfg.c:3434:\t\tieee80211_link_or_deflink(sdata, params-\u003elink_id, true);\nnet/mac80211/cfg.c-3435-\tstruct ieee80211_tx_queue_params p;\n--\nnet/mac80211/cfg.c=3708=static int ieee80211_set_tx_power(struct wiphy *wiphy,\n--\nnet/mac80211/cfg.c-3750-\nnet/mac80211/cfg.c:3751:\t\tfor (int link_id = 0;\nnet/mac80211/cfg.c:3752:\t\t     link_id \u003c ARRAY_SIZE(sdata-\u003elink);\nnet/mac80211/cfg.c:3753:\t\t     link_id++) {\nnet/mac80211/cfg.c-3754-\t\t\tstruct ieee80211_link_data *link =\nnet/mac80211/cfg.c:3755:\t\t\t\twiphy_dereference(wiphy, sdata-\u003elink[link_id]);\nnet/mac80211/cfg.c-3756-\n--\nnet/mac80211/cfg.c-3780-\nnet/mac80211/cfg.c:3781:\t\tfor (int link_id = 0;\nnet/mac80211/cfg.c:3782:\t\t     link_id \u003c ARRAY_SIZE(sdata-\u003elink);\nnet/mac80211/cfg.c:3783:\t\t     link_id++) {\nnet/mac80211/cfg.c-3784-\t\t\tstruct ieee80211_link_data *link =\nnet/mac80211/cfg.c:3785:\t\t\t\twiphy_dereference(wiphy, sdata-\u003elink[link_id]);\nnet/mac80211/cfg.c-3786-\n--\nnet/mac80211/cfg.c-3800-\nnet/mac80211/cfg.c:3801:\t\tfor (int link_id = 0;\nnet/mac80211/cfg.c:3802:\t\t     link_id \u003c ARRAY_SIZE(sdata-\u003elink);\nnet/mac80211/cfg.c:3803:\t\t     link_id++) {\nnet/mac80211/cfg.c-3804-\t\t\tstruct ieee80211_link_data *link =\nnet/mac80211/cfg.c:3805:\t\t\t\twiphy_dereference(wiphy, sdata-\u003elink[link_id]);\nnet/mac80211/cfg.c-3806-\n--\nnet/mac80211/cfg.c=3835=static int ieee80211_get_tx_power(struct wiphy *wiphy,\n--\nnet/mac80211/cfg.c-3837-\t\t\t\t  int radio_idx,\nnet/mac80211/cfg.c:3838:\t\t\t\t  unsigned int link_id,\nnet/mac80211/cfg.c-3839-\t\t\t\t  int *dbm)\n--\nnet/mac80211/cfg.c-3846-\t    (sdata-\u003eflags \u0026 IEEE80211_SDATA_IN_DRIVER))\nnet/mac80211/cfg.c:3847:\t\treturn drv_get_txpower(local, sdata, link_id, dbm);\nnet/mac80211/cfg.c-3848-\n--\nnet/mac80211/cfg.c-3851-\t} else {\nnet/mac80211/cfg.c:3852:\t\tlink_data = wiphy_dereference(wiphy, sdata-\u003elink[link_id]);\nnet/mac80211/cfg.c-3853-\n--\nnet/mac80211/cfg.c=3910=int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,\n--\nnet/mac80211/cfg.c-3924-\nnet/mac80211/cfg.c:3925:\tif (!ieee80211_vif_link_active(\u0026sdata-\u003evif, link-\u003elink_id))\nnet/mac80211/cfg.c-3926-\t\treturn 0;\n--\nnet/mac80211/cfg.c-3973-\t\t\t\t\t ieee80211_vif_is_mld(\u0026sdata-\u003evif) ?\nnet/mac80211/cfg.c:3974:\t\t\t\t\t link-\u003elink_id : -1);\nnet/mac80211/cfg.c-3975-\tif (err)\n--\nnet/mac80211/cfg.c=3983=static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,\n--\nnet/mac80211/cfg.c-3987-\tstruct ieee80211_local *local = wdev_priv(dev-\u003eieee80211_ptr);\nnet/mac80211/cfg.c:3988:\tunsigned int link_id;\nnet/mac80211/cfg.c-3989-\n--\nnet/mac80211/cfg.c-4003-\t/* no change, but if automatic follow powersave */\nnet/mac80211/cfg.c:4004:\tfor (link_id = 0; link_id \u003c ARRAY_SIZE(sdata-\u003elink); link_id++) {\nnet/mac80211/cfg.c-4005-\t\tstruct ieee80211_link_data *link;\nnet/mac80211/cfg.c-4006-\nnet/mac80211/cfg.c:4007:\t\tlink = sdata_dereference(sdata-\u003elink[link_id], sdata);\nnet/mac80211/cfg.c-4008-\n--\nnet/mac80211/cfg.c=4025=static void ieee80211_set_cqm_rssi_link(struct ieee80211_sub_if_data *sdata,\n--\nnet/mac80211/cfg.c-4047-\nnet/mac80211/cfg.c:4048:\tif (!ieee80211_vif_link_active(\u0026sdata-\u003evif, link-\u003elink_id))\nnet/mac80211/cfg.c-4049-\t\treturn;\n--\nnet/mac80211/cfg.c=4056=static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy,\n--\nnet/mac80211/cfg.c-4061-\tstruct ieee80211_vif *vif = \u0026sdata-\u003evif;\nnet/mac80211/cfg.c:4062:\tint link_id;\nnet/mac80211/cfg.c-4063-\n--\nnet/mac80211/cfg.c-4068-\t/* For MLD, handle CQM change on all the active links */\nnet/mac80211/cfg.c:4069:\tfor (link_id = 0; link_id \u003c IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {\nnet/mac80211/cfg.c-4070-\t\tstruct ieee80211_link_data *link =\nnet/mac80211/cfg.c:4071:\t\t\tsdata_dereference(sdata-\u003elink[link_id], sdata);\nnet/mac80211/cfg.c-4072-\n--\nnet/mac80211/cfg.c=4080=static int ieee80211_set_cqm_rssi_range_config(struct wiphy *wiphy,\n--\nnet/mac80211/cfg.c-4085-\tstruct ieee80211_vif *vif = \u0026sdata-\u003evif;\nnet/mac80211/cfg.c:4086:\tint link_id;\nnet/mac80211/cfg.c-4087-\n--\nnet/mac80211/cfg.c-4091-\t/* For MLD, handle CQM change on all the active links */\nnet/mac80211/cfg.c:4092:\tfor (link_id = 0; link_id \u003c IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {\nnet/mac80211/cfg.c-4093-\t\tstruct ieee80211_link_data *link =\nnet/mac80211/cfg.c:4094:\t\t\tsdata_dereference(sdata-\u003elink[link_id], sdata);\nnet/mac80211/cfg.c-4095-\n--\nnet/mac80211/cfg.c=4103=static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,\nnet/mac80211/cfg.c-4104-\t\t\t\t      struct net_device *dev,\nnet/mac80211/cfg.c:4105:\t\t\t\t      unsigned int link_id,\nnet/mac80211/cfg.c-4106-\t\t\t\t      const u8 *addr,\n--\nnet/mac80211/cfg.c=4210=static int ieee80211_start_radar_detection(struct wiphy *wiphy,\n--\nnet/mac80211/cfg.c-4212-\t\t\t\t\t   struct cfg80211_chan_def *chandef,\nnet/mac80211/cfg.c:4213:\t\t\t\t\t   u32 cac_time_ms, int link_id)\nnet/mac80211/cfg.c-4214-{\n--\nnet/mac80211/cfg.c-4225-\nnet/mac80211/cfg.c:4226:\tlink_data = sdata_dereference(sdata-\u003elink[link_id], sdata);\nnet/mac80211/cfg.c-4227-\tif (!link_data)\n--\nnet/mac80211/cfg.c=4245=static void ieee80211_end_cac(struct wiphy *wiphy,\nnet/mac80211/cfg.c:4246:\t\t\t      struct net_device *dev, unsigned int link_id)\nnet/mac80211/cfg.c-4247-{\n--\nnet/mac80211/cfg.c-4254-\tlist_for_each_entry(sdata, \u0026local-\u003einterfaces, list) {\nnet/mac80211/cfg.c:4255:\t\tlink_data = sdata_dereference(sdata-\u003elink[link_id], sdata);\nnet/mac80211/cfg.c-4256-\t\tif (!link_data)\n--\nnet/mac80211/cfg.c-4261-\nnet/mac80211/cfg.c:4262:\t\tif (sdata-\u003ewdev.links[link_id].cac_started) {\nnet/mac80211/cfg.c-4263-\t\t\tieee80211_link_release_channel(link_data);\nnet/mac80211/cfg.c:4264:\t\t\tsdata-\u003ewdev.links[link_id].cac_started = false;\nnet/mac80211/cfg.c-4265-\t\t}\n--\nnet/mac80211/cfg.c=4270=cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)\n--\nnet/mac80211/cfg.c-4374-\nnet/mac80211/cfg.c:4375:void ieee80211_csa_finish(struct ieee80211_vif *vif, unsigned int link_id)\nnet/mac80211/cfg.c-4376-{\n--\nnet/mac80211/cfg.c-4381-\nnet/mac80211/cfg.c:4382:\tif (WARN_ON(link_id \u003e= IEEE80211_MLD_MAX_NUM_LINKS))\nnet/mac80211/cfg.c-4383-\t\treturn;\n--\nnet/mac80211/cfg.c-4386-\nnet/mac80211/cfg.c:4387:\tlink_data = rcu_dereference(sdata-\u003elink[link_id]);\nnet/mac80211/cfg.c-4388-\tif (WARN_ON(!link_data)) {\n--\nnet/mac80211/cfg.c=4467=static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data)\n--\nnet/mac80211/cfg.c-4517-\tcfg80211_ch_switch_notify(sdata-\u003edev, \u0026link_data-\u003ecsa.chanreq.oper,\nnet/mac80211/cfg.c:4518:\t\t\t\t  link_data-\u003elink_id);\nnet/mac80211/cfg.c-4519-\n--\nnet/mac80211/cfg.c=4523=static void ieee80211_csa_finalize(struct ieee80211_link_data *link_data)\n--\nnet/mac80211/cfg.c-4525-\tstruct ieee80211_sub_if_data *sdata = link_data-\u003esdata;\nnet/mac80211/cfg.c:4526:\tint link_id = -1;\nnet/mac80211/cfg.c-4527-\n--\nnet/mac80211/cfg.c-4529-\t\tsdata_info(sdata, \"failed to finalize CSA on link %d, disconnecting\\n\",\nnet/mac80211/cfg.c:4530:\t\t\t   link_data-\u003elink_id);\nnet/mac80211/cfg.c-4531-\t\tif (sdata-\u003evif.type == NL80211_IFTYPE_AP ||\n--\nnet/mac80211/cfg.c-4533-\t\t\t/*\nnet/mac80211/cfg.c:4534:\t\t\t * link_id is expected only for AP/P2P_GO type\nnet/mac80211/cfg.c-4535-\t\t\t * currently\nnet/mac80211/cfg.c-4536-\t\t\t */\nnet/mac80211/cfg.c:4537:\t\t\tlink_id = link_data-\u003elink_id;\nnet/mac80211/cfg.c-4538-\nnet/mac80211/cfg.c-4539-\t\tcfg80211_stop_link(sdata-\u003elocal-\u003ehw.wiphy, \u0026sdata-\u003ewdev,\nnet/mac80211/cfg.c:4540:\t\t\t\t   link_id, GFP_KERNEL);\nnet/mac80211/cfg.c-4541-\t}\n--\nnet/mac80211/cfg.c=4694=static void ieee80211_color_change_abort(struct ieee80211_link_data *link)\n--\nnet/mac80211/cfg.c-4699-\nnet/mac80211/cfg.c:4700:\tcfg80211_color_change_aborted_notify(link-\u003esdata-\u003edev, link-\u003elink_id);\nnet/mac80211/cfg.c-4701-}\n--\nnet/mac80211/cfg.c=4704=__ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,\n--\nnet/mac80211/cfg.c-4713-\tstruct ieee80211_channel_switch ch_switch = {\nnet/mac80211/cfg.c:4714:\t\t.link_id = params-\u003elink_id,\nnet/mac80211/cfg.c-4715-\t};\n--\nnet/mac80211/cfg.c-4720-\tu64 changed = 0;\nnet/mac80211/cfg.c:4721:\tu8 link_id = params-\u003elink_id;\nnet/mac80211/cfg.c-4722-\tint err;\n--\nnet/mac80211/cfg.c-4728-\nnet/mac80211/cfg.c:4729:\tif (sdata-\u003ewdev.links[link_id].cac_started)\nnet/mac80211/cfg.c-4730-\t\treturn -EBUSY;\nnet/mac80211/cfg.c-4731-\nnet/mac80211/cfg.c:4732:\tif (WARN_ON(link_id \u003e= IEEE80211_MLD_MAX_NUM_LINKS))\nnet/mac80211/cfg.c-4733-\t\treturn -EINVAL;\nnet/mac80211/cfg.c-4734-\nnet/mac80211/cfg.c:4735:\tlink_data = wiphy_dereference(wiphy, sdata-\u003elink[link_id]);\nnet/mac80211/cfg.c-4736-\tif (!link_data)\n--\nnet/mac80211/cfg.c-4807-\tcfg80211_ch_switch_started_notify(sdata-\u003edev,\nnet/mac80211/cfg.c:4808:\t\t\t\t\t  \u0026link_data-\u003ecsa.chanreq.oper, link_id,\nnet/mac80211/cfg.c-4809-\t\t\t\t\t  params-\u003ecount, params-\u003eblock_tx);\n--\nnet/mac80211/cfg.c=4942=static int ieee80211_probe_peer(struct wiphy *wiphy, struct net_device *dev,\n--\nnet/mac80211/cfg.c-4957-\tconst u8 *src_addr;\nnet/mac80211/cfg.c:4958:\tint link_id;\nnet/mac80211/cfg.c-4959-\tint size;\n--\nnet/mac80211/cfg.c-4990-\t\t\t */\nnet/mac80211/cfg.c:4991:\t\t\tlink_id = sta-\u003edeflink.link_id;\nnet/mac80211/cfg.c-4992-\t\t\tconf = wiphy_dereference(local-\u003ehw.wiphy,\nnet/mac80211/cfg.c:4993:\t\t\t\t\t\t sdata-\u003evif.link_conf[link_id]);\nnet/mac80211/cfg.c-4994-\t\t\tif (!conf)\n--\nnet/mac80211/cfg.c-5001-\t\t\t */\nnet/mac80211/cfg.c:5002:\t\t\tlink_id = IEEE80211_LINK_UNSPECIFIED;\nnet/mac80211/cfg.c-5003-\t\t\tsrc_addr = sdata-\u003evif.addr;\n--\nnet/mac80211/cfg.c-5012-\t\tband = chanctx_conf-\u003edef.chan-\u003eband;\nnet/mac80211/cfg.c:5013:\t\tlink_id = 0;\nnet/mac80211/cfg.c-5014-\t\tsrc_addr = sdata-\u003evif.addr;\n--\nnet/mac80211/cfg.c-5042-\tinfo-\u003eband = band;\nnet/mac80211/cfg.c:5043:\tinfo-\u003econtrol.flags |= u32_encode_bits(link_id,\nnet/mac80211/cfg.c-5044-\t\t\t\t\t       IEEE80211_TX_CTRL_MLO_LINK);\n--\nnet/mac80211/cfg.c=5065=static int ieee80211_cfg_get_channel(struct wiphy *wiphy,\nnet/mac80211/cfg.c-5066-\t\t\t\t     struct wireless_dev *wdev,\nnet/mac80211/cfg.c:5067:\t\t\t\t     unsigned int link_id,\nnet/mac80211/cfg.c-5068-\t\t\t\t     struct cfg80211_chan_def *chandef)\n--\nnet/mac80211/cfg.c-5076-\trcu_read_lock();\nnet/mac80211/cfg.c:5077:\tlink = rcu_dereference(sdata-\u003elink[link_id]);\nnet/mac80211/cfg.c-5078-\tif (!link) {\n--\nnet/mac80211/cfg.c=5131=static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy,\nnet/mac80211/cfg.c-5132-\t\t\t\t      struct net_device *dev,\nnet/mac80211/cfg.c:5133:\t\t\t\t      unsigned int link_id,\nnet/mac80211/cfg.c-5134-\t\t\t\t      struct cfg80211_chan_def *chandef)\n--\nnet/mac80211/cfg.c-5144-\nnet/mac80211/cfg.c:5145:\tlink = sdata_dereference(sdata-\u003elink[link_id], sdata);\nnet/mac80211/cfg.c-5146-\n--\nnet/mac80211/cfg.c=5595=static int ieee80211_color_change_finalize(struct ieee80211_link_data *link)\n--\nnet/mac80211/cfg.c-5607-\tif (err) {\nnet/mac80211/cfg.c:5608:\t\tcfg80211_color_change_aborted_notify(sdata-\u003edev, link-\u003elink_id);\nnet/mac80211/cfg.c-5609-\t\treturn err;\n--\nnet/mac80211/cfg.c-5614-\t\t\t\t\t\t 1, changed);\nnet/mac80211/cfg.c:5615:\tcfg80211_color_change_notify(sdata-\u003edev, link-\u003elink_id);\nnet/mac80211/cfg.c-5616-\n--\nnet/mac80211/cfg.c=5642=void ieee80211_color_collision_detection_work(struct wiphy *wiphy,\n--\nnet/mac80211/cfg.c-5650-\tcfg80211_obss_color_collision_notify(sdata-\u003edev, link-\u003ecolor_bitmap,\nnet/mac80211/cfg.c:5651:\t\t\t\t\t     link-\u003elink_id);\nnet/mac80211/cfg.c-5652-}\nnet/mac80211/cfg.c-5653-\nnet/mac80211/cfg.c:5654:void ieee80211_color_change_finish(struct ieee80211_vif *vif, u8 link_id)\nnet/mac80211/cfg.c-5655-{\n--\nnet/mac80211/cfg.c-5658-\nnet/mac80211/cfg.c:5659:\tif (WARN_ON(link_id \u003e= IEEE80211_MLD_MAX_NUM_LINKS))\nnet/mac80211/cfg.c-5660-\t\treturn;\n--\nnet/mac80211/cfg.c-5663-\nnet/mac80211/cfg.c:5664:\tlink = rcu_dereference(sdata-\u003elink[link_id]);\nnet/mac80211/cfg.c-5665-\tif (WARN_ON(!link)) {\n\n"
}

65/3 2026/09/15 18:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 57240
							output: 43
							thoughts: 1827
						

Thoughts:
**Analyzing the mac80211 RX Path for Link ID Management**

Okay, let's dive into this `net/mac80211/rx.c` code. My initial thought is to trace the use of `status->link_id`. The patch I'm examining appears to be dealing with frame reordering and multiple links, particularly in the context of Multiple Link Device (MLD).

First, let's look at the critical section in `ieee80211_rx_handlers`. I see that the code now adjusts `rx->link_id`, `rx->link`, and `rx->link_sta` if reordering might have caused packets to arrive on different links. The important line is: `rx->link_sta = rcu_dereference(rx->sta->link[rx->link_id]);`. Wait a second... isn't `rx->link_id` used *before* it's updated with `status->link_id`? That's a potential logic bug or even KASAN vulnerability right there! The old `rx->link_id` is used, instead of the new one, at the moment, which could lead to incorrect link association.

Now, let's look at `ieee80211_invoke_fast_rx` and `ieee80211_prepare_and_rx_handle`. Aha! We're seeing that `status->link_id` gets set to `rx->link_id`, with a check for negative values (making sure to default to 0). Crucially, the comment in `include/net/mac80211.h` clarifies that `mac80211` is now responsible for internally managing `link_id`. Drivers previously set `status->link_valid` and `status->link_id`, but now mac80211 is taking over, using the frequency to determine the correct link.

Now, I need to verify how these changes are reflected in other parts of mac80211. Looking at `iface.c`, `mlme.c`, and `scan.c`, I notice that the code has been updated to directly dereference the `sta->link[status->link_id]` and `sdata->link[status->link_id]` arrays *without* checking `status->link_valid`. This is consistent with the `mac80211` managing the `link_id`. Also, there are similar removals in `ap.c`.

However, the question is, is `status->link_id` always initialized before it's used? I need to check how the drivers are setting up `IEEE80211_SKB_RXCB(skb)`. I have to confirm that `status` is initialized in the driver. For example, in mt76, I expect a `memset(status, 0, sizeof(*status));` call. I need to make sure this initialization is present and correct across all relevant drivers, including iwlwifi, by checking its `iwl_mld_rx_fill_status` function, or wherever the `rx_status` is prepared. This initialization is critical, because otherwise, `status->link_id` might be undefined, leading to unpredictable behavior and potential security issues. This whole thing smells like a careful refactoring of the RX path, with the goal of centralizing link ID management within the mac80211 core, but the initialization of `status->link_id` is vital, and needs review.



66/3 2026/09/15 18:17 tool grepper
0m Args:
{
  "Expression": "iwl_mld_rx_fill_status",
  "PathPrefix": "drivers/net/wireless/intel/iwlwifi/"
}

Results:
{
  "Output": "drivers/net/wireless/intel/iwlwifi/mld/rx.c=1397=static void iwl_mld_set_rx_rate(struct iwl_mld *mld,\n--\ndrivers/net/wireless/intel/iwlwifi/mld/rx.c-1493-/* Note: hdr can be NULL */\ndrivers/net/wireless/intel/iwlwifi/mld/rx.c:1494:static void iwl_mld_rx_fill_status(struct iwl_mld *mld, int link_id,\ndrivers/net/wireless/intel/iwlwifi/mld/rx.c-1495-\t\t\t\t   struct ieee80211_hdr *hdr,\n--\ndrivers/net/wireless/intel/iwlwifi/mld/rx.c=2014=void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,\n--\ndrivers/net/wireless/intel/iwlwifi/mld/rx.c-2122-\ndrivers/net/wireless/intel/iwlwifi/mld/rx.c:2123:\tiwl_mld_rx_fill_status(mld, link_id, hdr, skb, \u0026phy_data);\ndrivers/net/wireless/intel/iwlwifi/mld/rx.c-2124-\n--\ndrivers/net/wireless/intel/iwlwifi/mld/rx.c=2283=static void iwl_mld_no_data_rx(struct iwl_mld *mld,\n--\ndrivers/net/wireless/intel/iwlwifi/mld/rx.c-2340-\t/* link ID is ignored for NULL header */\ndrivers/net/wireless/intel/iwlwifi/mld/rx.c:2341:\tiwl_mld_rx_fill_status(mld, -1, NULL, skb, \u0026phy_data);\ndrivers/net/wireless/intel/iwlwifi/mld/rx.c-2342-\n"
}

67/3 2026/09/15 18:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 60396
							output: 47
							thoughts: 38
						

Thoughts:
Here's a summary of my thought process:

**Diving into the iwl_mld_rx_fill_status Function**

Okay, time to get to the core of this issue. My first move is to zero in on `iwl_mld_rx_fill_status` within `drivers/net/wireless/intel/iwlwifi/mld/rx.c`. This function is likely crucial to how the Intel wireless driver is handling and reporting the status of Multicast Listener Discovery (MLD) reception. Understanding this function is going to be key to grasping any potential problems with MLD handling on this system. I want to see what this function does with the RX information, how it interprets the status codes, and if there are any obvious bottlenecks or potential areas for error. Let's see what's what!



68/3 2026/09/15 18:17 tool read-file
0m Args:
{
  "File": "drivers/net/wireless/intel/iwlwifi/mld/rx.c",
  "FirstLine": 1494,
  "LineCount": 40
}

Results:
{
  "Contents": "1494:\tstatic void iwl_mld_rx_fill_status(struct iwl_mld *mld, int link_id,\n1495:\t\t\t\t\t   struct ieee80211_hdr *hdr,\n1496:\t\t\t\t\t   struct sk_buff *skb,\n1497:\t\t\t\t\t   struct iwl_mld_rx_phy_data *phy_data)\n1498:\t{\n1499:\t\tstruct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);\n1500:\t\tu32 rate_n_flags = phy_data-\u003erate_n_flags;\n1501:\t\tu32 format = rate_n_flags \u0026 RATE_MCS_MOD_TYPE_MSK;\n1502:\t\n1503:\t\tiwl_mld_fill_signal(mld, link_id, hdr, rx_status, phy_data);\n1504:\t\n1505:\t\trx_status-\u003edevice_timestamp = phy_data-\u003egp2_on_air_rise;\n1506:\t\n1507:\t\tiwl_mld_set_rx_rate(mld, phy_data, rx_status);\n1508:\t\n1509:\t\t/* must be before HE data (radiotap field order) */\n1510:\t\tif (format == RATE_MCS_MOD_TYPE_VHT)\n1511:\t\t\tiwl_mld_rx_vht(skb, phy_data);\n1512:\t\n1513:\t\t/* must be before L-SIG data (radiotap field order) */\n1514:\t\tif (format == RATE_MCS_MOD_TYPE_HE)\n1515:\t\t\tiwl_mld_rx_he(skb, phy_data);\n1516:\t\n1517:\t\tiwl_mld_decode_lsig(skb, phy_data);\n1518:\t\n1519:\t\t/* TLVs - must be after radiotap fixed fields */\n1520:\t\tif (format == RATE_MCS_MOD_TYPE_EHT)\n1521:\t\t\tiwl_mld_rx_eht(mld, skb, phy_data);\n1522:\t\n1523:\t#ifdef CONFIG_IWLWIFI_DEBUGFS\n1524:\t\tif (unlikely(mld-\u003emonitor.on)) {\n1525:\t\t\tiwl_mld_add_rtap_sniffer_config(mld, skb);\n1526:\t\n1527:\t\t\tif (mld-\u003emonitor.ptp_time) {\n1528:\t\t\t\tu64 adj_time =\n1529:\t\t\t\t\tiwl_mld_ptp_get_adj_time(mld,\n1530:\t\t\t\t\t\t\t\t phy_data-\u003egp2_on_air_rise *\n1531:\t\t\t\t\t\t\t\t NSEC_PER_USEC);\n1532:\t\n1533:\t\t\t\trx_status-\u003emactime = div64_u64(adj_time, NSEC_PER_USEC);\n"
}

69/3 2026/09/15 18:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 61458
							output: 47
							thoughts: 25
						

Thoughts:
Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:

**Investigating SKB Allocation in iwl_mld_rx_mpdu**

Alright, so the question on my mind right now is precisely *where* does the `skb` allocation happen within the `iwl_mld_rx_mpdu` function? I need to understand that crucial bit. I'm going to dive right in and start tracing the code – there's no substitute for seeing it firsthand. Time to start the hunt and get to the root of where this buffer is actually brought into existence.



70/3 2026/09/15 18:17 tool read-file
0m Args:
{
  "File": "drivers/net/wireless/intel/iwlwifi/mld/rx.c",
  "FirstLine": 2040,
  "LineCount": 45
}

Results:
{
  "Contents": "2040:\t\n2041:\t\tif (IWL_FW_CHECK(mld, mpdu_len + mpdu_desc_size \u003e pkt_len,\n2042:\t\t\t\t \"FW lied about packet len (%d)\\n\", pkt_len))\n2043:\t\t\treturn;\n2044:\t\n2045:\t\tiwl_mld_fill_phy_data_from_mpdu(mld, mpdu_desc, \u0026phy_data);\n2046:\t\n2047:\t\t/* Don't use dev_alloc_skb(), we'll have enough headroom once\n2048:\t\t * ieee80211_hdr pulled.\n2049:\t\t *\n2050:\t\t * For monitor mode we need more space to include the full PHY\n2051:\t\t * notification data.\n2052:\t\t */\n2053:\t\tif (unlikely(mld-\u003emonitor.on) \u0026\u0026 phy_data.ntfy)\n2054:\t\t\talloc_size += sizeof(struct iwl_rx_phy_air_sniffer_ntfy);\n2055:\t\tskb = alloc_skb(alloc_size, GFP_ATOMIC);\n2056:\t\tif (!skb) {\n2057:\t\t\tIWL_ERR(mld, \"alloc_skb failed\\n\");\n2058:\t\t\treturn;\n2059:\t\t}\n2060:\t\n2061:\t\thdr = (void *)(pkt-\u003edata + mpdu_desc_size);\n2062:\t\n2063:\t\tif (mpdu_desc-\u003emac_flags2 \u0026 IWL_RX_MPDU_MFLG2_PAD) {\n2064:\t\t\t/* If the device inserted padding it means that (it thought)\n2065:\t\t\t * the 802.11 header wasn't a multiple of 4 bytes long. In\n2066:\t\t\t * this case, reserve two bytes at the start of the SKB to\n2067:\t\t\t * align the payload properly in case we end up copying it.\n2068:\t\t\t */\n2069:\t\t\tskb_reserve(skb, 2);\n2070:\t\t}\n2071:\t\n2072:\t\trx_status = IEEE80211_SKB_RXCB(skb);\n2073:\t\n2074:\t\t/* this is needed early */\n2075:\t\tband = u8_get_bits(mpdu_desc-\u003emac_phy_band,\n2076:\t\t\t\t   IWL_RX_MPDU_MAC_PHY_BAND_BAND_MASK);\n2077:\t\tiwl_mld_fill_rx_status_band_freq(rx_status, band,\n2078:\t\t\t\t\t\t mpdu_desc-\u003ev3.channel);\n2079:\t\n2080:\t\n2081:\t\trcu_read_lock();\n2082:\t\n2083:\t\tlink_sta = iwl_mld_rx_with_sta(mld, hdr, skb, mpdu_desc, pkt, queue,\n2084:\t\t\t\t\t       \u0026drop);\n"
}

71/3 2026/09/15 18:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 62465
							output: 41
							thoughts: 130
						

Thoughts:
**My Initial Thoughts on SKB Allocation and mac80211 Processing**

Okay, so `alloc_skb` is supposed to zero out `skb->cb`. I remember that now. Specifically, I know that `__alloc_skb` handles this with `memset` on `skb->cb` to ensure it's clean on allocation. That's a critical detail for avoiding stale data and potential security issues, particularly in network protocols. I need to make sure I understand how `mac80211` utilizes this initialization. I'm especially interested in tracking the `status->link_id` field within the context of that zeroed control block.

Now, let's dive into `net/mac80211/rx.c`. I'm curious to see how the code handles packet reception when `ieee80211_rx_list` gets called. My focus is on the sections handling the `status->link_id`. Let's have a look at lines 5690 through 5750, specifically, because that should be the area of interest in the current task.



72/3 2026/09/15 18:17 tool read-file
0m Args:
{
  "File": "net/mac80211/rx.c",
  "FirstLine": 5690,
  "LineCount": 60
}

Results:
{
  "Contents": "5690:\t\t\t\tif (WARN_ONCE(!(status-\u003erate_idx \u003c= 15 ||\n5691:\t\t\t\t\t\tstatus-\u003erate_idx == 17 ||\n5692:\t\t\t\t\t\tstatus-\u003erate_idx == 19 ||\n5693:\t\t\t\t\t\tstatus-\u003erate_idx == 20 ||\n5694:\t\t\t\t\t\tstatus-\u003erate_idx == 23) ||\n5695:\t\t\t\t\t      !status-\u003enss ||\n5696:\t\t\t\t\t      status-\u003enss \u003e 8 ||\n5697:\t\t\t\t\t      status-\u003euhr.gi \u003e NL80211_RATE_INFO_EHT_GI_3_2,\n5698:\t\t\t\t\t      \"Rate marked as a UHR rate but data is invalid: MCS:%d, NSS:%d, GI:%d\\n\",\n5699:\t\t\t\t\t      status-\u003erate_idx, status-\u003enss, status-\u003euhr.gi))\n5700:\t\t\t\t\tgoto drop;\n5701:\t\t\t\tif (WARN_ONCE(status-\u003euhr.elr \u0026\u0026\n5702:\t\t\t\t\t      (status-\u003enss != 1 || status-\u003erate_idx \u003e 1 ||\n5703:\t\t\t\t\t       status-\u003euhr.gi != NL80211_RATE_INFO_EHT_GI_1_6 ||\n5704:\t\t\t\t\t       status-\u003ebw != RATE_INFO_BW_20 || status-\u003euhr.im),\n5705:\t\t\t\t\t      \"bad UHR ELR MCS MCS:%d, NSS:%d, GI:%d, BW:%d, IM:%d\\n\",\n5706:\t\t\t\t\t      status-\u003erate_idx, status-\u003enss, status-\u003euhr.gi,\n5707:\t\t\t\t\t      status-\u003ebw, status-\u003euhr.im))\n5708:\t\t\t\t\tgoto drop;\n5709:\t\t\t\tif (WARN_ONCE(status-\u003euhr.im \u0026\u0026\n5710:\t\t\t\t\t      (status-\u003enss != 1 || status-\u003erate_idx == 15),\n5711:\t\t\t\t\t      \"bad UHR IM MCS MCS:%d, NSS:%d\\n\",\n5712:\t\t\t\t\t      status-\u003erate_idx, status-\u003enss))\n5713:\t\t\t\t\tgoto drop;\n5714:\t\t\t\tbreak;\n5715:\t\t\tcase RX_ENC_S1G:\n5716:\t\t\t\tif (WARN_ONCE(status-\u003erate_idx \u003e 12 ||\n5717:\t\t\t\t\t      !status-\u003enss ||\n5718:\t\t\t\t\t      status-\u003enss \u003e 4,\n5719:\t\t\t\t\t      \"Rate marked as an S1G rate but data is invalid: MCS: %d, NSS: %d\\n\",\n5720:\t\t\t\t\t      status-\u003erate_idx, status-\u003enss))\n5721:\t\t\t\t\tgoto drop;\n5722:\t\t\t\tbreak;\n5723:\t\t\tdefault:\n5724:\t\t\t\tWARN_ON_ONCE(1);\n5725:\t\t\t\tfallthrough;\n5726:\t\t\tcase RX_ENC_LEGACY:\n5727:\t\t\t\tif (WARN_ON(status-\u003erate_idx \u003e= sband-\u003en_bitrates))\n5728:\t\t\t\t\tgoto drop;\n5729:\t\t\t\trate = \u0026sband-\u003ebitrates[status-\u003erate_idx];\n5730:\t\t\t}\n5731:\t\t}\n5732:\t\n5733:\t\tstatus-\u003erx_flags = 0;\n5734:\t\n5735:\t\tkcov_remote_start_common(skb_get_kcov_handle(skb));\n5736:\t\n5737:\t\t/*\n5738:\t\t * Frames with failed FCS/PLCP checksum are not returned,\n5739:\t\t * all other frames are returned without radiotap header\n5740:\t\t * if it was previously present.\n5741:\t\t * Also, frames with less than 16 bytes are dropped.\n5742:\t\t */\n5743:\t\tif (!(status-\u003eflag \u0026 RX_FLAG_8023))\n5744:\t\t\tskb = ieee80211_rx_monitor(local, skb, rate);\n5745:\t\tif (skb) {\n5746:\t\t\tif ((status-\u003eflag \u0026 RX_FLAG_8023) ||\n5747:\t\t\t\tieee80211_is_data_present(hdr-\u003eframe_control))\n5748:\t\t\t\tieee80211_tpt_led_trig_rx(local, skb-\u003elen);\n5749:\t\n"
}

73/3 2026/09/15 18:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 64011
							output: 35
							thoughts: 1498
						

Thoughts:
**Analyzing Potential `link_id` Issues in mac80211 RX Path**

Okay, let's dissect this. My initial focus is on the `link_id` field within the mac80211 RX path and whether it could potentially lead to out-of-bounds access or information leaks. I'm starting by examining the core functions involved in packet handling, specifically `__ieee80211_rx_handle_packet` and `ieee80211_prepare_and_rx_handle` within `net/mac80211/rx.c`. I've noticed a crucial patch that introduces setting `status->link_id` in both of those functions *before* the RX processing logic.

The code sets `status->link_id` to either `rx->link_id` (if valid) or 0 if `rx->link_id` is negative. This immediately raises a question: where does `rx->link_id` originate? It appears to come from either `rx->link->link_id` or `link_sta->link_id`, meaning it will be valid.

I'm now investigating `__ieee80211_queue_skb_to_iface` where the packet is enqueued.  Here, I can see that the code that used to set `status->link_id` is gone, but it does *increment* `link_sta->rx_stats.packets`. The key point is that `status->link_id` is *already* set by the time the `skb` gets queued. I'm now thinking about how the `link_id` is used downstream in functions like `ieee80211_iface_process_skb` and `ieee80211_sta_rx_queued_frame`. It looks like these functions use `rx_status->link_id` to dereference the appropriate `link_sta` or `link`. However, since `link_id` comes from a valid `rx->link->link_id`, or `link_sta->link_id`, this should be valid.  It's unlikely that the dereference would be out of bounds, though it would be good to confirm that there's proper bounds checking for these dereferences, but this feels more like a KASAN thing if anything.

The next thought is to consider the broader impact – could this `link_id` potentially lead to information leaks, especially in monitor mode or through the radiotap header? So now I need to check the radiotap generation and `ieee80211_rx_monitor` functions. I need to see if the `link_id` or `link_valid` field are exposed to userspace via radiotap. I'm searching the code now for any use of "link_id" or "link_valid" in radiotap-related functions.



74/3 2026/09/15 18:17 tool grepper
0m Args:
{
  "Expression": "link_id",
  "PathPrefix": "net/mac80211/rx.c"
}

Results:
{
  "Output": "net/mac80211/rx.c=1559=ieee80211_rx_h_check(struct ieee80211_rx_data *rx)\n--\nnet/mac80211/rx.c-1586-\t\t\tif (cfg80211_rx_spurious_frame(rx-\u003esdata-\u003edev, hdr-\u003eaddr2,\nnet/mac80211/rx.c:1587:\t\t\t\t\t\t       rx-\u003elink_id, GFP_ATOMIC))\nnet/mac80211/rx.c-1588-\t\t\t\treturn RX_DROP_U_SPURIOUS_NOTIF;\n--\nnet/mac80211/rx.c-1620-\t\t    cfg80211_rx_spurious_frame(rx-\u003esdata-\u003edev, hdr-\u003eaddr2,\nnet/mac80211/rx.c:1621:\t\t\t\t\t       rx-\u003elink_id, GFP_ATOMIC))\nnet/mac80211/rx.c-1622-\t\t\treturn RX_DROP_U_SPURIOUS_NOTIF;\n--\nnet/mac80211/rx.c=1840=ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)\n--\nnet/mac80211/rx.c-1958-\t\t\t\t\trx-\u003esdata-\u003edev, sta-\u003esta.addr,\nnet/mac80211/rx.c:1959:\t\t\t\t\trx-\u003elink_id, GFP_ATOMIC);\nnet/mac80211/rx.c-1960-\t\t\treturn RX_DROP_U_UNEXPECTED_4ADDR_FRAME;\n--\nnet/mac80211/rx.c=2667=bool ieee80211_is_our_addr(struct ieee80211_sub_if_data *sdata,\nnet/mac80211/rx.c:2668:\t\t\t   const u8 *addr, int *out_link_id)\nnet/mac80211/rx.c-2669-{\nnet/mac80211/rx.c:2670:\tunsigned int link_id;\nnet/mac80211/rx.c-2671-\n--\nnet/mac80211/rx.c-2678-\nnet/mac80211/rx.c:2679:\tfor (link_id = 0; link_id \u003c ARRAY_SIZE(sdata-\u003evif.link_conf); link_id++) {\nnet/mac80211/rx.c-2680-\t\tstruct ieee80211_bss_conf *conf;\nnet/mac80211/rx.c-2681-\nnet/mac80211/rx.c:2682:\t\tconf = rcu_dereference(sdata-\u003evif.link_conf[link_id]);\nnet/mac80211/rx.c-2683-\n--\nnet/mac80211/rx.c-2686-\t\tif (ether_addr_equal(conf-\u003eaddr, addr)) {\nnet/mac80211/rx.c:2687:\t\t\tif (out_link_id)\nnet/mac80211/rx.c:2688:\t\t\t\t*out_link_id = link_id;\nnet/mac80211/rx.c-2689-\t\t\treturn true;\n--\nnet/mac80211/rx.c=2721=static void ieee80211_deliver_skb_to_local_stack(struct sk_buff *skb,\n--\nnet/mac80211/rx.c-2733-\nnet/mac80211/rx.c:2734:\t\tcfg80211_rx_control_port(dev, skb, noencrypt, rx-\u003elink_id);\nnet/mac80211/rx.c-2735-\t\tdev_kfree_skb(skb);\n--\nnet/mac80211/rx.c=3266=ieee80211_rx_h_data(struct ieee80211_rx_data *rx)\n--\nnet/mac80211/rx.c-3287-\t\t\tcfg80211_rx_unexpected_4addr_frame(\nnet/mac80211/rx.c:3288:\t\t\t\trx-\u003esdata-\u003edev, rx-\u003esta-\u003esta.addr, rx-\u003elink_id,\nnet/mac80211/rx.c-3289-\t\t\t\tGFP_ATOMIC);\n--\nnet/mac80211/rx.c=3457=ieee80211_rx_check_bss_color_collision(struct ieee80211_rx_data *rx)\n--\nnet/mac80211/rx.c-3497-\t\t\t\t\t\t\t      BIT_ULL(color),\nnet/mac80211/rx.c:3498:\t\t\t\t\t\t\t      bss_conf-\u003elink_id);\nnet/mac80211/rx.c-3499-\t}\n--\nnet/mac80211/rx.c=3977=ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)\n--\nnet/mac80211/rx.c-3983-\t\t.len = rx-\u003eskb-\u003elen,\nnet/mac80211/rx.c:3984:\t\t.link_id = rx-\u003elink_id,\nnet/mac80211/rx.c:3985:\t\t.have_link_id = rx-\u003elink_id \u003e= 0,\nnet/mac80211/rx.c-3986-\t\t.no_sta = !rx-\u003esta,\n--\nnet/mac80211/rx.c=4208=static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,\n--\nnet/mac80211/rx.c-4234-\t\t * in the case of reordering frames may have arrived\nnet/mac80211/rx.c:4235:\t\t * on different links, so adjust the link_id, link\nnet/mac80211/rx.c-4236-\t\t * and link_sta accordingly.\n--\nnet/mac80211/rx.c-4239-\t\tif (ieee80211_vif_is_mld(\u0026rx-\u003esdata-\u003evif) \u0026\u0026\nnet/mac80211/rx.c:4240:\t\t    unlikely(rx-\u003elink_id != status-\u003elink_id)) {\nnet/mac80211/rx.c-4241-\t\t\tif (rx-\u003esta) {\nnet/mac80211/rx.c-4242-\t\t\t\trx-\u003elink_sta =\nnet/mac80211/rx.c:4243:\t\t\t\t\trcu_dereference(rx-\u003esta-\u003elink[rx-\u003elink_id]);\nnet/mac80211/rx.c-4244-\n--\nnet/mac80211/rx.c-4248-\t\t\t}\nnet/mac80211/rx.c:4249:\t\t\trx-\u003elink_id = status-\u003elink_id;\nnet/mac80211/rx.c:4250:\t\t\trx-\u003elink = rcu_dereference(rx-\u003esdata-\u003elink[status-\u003elink_id]);\nnet/mac80211/rx.c-4251-\t\t}\n--\nnet/mac80211/rx.c=4318=static bool\nnet/mac80211/rx.c:4319:ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id)\nnet/mac80211/rx.c-4320-{\nnet/mac80211/rx.c-4321-\tif (sta-\u003emlo)\nnet/mac80211/rx.c:4322:\t\treturn sta-\u003evalid_links \u0026 BIT(link_id);\nnet/mac80211/rx.c-4323-\nnet/mac80211/rx.c:4324:\treturn sta-\u003edeflink.link_id == link_id;\nnet/mac80211/rx.c-4325-}\n--\nnet/mac80211/rx.c=4327=static bool ieee80211_rx_data_set_link_sta(struct ieee80211_rx_data *rx,\n--\nnet/mac80211/rx.c-4332-\tif (WARN_ON_ONCE(!link_sta) ||\nnet/mac80211/rx.c:4333:\t    !ieee80211_rx_is_valid_sta_link_id(\u0026link_sta-\u003esta-\u003esta,\nnet/mac80211/rx.c:4334:\t\t\t\t\t       link_sta-\u003elink_id))\nnet/mac80211/rx.c-4335-\t\treturn false;\n--\nnet/mac80211/rx.c-4340-\trx-\u003elocal = sta-\u003esdata-\u003elocal;\nnet/mac80211/rx.c:4341:\trx-\u003elink = rcu_dereference(sta-\u003esdata-\u003elink[link_sta-\u003elink_id]);\nnet/mac80211/rx.c-4342-\tif (!rx-\u003elink)\n--\nnet/mac80211/rx.c-4344-\nnet/mac80211/rx.c:4345:\trx-\u003elink_id = rx-\u003elink-\u003elink_id;\nnet/mac80211/rx.c-4346-\trx-\u003elink_sta = link_sta;\n--\nnet/mac80211/rx.c=4351=static bool ieee80211_rx_data_set_link(struct ieee80211_rx_data *rx,\nnet/mac80211/rx.c:4352:\t\t\t\t       u8 link_id)\nnet/mac80211/rx.c-4353-{\nnet/mac80211/rx.c:4354:\trx-\u003elink_id = link_id;\nnet/mac80211/rx.c:4355:\trx-\u003elink = rcu_dereference(rx-\u003esdata-\u003elink[link_id]);\nnet/mac80211/rx.c-4356-\n--\nnet/mac80211/rx.c-4359-\nnet/mac80211/rx.c:4360:\tif (!ieee80211_rx_is_valid_sta_link_id(\u0026rx-\u003esta-\u003esta, link_id))\nnet/mac80211/rx.c-4361-\t\treturn false;\nnet/mac80211/rx.c-4362-\nnet/mac80211/rx.c:4363:\trx-\u003elink_sta = rcu_dereference(rx-\u003esta-\u003elink[link_id]);\nnet/mac80211/rx.c-4364-\n--\nnet/mac80211/rx.c=4368=static bool ieee80211_rx_data_set_sta(struct ieee80211_rx_data *rx,\nnet/mac80211/rx.c:4369:\t\t\t\t      struct sta_info *sta, int link_id)\nnet/mac80211/rx.c-4370-{\nnet/mac80211/rx.c:4371:\trx-\u003elink_id = link_id;\nnet/mac80211/rx.c-4372-\trx-\u003esta = sta;\n--\nnet/mac80211/rx.c-4382-\nnet/mac80211/rx.c:4383:\tif (link_id \u003c 0) {\nnet/mac80211/rx.c-4384-\t\tif (ieee80211_vif_is_mld(\u0026rx-\u003esdata-\u003evif) \u0026\u0026\n--\nnet/mac80211/rx.c-4386-\t\t\trx-\u003elink =\nnet/mac80211/rx.c:4387:\t\t\t\trcu_dereference(rx-\u003esdata-\u003elink[sta-\u003edeflink.link_id]);\nnet/mac80211/rx.c-4388-\t\t\trx-\u003elink_sta = \u0026sta-\u003edeflink;\n--\nnet/mac80211/rx.c-4391-\t\t}\nnet/mac80211/rx.c:4392:\t} else if (!ieee80211_rx_data_set_link(rx, link_id)) {\nnet/mac80211/rx.c-4393-\t\treturn false;\n--\nnet/mac80211/rx.c=4403=void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)\n--\nnet/mac80211/rx.c-4411-\tstruct tid_ampdu_rx *tid_agg_rx;\nnet/mac80211/rx.c:4412:\tint link_id = -1;\nnet/mac80211/rx.c-4413-\n--\nnet/mac80211/rx.c-4415-\tif (sta-\u003esta.valid_links)\nnet/mac80211/rx.c:4416:\t\tlink_id = ffs(sta-\u003esta.valid_links) - 1;\nnet/mac80211/rx.c-4417-\nnet/mac80211/rx.c:4418:\tif (!ieee80211_rx_data_set_sta(\u0026rx, sta, link_id))\nnet/mac80211/rx.c-4419-\t\treturn;\n--\nnet/mac80211/rx.c=4535=static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)\n--\nnet/mac80211/rx.c-4560-\t\t\treturn true;\nnet/mac80211/rx.c:4561:\t\treturn ieee80211_is_our_addr(sdata, hdr-\u003eaddr1, \u0026rx-\u003elink_id);\nnet/mac80211/rx.c-4562-\tcase NL80211_IFTYPE_ADHOC:\n--\nnet/mac80211/rx.c-4619-\t\t\treturn ieee80211_is_our_addr(sdata, hdr-\u003eaddr1,\nnet/mac80211/rx.c:4620:\t\t\t\t\t\t     \u0026rx-\u003elink_id);\nnet/mac80211/rx.c-4621-\n--\nnet/mac80211/rx.c-4631-\t\t\t    !ieee80211_is_our_addr(sdata, hdr-\u003eaddr1,\nnet/mac80211/rx.c:4632:\t\t\t\t\t\t   \u0026rx-\u003elink_id))\nnet/mac80211/rx.c-4633-\t\t\t\treturn false;\n--\nnet/mac80211/rx.c=5033=static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,\n--\nnet/mac80211/rx.c-5175-\tstatus = IEEE80211_SKB_RXCB(skb);\nnet/mac80211/rx.c:5176:\tstatus-\u003elink_id = rx-\u003elink_id \u003c 0 ? 0 : rx-\u003elink_id;\nnet/mac80211/rx.c-5177-\n--\nnet/mac80211/rx.c=5194=static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,\n--\nnet/mac80211/rx.c-5246-\tstatus = IEEE80211_SKB_RXCB(rx-\u003eskb);\nnet/mac80211/rx.c:5247:\tstatus-\u003elink_id = rx-\u003elink_id \u003c 0 ? 0 : rx-\u003elink_id;\nnet/mac80211/rx.c-5248-\n--\nnet/mac80211/rx.c=5283=static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,\n--\nnet/mac80211/rx.c-5307-\tsta = container_of(link_pubsta-\u003esta, struct sta_info, sta);\nnet/mac80211/rx.c:5308:\tif (!ieee80211_rx_data_set_sta(\u0026rx, sta, link_pubsta-\u003elink_id))\nnet/mac80211/rx.c-5309-\t\tgoto drop;\nnet/mac80211/rx.c-5310-\nnet/mac80211/rx.c:5311:\trx.link_id = link_pubsta-\u003elink_id;\nnet/mac80211/rx.c-5312-\n--\nnet/mac80211/rx.c=5343=static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,\n--\nnet/mac80211/rx.c-5415-\t\t\t\t\t   sta);\nnet/mac80211/rx.c:5416:\t\t\tlink_sta = rcu_dereference(sta-\u003elink[link_pubsta-\u003elink_id]);\nnet/mac80211/rx.c-5417-\n--\nnet/mac80211/rx.c-5457-\t\t\tsdata =\tsta-\u003esdata;\nnet/mac80211/rx.c:5458:\t\t\tlink = rcu_dereference(sdata-\u003elink[link_sta-\u003elink_id]);\nnet/mac80211/rx.c-5459-\n--\nnet/mac80211/rx.c-5521-\t\t\tif (link_sta)\nnet/mac80211/rx.c:5522:\t\t\t\tlink = rcu_dereference(sdata-\u003elink[link_sta-\u003elink_id]);\nnet/mac80211/rx.c-5523-\t\t}\n--\nnet/mac80211/rx.c-5574-\t\trx.link = link;\nnet/mac80211/rx.c:5575:\t\trx.link_id = link-\u003elink_id;\nnet/mac80211/rx.c-5576-\t\trx.sta = NULL;\n"
}

75/3 2026/09/15 18:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 71193
							output: 41
							thoughts: 53
						

Thoughts:
**Diving into the MAC80211 RX Path**

Okay, time to get my hands dirty. I need to understand what's happening in `net/mac80211/rx.c` around the critical receive processing section. I'm thinking specifically around those lines where the data frames are being handled, so let's check lines 4220-4255. Gotta make sure I have a good grasp on this. Alright, let's pull those lines up to my working set and read lines 4220-4260 using the `read-file` command. I'll read through these and see what I can learn.



76/3 2026/09/15 18:17 tool read-file
0m Args:
{
  "File": "net/mac80211/rx.c",
  "FirstLine": 4220,
  "LineCount": 45
}

Results:
{
  "Contents": "4220:\t\n4221:\t\t/* Lock here to avoid hitting all of the data used in the RX\n4222:\t\t * path (e.g. key data, station data, ...) concurrently when\n4223:\t\t * a frame is released from the reorder buffer due to timeout\n4224:\t\t * from the timer, potentially concurrently with RX from the\n4225:\t\t * driver.\n4226:\t\t */\n4227:\t\tspin_lock_bh(\u0026rx-\u003elocal-\u003erx_path_lock);\n4228:\t\n4229:\t\twhile ((skb = __skb_dequeue(frames))) {\n4230:\t\t\tstruct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);\n4231:\t\n4232:\t\t\t/*\n4233:\t\t\t * Most fields are valid across frames. However,\n4234:\t\t\t * in the case of reordering frames may have arrived\n4235:\t\t\t * on different links, so adjust the link_id, link\n4236:\t\t\t * and link_sta accordingly.\n4237:\t\t\t */\n4238:\t\t\trx-\u003eskb = skb;\n4239:\t\t\tif (ieee80211_vif_is_mld(\u0026rx-\u003esdata-\u003evif) \u0026\u0026\n4240:\t\t\t    unlikely(rx-\u003elink_id != status-\u003elink_id)) {\n4241:\t\t\t\tif (rx-\u003esta) {\n4242:\t\t\t\t\trx-\u003elink_sta =\n4243:\t\t\t\t\t\trcu_dereference(rx-\u003esta-\u003elink[rx-\u003elink_id]);\n4244:\t\n4245:\t\t\t\t\t/* Link got disabled, just use deflink */\n4246:\t\t\t\t\tif (!rx-\u003elink_sta)\n4247:\t\t\t\t\t\trx-\u003elink_sta = \u0026rx-\u003esta-\u003edeflink;\n4248:\t\t\t\t}\n4249:\t\t\t\trx-\u003elink_id = status-\u003elink_id;\n4250:\t\t\t\trx-\u003elink = rcu_dereference(rx-\u003esdata-\u003elink[status-\u003elink_id]);\n4251:\t\t\t}\n4252:\t\n4253:\t\t\tif (WARN_ON_ONCE(!rx-\u003elink)) {\n4254:\t\t\t\tres = RX_DROP_U_NO_LINK;\n4255:\t\t\t\tgoto rxh_next;\n4256:\t\t\t}\n4257:\t\n4258:\t\t\tCALL_RXH(ieee80211_rx_h_check_more_data);\n4259:\t\t\tCALL_RXH(ieee80211_rx_h_uapsd_and_pspoll);\n4260:\t\t\tCALL_RXH(ieee80211_rx_h_sta_process);\n4261:\t\t\tCALL_RXH(ieee80211_rx_h_decrypt);\n4262:\t\t\tCALL_RXH(ieee80211_rx_h_defragment);\n4263:\t\t\tCALL_RXH(ieee80211_rx_h_michael_mic_verify);\n4264:\t\t\t/* must be after MMIC verify so header is counted in MPDU mic */\n"
}

77/3 2026/09/15 18:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 72257
							output: 41
							thoughts: 518
						

Thoughts:
**Analyzing Potential Array Index Out-of-Bounds in mac80211**

Okay, let's break this down. I'm looking at `ieee80211_rx_handlers` and immediately I'm tripped up by this line: `rx->link_sta = rcu_dereference(rx->sta->link[rx->link_id]);`. The crux of the issue is how `rx->link_id` is used to index into `rx->sta->link`. My first thought is to check when `rx->link_id` is assigned and, yep, it's set after the array dereference via `rx->link_id = status->link_id`. This strikes me as a potential array index out-of-bounds issue, because `sta->link` is an array of size `IEEE80211_MLD_MAX_NUM_LINKS`, but `rx->link_id` seems to be assigned later, meaning that it may be invalid at the time of the array dereference.

Next, I need to look at how `link_id` is used in the transmit path. I'll search `net/mac80211/tx.c`. I see `__ieee80211_tx_skb_tid_band`, which takes a `link_id` argument, and then `ieee80211_tx_skb_tid` also takes `link_id`. Let's also check `net/mac80211/offchannel.c`. I notice the call to `ieee80211_tx_skb_tid` there as well, also passing `link_id`. Now I want to look closely at `ieee80211_xmit` to see how `sta` is being used and whether the `link_id` is influencing any operations in that critical function.



78/3 2026/09/15 18:17 tool grepper
0m Args:
{
  "Expression": "void ieee80211_xmit\\(",
  "PathPrefix": "net/mac80211/"
}

Results:
{
  "Output": "net/mac80211/ieee80211_i.h=2468=void ieee80211_set_wmm_default(struct ieee80211_link_data *link,\nnet/mac80211/ieee80211_i.h-2469-\t\t\t       bool bss_notify, bool enable_qos);\nnet/mac80211/ieee80211_i.h:2470:void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,\nnet/mac80211/ieee80211_i.h-2471-\t\t    struct sta_info *sta, struct sk_buff *skb);\n--\nnet/mac80211/tx.c=2051=static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata,\n--\nnet/mac80211/tx.c-2087-\nnet/mac80211/tx.c:2088:void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,\nnet/mac80211/tx.c-2089-\t\t    struct sta_info *sta, struct sk_buff *skb)\n"
}

79/3 2026/09/15 18:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 73605
							output: 41
							thoughts: 31
						

Thoughts:
Okay, here we go...

**Diving into the 802.11 Transmit Path**

Alright, time to dig into the heart of the beast. I want to refresh my memory and maybe spot something interesting in the `ieee80211_xmit` function, specifically within `net/mac80211/tx.c`. It's where the actual transmission of 802.11 frames is orchestrated. Let's see what kind of optimizations or handling of corner cases they're doing these days. I'm expecting to see the usual suspects: rate selection, hardware queue management, retry logic, maybe some frame aggregation if it's a modern implementation. I'm particularly interested in how they're dealing with the interaction between the software stack and the hardware's transmit capabilities. Are they using DMA effectively? Are there any clever techniques to minimize CPU overhead during transmit? Let's get to it.



80/3 2026/09/15 18:17 tool read-file
0m Args:
{
  "File": "net/mac80211/tx.c",
  "FirstLine": 2088,
  "LineCount": 45
}

Results:
{
  "Contents": "2088:\tvoid ieee80211_xmit(struct ieee80211_sub_if_data *sdata,\n2089:\t\t\t    struct sta_info *sta, struct sk_buff *skb)\n2090:\t{\n2091:\t\tstruct ieee80211_local *local = sdata-\u003elocal;\n2092:\t\tstruct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);\n2093:\t\tstruct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb-\u003edata;\n2094:\t\tint headroom;\n2095:\t\tenum ieee80211_encrypt encrypt;\n2096:\t\n2097:\t\tif (info-\u003eflags \u0026 IEEE80211_TX_INTFL_DONT_ENCRYPT)\n2098:\t\t\tencrypt = ENCRYPT_NO;\n2099:\t\telse if (ieee80211_is_mgmt(hdr-\u003eframe_control))\n2100:\t\t\tencrypt = ENCRYPT_MGMT;\n2101:\t\telse\n2102:\t\t\tencrypt = ENCRYPT_DATA;\n2103:\t\n2104:\t\theadroom = local-\u003etx_headroom;\n2105:\t\tif (encrypt != ENCRYPT_NO)\n2106:\t\t\theadroom += IEEE80211_ENCRYPT_HEADROOM;\n2107:\t\theadroom -= skb_headroom(skb);\n2108:\t\theadroom = max_t(int, 0, headroom);\n2109:\t\n2110:\t\tif (ieee80211_skb_resize(sdata, skb, headroom, encrypt)) {\n2111:\t\t\tieee80211_free_txskb(\u0026local-\u003ehw, skb);\n2112:\t\t\treturn;\n2113:\t\t}\n2114:\t\n2115:\t\t/* reload after potential resize */\n2116:\t\thdr = (struct ieee80211_hdr *) skb-\u003edata;\n2117:\t\tinfo-\u003econtrol.vif = \u0026sdata-\u003evif;\n2118:\t\n2119:\t\tif (ieee80211_vif_is_mesh(\u0026sdata-\u003evif)) {\n2120:\t\t\tif (ieee80211_is_data(hdr-\u003eframe_control) \u0026\u0026\n2121:\t\t\t    is_unicast_ether_addr(hdr-\u003eaddr1)) {\n2122:\t\t\t\tif (mesh_nexthop_resolve(sdata, skb))\n2123:\t\t\t\t\treturn; /* skb queued: don't free */\n2124:\t\t\t} else {\n2125:\t\t\t\tieee80211_mps_set_frame_flags(sdata, NULL, hdr);\n2126:\t\t\t}\n2127:\t\t}\n2128:\t\n2129:\t\tieee80211_set_qos_hdr(sdata, skb);\n2130:\t\tieee80211_tx(sdata, sta, skb, false);\n2131:\t}\n2132:\t\n"
}

81/3 2026/09/15 18:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 74721
							output: 42
							thoughts: 20
						

Thoughts:
Here's my thought process, as if I were analyzing the situation:

**Tracing the Usage of 'sta' within ieee80211_tx**

Okay, so I need to understand how the `sta` variable is utilized within the `ieee80211_tx` context. This is a common debugging or auditing task. Let's start by doing a quick code search for that specific string. I'm going to look for all instances of `sta` directly within that function or any functions it calls. Since I'm familiar with this area, I'll be looking for references that could indicate station-specific transmission handling. I'm specifically looking for patterns like:

*   Accesses to members of a `sta` structure (e.g., `sta->flags`, `sta->rate`, `sta->tid_stats`).
*   Comparisons of `sta` with other variables (e.g., checking if `sta` is valid or active).
*   Uses of `sta` as a function argument, particularly in functions that handle QoS, rate control, or frame formatting.
*   Assignments to `sta` from other structures or function calls (e.g., retrieving the `sta` pointer from a list or hash table).
*   Any manipulation or use of the returned pointer from a `sta` related function.

I expect to see `sta` being used to access information specific to a wireless station (like its capabilities, supported rates, power limits, security context, etc.) as the driver is preparing frames for transmission. Understanding *how* it's used will give me a better grasp of the transmission process and any potential optimizations or security considerations. I need to note the context of each usage to form a complete understanding.



82/3 2026/09/15 18:17 tool grepper
0m Args:
{
  "Expression": "ieee80211_tx\\(",
  "PathPrefix": "net/mac80211/tx.c"
}

Results:
{
  "Output": "net/mac80211/tx.c=1732=static bool ieee80211_tx_frags(struct ieee80211_local *local,\n--\nnet/mac80211/tx.c-1807- */\nnet/mac80211/tx.c:1808:static bool __ieee80211_tx(struct ieee80211_local *local,\nnet/mac80211/tx.c-1809-\t\t\t   struct sk_buff_head *skbs, struct sta_info *sta,\n--\nnet/mac80211/tx.c=1994=EXPORT_SYMBOL(ieee80211_tx_prepare_skb);\n--\nnet/mac80211/tx.c-1998- */\nnet/mac80211/tx.c:1999:static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,\nnet/mac80211/tx.c-2000-\t\t\t struct sta_info *sta, struct sk_buff *skb,\n--\nnet/mac80211/tx.c-2037-\tif (!invoke_tx_handlers_late(\u0026tx))\nnet/mac80211/tx.c:2038:\t\tresult = __ieee80211_tx(local, \u0026tx.skbs, tx.sta, txpending);\nnet/mac80211/tx.c-2039-\n--\nnet/mac80211/tx.c=2088=void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,\n--\nnet/mac80211/tx.c-2129-\tieee80211_set_qos_hdr(sdata, skb);\nnet/mac80211/tx.c:2130:\tieee80211_tx(sdata, sta, skb, false);\nnet/mac80211/tx.c-2131-}\n--\nnet/mac80211/tx.c=4964=static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,\n--\nnet/mac80211/tx.c-4986-\t\t}\nnet/mac80211/tx.c:4987:\t\tresult = ieee80211_tx(sdata, NULL, skb, true);\nnet/mac80211/tx.c-4988-\t} else if (info-\u003eflags \u0026 IEEE80211_TX_CTL_HW_80211_ENCAP) {\n--\nnet/mac80211/tx.c-5006-\nnet/mac80211/tx.c:5007:\t\tresult = __ieee80211_tx(local, \u0026skbs, sta, true);\nnet/mac80211/tx.c-5008-\t}\n"
}

83/3 2026/09/15 18:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 75970
							output: 41
							thoughts: 28
						

Thoughts:
Okay, let's dive into this code.

**Analyzing the IEEE 802.11 Transmit Path**

Alright, I'm focusing on `ieee80211_tx`, specifically lines 2000 through 2035. I'm assuming, given the context, this is a critical section of the 802.11 transmit process. My initial goal is to understand what's happening within this block of code. Based on the line numbers, I'd wager it’s involved in either packet preparation, queueing, rate selection, or potentially the final stages of transmission before the packet hits the air. I'll need to pinpoint which. I'm looking for key data structures being manipulated here – things like frame descriptors, rate control data, hardware queue pointers, etc. I'm expecting to see conditions around frame types, likely to decide whether to use a specific MCS rate, or different retry limits. I'll be looking for any hints on how QoS is handled or any interaction with the hardware. There may be a call to a lower-level function to trigger the actual transmission. I'll need to know which device registers this block is writing to. This segment must be essential to overall throughput and latency, therefore, I need to get a clear picture of exactly what is happening in this part of the process. I also must keep an eye out for any potential race conditions and/or optimization opportunities.



84/3 2026/09/15 18:17 tool read-file
0m Args:
{
  "File": "net/mac80211/tx.c",
  "FirstLine": 2000,
  "LineCount": 40
}

Results:
{
  "Contents": "2000:\t\t\t\t struct sta_info *sta, struct sk_buff *skb,\n2001:\t\t\t\t bool txpending)\n2002:\t{\n2003:\t\tstruct ieee80211_local *local = sdata-\u003elocal;\n2004:\t\tstruct ieee80211_tx_data tx;\n2005:\t\tieee80211_tx_result res_prepare;\n2006:\t\tstruct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);\n2007:\t\tbool result = true;\n2008:\t\n2009:\t\tif (unlikely(skb-\u003elen \u003c 10)) {\n2010:\t\t\tdev_kfree_skb(skb);\n2011:\t\t\treturn true;\n2012:\t\t}\n2013:\t\n2014:\t\t/* initialises tx */\n2015:\t\tres_prepare = ieee80211_tx_prepare(sdata, \u0026tx, sta, skb);\n2016:\t\n2017:\t\tif (unlikely(res_prepare == TX_DROP)) {\n2018:\t\t\tieee80211_free_txskb(\u0026local-\u003ehw, skb);\n2019:\t\t\ttx.sdata-\u003etx_handlers_drop++;\n2020:\t\t\treturn true;\n2021:\t\t} else if (unlikely(res_prepare == TX_QUEUED)) {\n2022:\t\t\treturn true;\n2023:\t\t}\n2024:\t\n2025:\t\t/* set up hw_queue value early */\n2026:\t\tif (!(info-\u003eflags \u0026 IEEE80211_TX_CTL_TX_OFFCHAN) ||\n2027:\t\t    !ieee80211_hw_check(\u0026local-\u003ehw, QUEUE_CONTROL))\n2028:\t\t\tinfo-\u003ehw_queue =\n2029:\t\t\t\tsdata-\u003evif.hw_queue[skb_get_queue_mapping(skb)];\n2030:\t\n2031:\t\tif (invoke_tx_handlers_early(\u0026tx))\n2032:\t\t\treturn true;\n2033:\t\n2034:\t\tif (ieee80211_queue_skb(local, sdata, tx.sta, tx.skb))\n2035:\t\t\treturn true;\n2036:\t\n2037:\t\tif (!invoke_tx_handlers_late(\u0026tx))\n2038:\t\t\tresult = __ieee80211_tx(local, \u0026tx.skbs, tx.sta, txpending);\n2039:\t\n"
}

85/3 2026/09/15 18:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 77041
							output: 44
							thoughts: 48
						

Thoughts:
Okay, let's break this down.

**Diving into the Transmission Preparation**

Alright, my brain just flagged `ieee80211_tx_prepare(sdata, &tx, sta, skb);`. Time to dig in and understand what's happening at the heart of the 802.11 transmission process. Specifically, I need to see what's being set up by that `ieee80211_tx_prepare` function call. I need to understand how the station data (`sdata`), transmission information (`&tx`), station information (`sta`), and the socket buffer (`skb`) are interacting during the preparation phase. This function is likely the linchpin, where we configure the packet for transmission, potentially setting up things like MAC headers, rate control parameters, power levels, and queuing. I need to understand how it's preparing the packet for the radio and ensure everything is set up correctly based on the station's capabilities and the current network conditions. Let's see how it goes.



86/3 2026/09/15 18:17 tool grepper
0m Args:
{
  "Expression": "ieee80211_tx_prepare\\(",
  "PathPrefix": "net/mac80211/tx.c"
}

Results:
{
  "Output": "net/mac80211/tx.c=1253=static ieee80211_tx_result\nnet/mac80211/tx.c:1254:ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,\nnet/mac80211/tx.c-1255-\t\t     struct ieee80211_tx_data *tx,\n--\nnet/mac80211/tx.c=1956=bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,\n--\nnet/mac80211/tx.c-1964-\nnet/mac80211/tx.c:1965:\tif (ieee80211_tx_prepare(sdata, \u0026tx, NULL, skb) == TX_DROP) {\nnet/mac80211/tx.c-1966-\t\tkfree_skb(skb);\n--\nnet/mac80211/tx.c=1999=static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,\n--\nnet/mac80211/tx.c-2014-\t/* initialises tx */\nnet/mac80211/tx.c:2015:\tres_prepare = ieee80211_tx_prepare(sdata, \u0026tx, sta, skb);\nnet/mac80211/tx.c-2016-\n--\nnet/mac80211/tx.c=6286=ieee80211_get_buffered_bc(struct ieee80211_hw *hw,\n--\nnet/mac80211/tx.c-6339-\t\t\tsdata = IEEE80211_DEV_TO_SUB_IF(skb-\u003edev);\nnet/mac80211/tx.c:6340:\t\tif (!ieee80211_tx_prepare(sdata, \u0026tx, NULL, skb))\nnet/mac80211/tx.c-6341-\t\t\tbreak;\n"
}

87/3 2026/09/15 18:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 77937
							output: 41
							thoughts: 44
						

Thoughts:
Okay, let's dive into this. I'm going to pull up `net/mac80211/tx.c` and focus on the `ieee80211_tx_prepare` function, specifically lines 1254 through 1300. I need to refresh my memory on the data preparation process for transmitting 802.11 frames. I'm interested in how the driver handles the various control fields, frame types, and data elements before handing them off to the hardware. I will be looking to see if there are any aspects of the hardware config, such as buffer allocation or rate selection, that are addressed here. This function is key to how the MAC layer interacts with the lower physical layers, so understanding its role is important for any optimization or troubleshooting work.



88/3 2026/09/15 18:17 tool read-file
0m Args:
{
  "File": "net/mac80211/tx.c",
  "FirstLine": 1254,
  "LineCount": 45
}

Results:
{
  "Contents": "1254:\tieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,\n1255:\t\t\t     struct ieee80211_tx_data *tx,\n1256:\t\t\t     struct sta_info *sta, struct sk_buff *skb)\n1257:\t{\n1258:\t\tstruct ieee80211_local *local = sdata-\u003elocal;\n1259:\t\tstruct ieee80211_hdr *hdr;\n1260:\t\tstruct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);\n1261:\t\tbool aggr_check = false;\n1262:\t\tint tid;\n1263:\t\n1264:\t\tmemset(tx, 0, sizeof(*tx));\n1265:\t\ttx-\u003eskb = skb;\n1266:\t\ttx-\u003elocal = local;\n1267:\t\ttx-\u003esdata = sdata;\n1268:\t\t__skb_queue_head_init(\u0026tx-\u003eskbs);\n1269:\t\n1270:\t\t/*\n1271:\t\t * If this flag is set to true anywhere, and we get here,\n1272:\t\t * we are doing the needed processing, so remove the flag\n1273:\t\t * now.\n1274:\t\t */\n1275:\t\tinfo-\u003econtrol.flags \u0026= ~IEEE80211_TX_INTCFL_NEED_TXPROCESSING;\n1276:\t\n1277:\t\thdr = (struct ieee80211_hdr *) skb-\u003edata;\n1278:\t\n1279:\t\tif (likely(sta)) {\n1280:\t\t\tif (!IS_ERR(sta))\n1281:\t\t\t\ttx-\u003esta = sta;\n1282:\t\t} else {\n1283:\t\t\tif (sdata-\u003evif.type == NL80211_IFTYPE_AP_VLAN) {\n1284:\t\t\t\ttx-\u003esta = rcu_dereference(sdata-\u003eu.vlan.sta);\n1285:\t\t\t\tif (!tx-\u003esta \u0026\u0026 sdata-\u003ewdev.use_4addr)\n1286:\t\t\t\t\treturn TX_DROP;\n1287:\t\t\t} else if (tx-\u003esdata-\u003econtrol_port_protocol == tx-\u003eskb-\u003eprotocol) {\n1288:\t\t\t\ttx-\u003esta = sta_info_get_bss(sdata, hdr-\u003eaddr1);\n1289:\t\t\t}\n1290:\t\t\tif (!tx-\u003esta \u0026\u0026 !is_multicast_ether_addr(hdr-\u003eaddr1)) {\n1291:\t\t\t\ttx-\u003esta = sta_info_get(sdata, hdr-\u003eaddr1);\n1292:\t\t\t\taggr_check = true;\n1293:\t\t\t}\n1294:\t\t}\n1295:\t\n1296:\t\tif (tx-\u003esta \u0026\u0026 ieee80211_is_data_qos(hdr-\u003eframe_control) \u0026\u0026\n1297:\t\t    !ieee80211_is_qos_nullfunc(hdr-\u003eframe_control) \u0026\u0026\n1298:\t\t    ieee80211_hw_check(\u0026local-\u003ehw, AMPDU_AGGREGATION) \u0026\u0026\n"
}

89/3 2026/09/15 18:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 79042
							output: 29
							thoughts: 1943
						

Thoughts:
**Uninitialized Memory Analysis in Wireless Driver Patch**

Alright, let's break down this patch and pinpoint any potential issues related to uninitialized memory. My initial observation about the use of `ERR_PTR(-ENOENT)` to prevent STA lookups seems sound; it's a clever way to ensure `tx->sta` remains NULL, thus avoiding an unnecessary lookup when `no_sta` is true.

Now, focusing on the diffs, I'm examining each section for uninitialized memory usage, leaks to userspace, or conditional branches depending on uninitialized data.

1.  **`ath11k/dp_rx.c`**: Passing `&pubsta->deflink` looks fine; we're just providing a pointer to an existing field.

2.  **`ath12k/dp_mon.c`**: Removal of `status->link_valid = 0;` isn't immediately concerning, as long as `status` is properly initialized elsewhere. I'll need to confirm that `status` is always initialized before use.

3.  **`ath12k/dp_rx.c`**: The `link_pubsta` initialization to NULL is good practice. The check for `link_id >= 0` and subsequent dereference of `pubsta->link[link_id]` raises a potential out-of-bounds access if `link_id` is too large. However, I expect that KASAN or UBSAN should catch an out-of-bounds read, not KMSAN. The use of `rcu_dereference` is appropriate. Using `&pubsta->deflink` if there are no valid links is expected behaviour, it shouldn't be a source of uninitialized memory.

4.  **`ath12k/hw.h`, `ath12k/wifi7/dp_rx.c`, `ath12k/wifi7/hw.c`**: These functions return link IDs based on hardware-specific logic. The use of `dp_peer->hw_links` is fine if the array is properly initialized. I'll have to make sure this is actually the case.

5.  **`ath12k/wmi.c`**: Removing assignments to `status->link_valid` and `status->link_id` requires close scrutiny of how `status` is used. If this structure isn't initialized, this is a potential issue.

6.  **`iwlwifi/mld/agg.c`, `agg.h`, `rx.c`, `rx.h`, `tests/agg.c`**: The change to use `link_sta` instead of `sta` makes sense and doesn't inherently introduce uninitialized memory issues, as long as `link_sta` is used where the previous `sta` was used.

7.  **`iwlwifi/mvm/rx.c`, `rxmq.c`**: Passing `sta ? &sta->deflink : NULL` is standard and safe, it's not a source of uninitialized memory.

8.  **`mt76/mac80211.c`**: The assignment to `*link_sta` is always happening. If `sta` is NULL the `*link_sta` is assigned to `NULL`, otherwise to a pointer that is an address of the `sta` or its `deflink`. This is fine.

9.  **`rtw89/core.c`, `mac80211_hwsim_main.c`**: Removing assignments to `rx_status->link_valid` and `rx_status->link_id` is similar to the `ath12k` case. I'll need to check how `rx_status` is used.

10. **`include/net/cfg80211.h`**: The addition of `no_sta` to `cfg80211_mgmt_tx_params` and `cfg80211_rx_info` seems safe, as the struct is either zero-initialized (by the compiler or using `memset`) or properly initialized. The `no_sta` flag is only sent to userspace if it's true, and it's a zero-byte flag (no data), so no uninitialized data is leaked.

11. **`include/net/mac80211.h`**: This is interesting. Changing `ieee80211_rx_status` to remove `link_valid` seems fine on its own but I need to make sure that the `link_valid` and `link_id` are no longer sent to user space. I have to check all instances where the `struct ieee8


90/3 2026/09/15 18:18 tool grepper
0m Args:
{
  "Expression": "sizeof\\(struct ieee80211_rx_status\\)"
}

Results:
{
  "Output": "drivers/net/wireless/ath/ath9k/htc_drv_txrx.c=972=static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,\n--\ndrivers/net/wireless/ath/ath9k/htc_drv_txrx.c-1026-\ndrivers/net/wireless/ath/ath9k/htc_drv_txrx.c:1027:\tmemset(rx_status, 0, sizeof(struct ieee80211_rx_status));\ndrivers/net/wireless/ath/ath9k/htc_drv_txrx.c-1028-\n--\ndrivers/net/wireless/ath/ath9k/htc_drv_txrx.c=1081=void ath9k_rx_tasklet(struct tasklet_struct *t)\n--\ndrivers/net/wireless/ath/ath9k/htc_drv_txrx.c-1112-\t\tmemcpy(IEEE80211_SKB_RXCB(rxbuf-\u003eskb), \u0026rx_status,\ndrivers/net/wireless/ath/ath9k/htc_drv_txrx.c:1113:\t\t       sizeof(struct ieee80211_rx_status));\ndrivers/net/wireless/ath/ath9k/htc_drv_txrx.c-1114-\t\tskb = rxbuf-\u003eskb;\n--\ndrivers/net/wireless/ath/ath9k/recv.c=1058=int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)\n--\ndrivers/net/wireless/ath/ath9k/recv.c-1111-\t\trxs = IEEE80211_SKB_RXCB(hdr_skb);\ndrivers/net/wireless/ath/ath9k/recv.c:1112:\t\tmemset(rxs, 0, sizeof(struct ieee80211_rx_status));\ndrivers/net/wireless/ath/ath9k/recv.c-1113-\n--\ndrivers/net/wireless/realtek/rtl8xxxu/core.c=6320=int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff *skb)\n--\ndrivers/net/wireless/realtek/rtl8xxxu/core.c-6370-\t\trx_status = IEEE80211_SKB_RXCB(skb);\ndrivers/net/wireless/realtek/rtl8xxxu/core.c:6371:\t\tmemset(rx_status, 0, sizeof(struct ieee80211_rx_status));\ndrivers/net/wireless/realtek/rtl8xxxu/core.c-6372-\n--\ndrivers/net/wireless/realtek/rtl8xxxu/core.c=6439=int rtl8xxxu_parse_rxdesc24(struct rtl8xxxu_priv *priv, struct sk_buff *skb)\n--\ndrivers/net/wireless/realtek/rtl8xxxu/core.c-6480-\t\trx_status = IEEE80211_SKB_RXCB(skb);\ndrivers/net/wireless/realtek/rtl8xxxu/core.c:6481:\t\tmemset(rx_status, 0, sizeof(struct ieee80211_rx_status));\ndrivers/net/wireless/realtek/rtl8xxxu/core.c-6482-\n--\ndrivers/net/wireless/ti/wl1251/rx.c=32=static void wl1251_rx_status(struct wl1251 *wl,\n--\ndrivers/net/wireless/ti/wl1251/rx.c-39-\ndrivers/net/wireless/ti/wl1251/rx.c:40:\tmemset(status, 0, sizeof(struct ieee80211_rx_status));\ndrivers/net/wireless/ti/wl1251/rx.c-41-\n--\ndrivers/net/wireless/ti/wlcore/rx.c=45=static void wl1271_rx_status(struct wl1271 *wl,\n--\ndrivers/net/wireless/ti/wlcore/rx.c-49-{\ndrivers/net/wireless/ti/wlcore/rx.c:50:\tmemset(status, 0, sizeof(struct ieee80211_rx_status));\ndrivers/net/wireless/ti/wlcore/rx.c-51-\n--\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.c=216=void mac80211_hwsim_nan_rx(struct ieee80211_hw *hw,\n--\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.c-241-\tmemcpy(\u0026rx_status, IEEE80211_SKB_RXCB(skb),\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.c:242:\t       sizeof(struct ieee80211_rx_status));\ndrivers/net/wireless/virtual/mac80211_hwsim_nan.c-243-\n--\ninclude/net/mac80211.h=1777=struct ieee80211_rx_status {\n--\ninclude/net/mac80211.h-1817-\ninclude/net/mac80211.h:1818:static_assert(sizeof(struct ieee80211_rx_status) \u003c= sizeof_field(struct sk_buff, cb));\ninclude/net/mac80211.h-1819-\n--\nnet/mac80211/rx.c=5795=void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb)\n--\nnet/mac80211/rx.c-5798-\nnet/mac80211/rx.c:5799:\tBUILD_BUG_ON(sizeof(struct ieee80211_rx_status) \u003e sizeof(skb-\u003ecb));\nnet/mac80211/rx.c-5800-\n"
}

91/3 2026/09/15 18:18 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 83623
							output: 44
							thoughts: 752
						

Thoughts:
**Analyzing the mac80211 RX Path for `link_id` Usage**

Okay, so `struct ieee80211_rx_status` is stored internally in the kernel's mac80211 stack, specifically in the `skb->cb`.  Good, that's expected and the `static_assert` confirms that it fits within the control buffer. Always good to double-check those size constraints.

Now, I'm digging into `net/mac80211/rx.c` because I'm seeing a change concerning the `link_valid` field. Previously, it looked like drivers were setting `status->link_valid` and `status->link_id` directly. Now, it seems mac80211 sets the `link_id`.

Aha! `status->link_id` is set by mac80211 now: `status->link_id = rx->link_id < 0 ? 0 : rx->link_id;`. I need to make sure I understand the read side. My immediate question is: is `status->link_id` EVER read before it gets set? I need to check the code.

Okay, I see the code in `ieee80211_rx_handlers`, where a loop dequeues `skb`s from `frames`, and in this loop `status` is set to `IEEE80211_SKB_RXCB(skb)`. There's an `if` condition specifically addressing reordering and multi-link devices. The condition checks `ieee80211_vif_is_mld()` and compares `rx->link_id` to `status->link_id`. Now I see it. The use of `status->link_id` is specifically for multi-link devices and potentially different links, and to adjust rx->link_id based on a per-frame `link_id`.

I need to trace the source of the `frames` that are dequeued in `ieee80211_rx_handlers` - where do they come from and what is in them? Let's check where `ieee80211_rx_handlers` is called!



92/3 2026/09/15 18:18 tool grepper
0m Args:
{
  "Expression": "ieee80211_rx_handlers\\(",
  "PathPrefix": "net/mac80211/rx.c"
}

Results:
{
  "Output": "net/mac80211/rx.c=4191=static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx,\n--\nnet/mac80211/rx.c-4207-\nnet/mac80211/rx.c:4208:static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,\nnet/mac80211/rx.c-4209-\t\t\t\t  struct sk_buff_head *frames)\n--\nnet/mac80211/rx.c=4290=static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)\n--\nnet/mac80211/rx.c-4308-\nnet/mac80211/rx.c:4309:\tieee80211_rx_handlers(rx, \u0026reorder_release);\nnet/mac80211/rx.c-4310-\treturn;\n--\nnet/mac80211/rx.c=4403=void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)\n--\nnet/mac80211/rx.c-4439-\nnet/mac80211/rx.c:4440:\tieee80211_rx_handlers(\u0026rx, \u0026frames);\nnet/mac80211/rx.c-4441-}\n--\nnet/mac80211/rx.c=4443=void ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid,\n--\nnet/mac80211/rx.c-4519-\nnet/mac80211/rx.c:4520:\tieee80211_rx_handlers(\u0026rx, \u0026frames);\nnet/mac80211/rx.c-4521-\n"
}

93/3 2026/09/15 18:18 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 85463
							output: 41
							thoughts: 55
						

Thoughts:
Here's my take on those lines of code, seeing as I've worked with this before:

**Diving into the 802.11 Receiver Path**

Okay, so we're looking at `ieee80211_invoke_rx_handlers(rx)`. Right, that's the central function call for processing a received 802.11 frame. Let me just pull up those lines in `net/mac80211/rx.c`, specifically lines 4290 to 4315. I'll bet it's the section where they're iterating through all registered receive handlers, possibly checking some conditions. Most likely this is where the `rx` packet is getting passed to the various layers, perhaps doing some final checks on the frame or filtering it before it's sent up to the higher networking stack.

I'd expect to see a loop, probably using a `for_each_handler` macro or something similar, and a bunch of conditional calls, like `if (handler->filter(rx))`. I'm thinking they're probably deciding which handlers to call based on the frame type, MAC address, or perhaps some internal flags that the driver has set. Definitely keeping an eye out for potential races or lock contention in this section. I remember some past issues, so I'm keen to make sure it's thread-safe and robust. This part is critical for performance – any bottleneck here, and the entire RX path gets backed up. Let's see if my mental model is accurate.



94/3 2026/09/15 18:18 tool read-file
0m Args:
{
  "File": "net/mac80211/rx.c",
  "FirstLine": 4290,
  "LineCount": 30
}

Results:
{
  "Contents": "4290:\tstatic void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)\n4291:\t{\n4292:\t\tstruct sk_buff_head reorder_release;\n4293:\t\tieee80211_rx_result res;\n4294:\t\n4295:\t\t__skb_queue_head_init(\u0026reorder_release);\n4296:\t\n4297:\t#define CALL_RXH(rxh)\t\t\t\\\n4298:\t\tdo {\t\t\t\t\\\n4299:\t\t\tres = rxh(rx);\t\t\\\n4300:\t\t\tif (res != RX_CONTINUE)\t\\\n4301:\t\t\t\tgoto rxh_next;  \\\n4302:\t\t} while (0)\n4303:\t\n4304:\t\tCALL_RXH(ieee80211_rx_h_check_dup);\n4305:\t\tCALL_RXH(ieee80211_rx_h_check);\n4306:\t\n4307:\t\tieee80211_rx_reorder_ampdu(rx, \u0026reorder_release);\n4308:\t\n4309:\t\tieee80211_rx_handlers(rx, \u0026reorder_release);\n4310:\t\treturn;\n4311:\t\n4312:\t rxh_next:\n4313:\t\tieee80211_rx_handlers_result(rx, res);\n4314:\t\n4315:\t#undef CALL_RXH\n4316:\t}\n4317:\t\n4318:\tstatic bool\n4319:\tieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id)\n"
}

95/3 2026/09/15 18:18 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 86393
							output: 41
							thoughts: 73
						

Thoughts:
**Analyzing the Network Packet Reception Flow**

Okay, so I'm diving into this `mac80211` code again. The key question I need to answer is: precisely where in the receive path is that `skb` (socket buffer) actually prepared *before* it gets to `ieee80211_invoke_rx_handlers`? I need to understand the data's journey.

My initial thought is that the preparation probably occurs within `ieee80211_prepare_and_rx_handle`. It seems the most logical place to look. Let's get down to brass tacks and check the specifics.

Alright, time to crack open `net/mac80211/rx.c` and go straight to lines 5220 through 5265. That should give me a pretty clear picture of what's happening just upstream of the function I'm concerned with. Hopefully, this section holds the key to understanding the data manipulation. Time to analyze.



96/3 2026/09/15 18:18 tool read-file
0m Args:
{
  "File": "net/mac80211/rx.c",
  "FirstLine": 5220,
  "LineCount": 45
}

Results:
{
  "Contents": "5220:\t\n5221:\t\tif (!ieee80211_accept_frame(rx))\n5222:\t\t\treturn false;\n5223:\t\n5224:\t\tif (!consume) {\n5225:\t\t\tstruct skb_shared_hwtstamps *shwt;\n5226:\t\n5227:\t\t\trx-\u003eskb = skb_copy(skb, GFP_ATOMIC);\n5228:\t\t\tif (!rx-\u003eskb) {\n5229:\t\t\t\tif (net_ratelimit())\n5230:\t\t\t\t\twiphy_debug(local-\u003ehw.wiphy,\n5231:\t\t\t\t\t\t\"failed to copy skb for %s\\n\",\n5232:\t\t\t\t\t\tsdata-\u003ename);\n5233:\t\t\t\treturn true;\n5234:\t\t\t}\n5235:\t\n5236:\t\t\t/* skb_copy() does not copy the hw timestamps, so copy it\n5237:\t\t\t * explicitly\n5238:\t\t\t */\n5239:\t\t\tshwt = skb_hwtstamps(rx-\u003eskb);\n5240:\t\t\tshwt-\u003ehwtstamp = skb_hwtstamps(skb)-\u003ehwtstamp;\n5241:\t\n5242:\t\t\t/* Update the hdr pointer to the new skb for translation below */\n5243:\t\t\thdr = (struct ieee80211_hdr *)rx-\u003eskb-\u003edata;\n5244:\t\t}\n5245:\t\n5246:\t\tstatus = IEEE80211_SKB_RXCB(rx-\u003eskb);\n5247:\t\tstatus-\u003elink_id = rx-\u003elink_id \u003c 0 ? 0 : rx-\u003elink_id;\n5248:\t\n5249:\t\tif (ieee80211_is_s1g_beacon(hdr-\u003eframe_control)) {\n5250:\t\t\tieee80211_invoke_rx_handlers(rx);\n5251:\t\t\treturn true;\n5252:\t\t}\n5253:\t\n5254:\t\t/* Store a copy of the pre-translated link addresses for SW crypto */\n5255:\t\tif (unlikely(is_unicast_ether_addr(hdr-\u003eaddr1) \u0026\u0026\n5256:\t\t\t     !ieee80211_is_data(hdr-\u003eframe_control)))\n5257:\t\t\tmemcpy(rx-\u003elink_addrs, \u0026hdr-\u003eaddrs, 3 * ETH_ALEN);\n5258:\t\n5259:\t\tif (unlikely(rx-\u003esta \u0026\u0026 rx-\u003esta-\u003esta.mlo) \u0026\u0026\n5260:\t\t    is_unicast_ether_addr(hdr-\u003eaddr1) \u0026\u0026\n5261:\t\t    !ieee80211_is_probe_resp(hdr-\u003eframe_control) \u0026\u0026\n5262:\t\t    !ieee80211_is_beacon(hdr-\u003eframe_control)) {\n5263:\t\t\t/* translate to MLD addresses */\n5264:\t\t\tif (ether_addr_equal(link-\u003econf-\u003eaddr, hdr-\u003eaddr1))\n"
}

97/3 2026/09/15 18:18 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 87524
							output: 41
							thoughts: 246
						

Thoughts:
**Analyzing the 802.11 Receiver Path**

Okay, let's break this down. That `status = IEEE80211_SKB_RXCB(rx->skb)` assignment is interesting. It's *always* set in `ieee80211_prepare_and_rx_handle` – a fundamental part of the receive process. And I recall that `ieee80211_rx_reorder_ampdu` is responsible for buffering frames. Aha, and when those buffered frames are eventually released, *their* `status->link_id` has already been set, originating from when they first entered `ieee80211_prepare_and_rx_handle`.

But hold on, there are other callers of `ieee80211_rx_handlers`. I need to ensure the `link_id` is handled consistently across the board. Let me jump into `net/mac80211/rx.c` and specifically examine lines 4403 through 4521. First, I have to consider `ieee80211_release_reorder_timeout`. Lines 4405-4445 in `net/mac80211/rx.c` must be analyzed.



98/3 2026/09/15 18:18 tool read-file
0m Args:
{
  "File": "net/mac80211/rx.c",
  "FirstLine": 4405,
  "LineCount": 40
}

Results:
{
  "Contents": "4405:\t\tstruct sk_buff_head frames;\n4406:\t\tstruct ieee80211_rx_data rx = {\n4407:\t\t\t/* This is OK -- must be QoS data frame */\n4408:\t\t\t.security_idx = tid,\n4409:\t\t\t.seqno_idx = tid,\n4410:\t\t};\n4411:\t\tstruct tid_ampdu_rx *tid_agg_rx;\n4412:\t\tint link_id = -1;\n4413:\t\n4414:\t\t/* NOTE: the correct link STA for the frame is set later */\n4415:\t\tif (sta-\u003esta.valid_links)\n4416:\t\t\tlink_id = ffs(sta-\u003esta.valid_links) - 1;\n4417:\t\n4418:\t\tif (!ieee80211_rx_data_set_sta(\u0026rx, sta, link_id))\n4419:\t\t\treturn;\n4420:\t\n4421:\t\ttid_agg_rx = rcu_dereference(sta-\u003eampdu_mlme.tid_rx[tid]);\n4422:\t\tif (!tid_agg_rx)\n4423:\t\t\treturn;\n4424:\t\n4425:\t\t__skb_queue_head_init(\u0026frames);\n4426:\t\n4427:\t\tspin_lock(\u0026tid_agg_rx-\u003ereorder_lock);\n4428:\t\tieee80211_sta_reorder_release(sta-\u003esdata, tid_agg_rx, \u0026frames);\n4429:\t\tspin_unlock(\u0026tid_agg_rx-\u003ereorder_lock);\n4430:\t\n4431:\t\tif (!skb_queue_empty(\u0026frames)) {\n4432:\t\t\tstruct ieee80211_event event = {\n4433:\t\t\t\t.type = BA_FRAME_TIMEOUT,\n4434:\t\t\t\t.u.ba.tid = tid,\n4435:\t\t\t\t.u.ba.sta = \u0026sta-\u003esta,\n4436:\t\t\t};\n4437:\t\t\tdrv_event_callback(rx.local, rx.sdata, \u0026event);\n4438:\t\t}\n4439:\t\n4440:\t\tieee80211_rx_handlers(\u0026rx, \u0026frames);\n4441:\t}\n4442:\t\n4443:\tvoid ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid,\n4444:\t\t\t\t\t\t  u16 ssn, u64 filtered,\n"
}

99/3 2026/09/15 18:18 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 88782
							output: 41
							thoughts: 120
						

Thoughts:
**My Analysis of Frame Handling in the 802.11 Stack**

Okay, so I'm tracing the path of these frames, specifically those in `frames`. I remember that these frames are initially stashed in `tid_agg_rx->reorder_buf`. That buffer gets populated through the `ieee80211_rx_reorder_ampdu` function.  Going back a step further, `ieee80211_rx_reorder_ampdu` is called from `ieee80211_prepare_and_rx_handle`, where the `status->link_id` is actually initialized, which is critical for associating this packet with the correct link and reorder buffer. That's good to know.

I'm also curious about how filtered frames are handled, so I need to check out what's happening with `ieee80211_mark_rx_ba_filtered_frames` next. I need to understand how the filtering logic works, and how it interacts with the reordering process. This likely intersects with how those `link_id` values influence the overall frame reception.



100/3 2026/09/15 18:18 tool read-file
0m Args:
{
  "File": "net/mac80211/rx.c",
  "FirstLine": 4445,
  "LineCount": 40
}

Results:
{
  "Contents": "4445:\t\t\t\t\t\t  u16 received_mpdus)\n4446:\t{\n4447:\t\tstruct ieee80211_local *local;\n4448:\t\tstruct sta_info *sta;\n4449:\t\tstruct tid_ampdu_rx *tid_agg_rx;\n4450:\t\tstruct sk_buff_head frames;\n4451:\t\tstruct ieee80211_rx_data rx = {\n4452:\t\t\t/* This is OK -- must be QoS data frame */\n4453:\t\t\t.security_idx = tid,\n4454:\t\t\t.seqno_idx = tid,\n4455:\t\t};\n4456:\t\tint i, diff;\n4457:\t\n4458:\t\tif (WARN_ON(!pubsta || tid \u003e= IEEE80211_NUM_TIDS))\n4459:\t\t\treturn;\n4460:\t\n4461:\t\t__skb_queue_head_init(\u0026frames);\n4462:\t\n4463:\t\tsta = container_of(pubsta, struct sta_info, sta);\n4464:\t\n4465:\t\tlocal = sta-\u003esdata-\u003elocal;\n4466:\t\tWARN_ONCE(local-\u003ehw.max_rx_aggregation_subframes \u003e 64,\n4467:\t\t\t  \"RX BA marker can't support max_rx_aggregation_subframes %u \u003e 64\\n\",\n4468:\t\t\t  local-\u003ehw.max_rx_aggregation_subframes);\n4469:\t\n4470:\t\tif (!ieee80211_rx_data_set_sta(\u0026rx, sta, -1))\n4471:\t\t\treturn;\n4472:\t\n4473:\t\trcu_read_lock();\n4474:\t\ttid_agg_rx = rcu_dereference(sta-\u003eampdu_mlme.tid_rx[tid]);\n4475:\t\tif (!tid_agg_rx)\n4476:\t\t\tgoto out;\n4477:\t\n4478:\t\tspin_lock_bh(\u0026tid_agg_rx-\u003ereorder_lock);\n4479:\t\n4480:\t\tif (received_mpdus \u003e= IEEE80211_SN_MODULO \u003e\u003e 1) {\n4481:\t\t\tint release;\n4482:\t\n4483:\t\t\t/* release all frames in the reorder buffer */\n4484:\t\t\trelease = (tid_agg_rx-\u003ehead_seq_num + tid_agg_rx-\u003ebuf_size) %\n"
}

101/3 2026/09/15 18:18 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 89857
							output: 41
							thoughts: 63
						

Thoughts:
Here's my summary, as if I were thinking these thoughts:

**Diving Deep into the Packet Handling**

Alright, time to get into the nitty-gritty. I'm going to take a look at the `__ieee80211_rx_handle_packet` function. Let's see... yep, it's right here in `net/mac80211/rx.c`. Okay, let's approach this methodically. I'll read through `__ieee80211_rx_handle_packet` line by line, from the very beginning. I want to understand *exactly* how it's handling received packets. No shortcuts, I need a complete picture of its execution flow and every potential conditional branch. Let's get to work.



102/3 2026/09/15 18:18 tool read-file
0m Args:
{
  "File": "net/mac80211/rx.c",
  "FirstLine": 5340,
  "LineCount": 90
}

Results:
{
  "Contents": "5340:\t * This is the actual Rx frames handler. as it belongs to Rx path it must\n5341:\t * be called with rcu_read_lock protection.\n5342:\t */\n5343:\tstatic void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,\n5344:\t\t\t\t\t\t struct ieee80211_link_sta *link_pubsta,\n5345:\t\t\t\t\t\t struct sk_buff *skb,\n5346:\t\t\t\t\t\t struct list_head *list)\n5347:\t{\n5348:\t\tstruct ieee80211_local *local = hw_to_local(hw);\n5349:\t\tstruct ieee80211_sub_if_data *sdata;\n5350:\t\tstruct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);\n5351:\t\tstruct ieee80211_hdr *hdr;\n5352:\t\tstruct link_sta_info *link_sta;\n5353:\t\tstruct sta_info *sta;\n5354:\t\t__le16 fc;\n5355:\t\tstruct ieee80211_rx_data rx;\n5356:\t\tstruct rhlist_head *tmp;\n5357:\t\tbool rx_data_pending;\n5358:\t\tint err = 0;\n5359:\t\n5360:\t\tfc = ((struct ieee80211_hdr *)skb-\u003edata)-\u003eframe_control;\n5361:\t\tmemset(\u0026rx, 0, sizeof(rx));\n5362:\t\trx.skb = skb;\n5363:\t\trx.local = local;\n5364:\t\trx.list = list;\n5365:\t\n5366:\t\tif (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc))\n5367:\t\t\tI802_DEBUG_INC(local-\u003edot11ReceivedFragmentCount);\n5368:\t\n5369:\t\tif (ieee80211_is_mgmt(fc)) {\n5370:\t\t\t/* drop frame if too short for header */\n5371:\t\t\tif (skb-\u003elen \u003c ieee80211_hdrlen(fc))\n5372:\t\t\t\terr = -ENOBUFS;\n5373:\t\t\telse\n5374:\t\t\t\terr = skb_linearize(skb);\n5375:\t\t} else if (ieee80211_is_s1g_beacon(fc)) {\n5376:\t\t\tsize_t s1g_hdr_len = offsetof(struct ieee80211_ext,\n5377:\t\t\t\t\t\t      u.s1g_beacon.variable) +\n5378:\t\t\t\t\t     ieee80211_s1g_optional_len(fc);\n5379:\t\n5380:\t\t\tif (skb-\u003elen \u003c s1g_hdr_len)\n5381:\t\t\t\terr = -ENOBUFS;\n5382:\t\t\telse\n5383:\t\t\t\terr = skb_linearize(skb);\n5384:\t\t} else if (ieee80211_is_ext(fc)) {\n5385:\t\t\terr = -EINVAL;\n5386:\t\t} else {\n5387:\t\t\terr = !pskb_may_pull(skb, ieee80211_hdrlen(fc));\n5388:\t\t}\n5389:\t\n5390:\t\tif (err) {\n5391:\t\t\tdev_kfree_skb(skb);\n5392:\t\t\treturn;\n5393:\t\t}\n5394:\t\n5395:\t\thdr = (struct ieee80211_hdr *)skb-\u003edata;\n5396:\t\tieee80211_parse_qos(\u0026rx);\n5397:\t\tieee80211_verify_alignment(\u0026rx);\n5398:\t\n5399:\t\tif (unlikely(ieee80211_is_probe_resp(hdr-\u003eframe_control) ||\n5400:\t\t\t     ieee80211_is_beacon(hdr-\u003eframe_control) ||\n5401:\t\t\t     ieee80211_is_s1g_beacon(hdr-\u003eframe_control)))\n5402:\t\t\tieee80211_scan_rx(local, skb);\n5403:\t\n5404:\t\t/*\n5405:\t\t * RX of a data frame should only happen to existing stations.\n5406:\t\t * It is therefore more efficient to use the one provided by the driver\n5407:\t\t * or search based on the station's address.\n5408:\t\t *\n5409:\t\t * Note we will fall through and handle a spurious data frame in the\n5410:\t\t * same way as a management frame.\n5411:\t\t */\n5412:\t\tif (ieee80211_is_data(fc)) {\n5413:\t\t\tif (link_pubsta) {\n5414:\t\t\t\tsta = container_of(link_pubsta-\u003esta, struct sta_info,\n5415:\t\t\t\t\t\t   sta);\n5416:\t\t\t\tlink_sta = rcu_dereference(sta-\u003elink[link_pubsta-\u003elink_id]);\n5417:\t\n5418:\t\t\t\trx.sdata = sta-\u003esdata;\n5419:\t\t\t\tif (ieee80211_rx_data_set_link_sta(\u0026rx, link_sta) \u0026\u0026\n5420:\t\t\t\t    ieee80211_prepare_and_rx_handle(\u0026rx, skb, true))\n5421:\t\t\t\t\treturn;\n5422:\t\n5423:\t\t\t\tgoto out;\n5424:\t\t\t}\n5425:\t\n5426:\t\t\trx_data_pending = false;\n5427:\t\n5428:\t\t\t/* Search for stations on non-MLD interfaces */\n5429:\t\t\tfor_each_sta_info(local, hdr-\u003eaddr2, sta, tmp) {\n"
}

103/3 2026/09/15 18:18 llm kmsan-evaluator
1m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 91740
							output: 41
							thoughts: 36
						

Thoughts:
Okay, let's dive into the core of this function, `__ieee80211_rx_handle_packet` between lines 5430 and 5590. My immediate goal is to understand how this function is handling the incoming 802.11 packets. I need to grasp its logic, especially in relation to the overall receive path within the wireless driver. I'll be looking for key aspects like packet classification, any integrity checks that are performed, potential buffer management and how it interacts with the rest of the stack.



104/3 2026/09/15 18:19 tool read-file
0m Args:
{
  "File": "net/mac80211/rx.c",
  "FirstLine": 5430,
  "LineCount": 90
}

Results:
{
  "Contents": "5430:\t\t\t\tstruct ieee80211_link_data *link;\n5431:\t\n5432:\t\t\t\tif (ieee80211_vif_is_mld(\u0026sta-\u003esdata-\u003evif))\n5433:\t\t\t\t\tcontinue;\n5434:\t\n5435:\t\t\t\tlink = \u0026sta-\u003esdata-\u003edeflink;\n5436:\t\t\t\tif (!ieee80211_rx_valid_freq(status-\u003efreq, link))\n5437:\t\t\t\t\tcontinue;\n5438:\t\n5439:\t\t\t\tif (rx_data_pending) {\n5440:\t\t\t\t\tieee80211_prepare_and_rx_handle(\u0026rx, skb,\n5441:\t\t\t\t\t\t\t\t\tfalse);\n5442:\t\t\t\t\trx_data_pending = false;\n5443:\t\t\t\t}\n5444:\t\n5445:\t\t\t\trx.sdata = sta-\u003esdata;\n5446:\t\t\t\tif (!ieee80211_rx_data_set_link_sta(\u0026rx, \u0026sta-\u003edeflink))\n5447:\t\t\t\t\tcontinue;\n5448:\t\n5449:\t\t\t\trx_data_pending = true;\n5450:\t\t\t}\n5451:\t\n5452:\t\t\t/* And search stations on MLD interfaces */\n5453:\t\t\tfor_each_link_sta_info(local, hdr-\u003eaddr2, link_sta, tmp) {\n5454:\t\t\t\tstruct ieee80211_link_data *link;\n5455:\t\n5456:\t\t\t\tsta = link_sta-\u003esta;\n5457:\t\t\t\tsdata =\tsta-\u003esdata;\n5458:\t\t\t\tlink = rcu_dereference(sdata-\u003elink[link_sta-\u003elink_id]);\n5459:\t\n5460:\t\t\t\tif (!ieee80211_rx_valid_freq(status-\u003efreq, link))\n5461:\t\t\t\t\tcontinue;\n5462:\t\n5463:\t\t\t\tif (rx_data_pending) {\n5464:\t\t\t\t\tieee80211_prepare_and_rx_handle(\u0026rx, skb,\n5465:\t\t\t\t\t\t\t\t\tfalse);\n5466:\t\t\t\t\trx_data_pending = false;\n5467:\t\t\t\t}\n5468:\t\n5469:\t\t\t\trx.sdata = sta-\u003esdata;\n5470:\t\t\t\tif (!ieee80211_rx_data_set_link_sta(\u0026rx, link_sta))\n5471:\t\t\t\t\tcontinue;\n5472:\t\n5473:\t\t\t\trx_data_pending = true;\n5474:\t\t\t}\n5475:\t\n5476:\t\t\tif (rx_data_pending) {\n5477:\t\t\t\tif (ieee80211_prepare_and_rx_handle(\u0026rx, skb, true))\n5478:\t\t\t\t\treturn;\n5479:\t\n5480:\t\t\t\tgoto out;\n5481:\t\t\t}\n5482:\t\n5483:\t\t\t/* fall through, e.g. for spurious frame notification */\n5484:\t\t}\n5485:\t\n5486:\t\t/*\n5487:\t\t * This is a management frame (or a data frame without a station) and\n5488:\t\t * it will be delivered independent of whether a station exists,\n5489:\t\t * so iterate the interfaces.\n5490:\t\t */\n5491:\t\trx_data_pending = false;\n5492:\t\n5493:\t\t/* We expect the driver to provide frequency information */\n5494:\t\tif (WARN_ON_ONCE(!local-\u003eemulate_chanctx \u0026\u0026 !status-\u003efreq))\n5495:\t\t\tgoto out;\n5496:\t\n5497:\t\tlist_for_each_entry_rcu(sdata, \u0026local-\u003einterfaces, list) {\n5498:\t\t\tstruct ieee80211_link_data *link = NULL;\n5499:\t\n5500:\t\t\tif (!ieee80211_sdata_running(sdata))\n5501:\t\t\t\tcontinue;\n5502:\t\n5503:\t\t\tif (sdata-\u003evif.type == NL80211_IFTYPE_MONITOR ||\n5504:\t\t\t    sdata-\u003evif.type == NL80211_IFTYPE_AP_VLAN)\n5505:\t\t\t\tcontinue;\n5506:\t\n5507:\t\t\tlink_sta = NULL;\n5508:\t\t\tlink = NULL;\n5509:\t\n5510:\t\t\t/* Try to resolve the station */\n5511:\t\t\tif (!ieee80211_vif_is_mld(\u0026sdata-\u003evif)) {\n5512:\t\t\t\tsta = sta_info_get_bss(sdata, hdr-\u003eaddr2);\n5513:\t\n5514:\t\t\t\tif (sta) {\n5515:\t\t\t\t\tlink_sta = \u0026sta-\u003edeflink;\n5516:\t\t\t\t\tlink = \u0026sdata-\u003edeflink;\n5517:\t\t\t\t}\n5518:\t\t\t} else {\n5519:\t\t\t\tlink_sta = link_sta_info_get_bss(sdata, hdr-\u003eaddr2);\n"
}

105/3 2026/09/15 18:19 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 93296
							output: 41
							thoughts: 31
						

Thoughts:
Here's my attempt at summarizing the thought process, as if I were an expert:

**Diving into the MAC80211 RX Path**

Alright, time to dig into the heart of the matter – the receive path within `net/mac80211/rx.c`. I need to understand how the driver processes incoming frames, so I'm honing in on lines 5520 through 5585. This is likely where we find a critical piece of the puzzle, probably dealing with frame handling within the softirq context. I'm expecting to see logic that deals with initial checks, potentially buffer allocation or reuse if they're using a ring buffer structure, any potential frame type discriminations like data or management frames, and most importantly, the core of the work queues that feeds into the upper layers.

I'm particularly interested in how they're handling potential errors here. Is there robust error checking on the frame's integrity right here, or does that occur earlier in the process? Are we handling any potential race conditions with other receive processes, perhaps with the interrupt handler itself? Line numbers like these are the heart of where the action happens. I'm going to scan the code looking for important flags and variables that they use. The goal is to fully understand all of the steps that the system takes as it takes in a data frame.



106/3 2026/09/15 18:19 tool read-file
0m Args:
{
  "File": "net/mac80211/rx.c",
  "FirstLine": 5520,
  "LineCount": 65
}

Results:
{
  "Contents": "5520:\t\n5521:\t\t\t\tif (link_sta)\n5522:\t\t\t\t\tlink = rcu_dereference(sdata-\u003elink[link_sta-\u003elink_id]);\n5523:\t\t\t}\n5524:\t\n5525:\t\t\t/*\n5526:\t\t\t * If we found a STA and it has a valid link information, then\n5527:\t\t\t * RX using the station.\n5528:\t\t\t */\n5529:\t\t\tif (link_sta \u0026\u0026 link \u0026\u0026\n5530:\t\t\t    ieee80211_rx_valid_freq(status-\u003efreq, link)) {\n5531:\t\t\t\tif (rx_data_pending) {\n5532:\t\t\t\t\tieee80211_prepare_and_rx_handle(\u0026rx, skb, false);\n5533:\t\t\t\t\trx_data_pending = false;\n5534:\t\t\t\t}\n5535:\t\n5536:\t\t\t\t/* No valid_links check as we need to RX beacons */\n5537:\t\n5538:\t\t\t\trx.sdata = sdata;\n5539:\t\t\t\tif (ieee80211_rx_data_set_link_sta(\u0026rx, link_sta))\n5540:\t\t\t\t\trx_data_pending = true;\n5541:\t\n5542:\t\t\t\tcontinue;\n5543:\t\t\t}\n5544:\t\n5545:\t\t\t/* No station, try to resolve the link and RX */\n5546:\t\t\tif (ieee80211_vif_is_mld(\u0026sdata-\u003evif)) {\n5547:\t\t\t\tstruct ieee80211_chanctx_conf *conf;\n5548:\t\t\t\tbool found = false;\n5549:\t\n5550:\t\t\t\tfor_each_link_data_rcu(sdata, link) {\n5551:\t\t\t\t\tconf = rcu_dereference(link-\u003econf-\u003echanctx_conf);\n5552:\t\t\t\t\tif (!conf || !conf-\u003edef.chan)\n5553:\t\t\t\t\t\tcontinue;\n5554:\t\n5555:\t\t\t\t\tif (status-\u003efreq == conf-\u003edef.chan-\u003ecenter_freq) {\n5556:\t\t\t\t\t\tfound = true;\n5557:\t\t\t\t\t\tbreak;\n5558:\t\t\t\t\t}\n5559:\t\t\t\t}\n5560:\t\n5561:\t\t\t\tif (!found)\n5562:\t\t\t\t\tlink = \u0026sdata-\u003edeflink;\n5563:\t\t\t} else {\n5564:\t\t\t\tlink = \u0026sdata-\u003edeflink;\n5565:\t\t\t}\n5566:\t\n5567:\t\t\tif (rx_data_pending) {\n5568:\t\t\t\tieee80211_prepare_and_rx_handle(\u0026rx, skb, false);\n5569:\t\t\t\trx_data_pending = false;\n5570:\t\t\t}\n5571:\t\n5572:\t\t\trx.sdata = sdata;\n5573:\t\t\trx.local = sdata-\u003elocal;\n5574:\t\t\trx.link = link;\n5575:\t\t\trx.link_id = link-\u003elink_id;\n5576:\t\t\trx.sta = NULL;\n5577:\t\t\trx.link_sta = NULL;\n5578:\t\n5579:\t\t\trx_data_pending = true;\n5580:\t\t}\n5581:\t\n5582:\t\tif (rx_data_pending \u0026\u0026\n5583:\t\t    ieee80211_prepare_and_rx_handle(\u0026rx, skb, true))\n5584:\t\t\treturn;\n"
}

107/3 2026/09/15 18:19 llm kmsan-evaluator-compressor
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Reply:
### 1. Approaches Tried and Results

* **Diff Analysis**:
  * Inspected diff in `ath11k`, `ath12k`, `iwlwifi`, `mt76`, `rtw89`, `mac80211_hwsim`, `cfg80211`, and `mac80211`.
  * The patch modifies IEEE 802.11 MLO (Multi-Link Operation) frame handling across the RX and TX paths:
    * `struct ieee80211_rx_status`: Removed bitfield `link_valid:1`. Kept `link_id:4`. Link identification is shifted to be handled internally by `mac80211` based on frequency (`freq`) or resolved station links rather than relying on `status->link_valid` from hardware/driver.
    * Changed RX entry points (`ieee80211_rx_list`, `ieee80211_rx_napi`, driver wrappers) signature from accepting `struct ieee80211_sta *sta` to `struct ieee80211_link_sta *link_sta`.
    * Added `NL80211_ATTR_FRAME_NO_STA` netlink attribute (flag), `no_sta` boolean field to `struct cfg80211_mgmt_tx_params` and `struct cfg80211_rx_info`.
    * In `mac80211/rx.c`: Updated RX handlers, fast rx, defragmentation, and queueing to use `link_sta`. Handled per-link frame adjustments during reordering.

* **Userspace Leaks / Netlink Analysis (`cfg80211_rx_info` and `cfg80211_mgmt_tx_params`)**:
  * Checked `net/mac80211/rx.c:3980` (`ieee80211_rx_h_userspace_mgmt`): `struct cfg80211_rx_info info = { ... }` uses designated initializers (all non-specified fields zero-initialized, including padding and `.no_sta = !rx->sta`).
  * Checked `cfg80211_rx_mgmt_khz` and `cfg80211_rx_mgmt` in `include/net/cfg80211.h`: Both use designated initializers `struct cfg80211_rx_info info = { .freq = ... };`.
  * Checked `nl80211_send_mgmt`: Sends `NL80211_ATTR_FRAME_NO_STA` via `nla_put_flag(msg, NL80211_ATTR_FRAME_NO_STA)`. Flag attributes have zero payload length (no bytes copied, no padding leak).
  * Checked `struct cfg80211_mgmt_tx_params params`:
    * In `net/wireless/nl80211.c:14787` (`nl80211_tx_mgmt`): Designated initializer used, `params.no_sta = nla_get_flag(...)`.
    * In `drivers/net/wireless/ath/wil6210/debugfs.c:972`: Uses `memset(&params, 0, sizeof(params));`.

* **Uninitialized Memory in `rx_status` / `skb->cb` Analysis**:
  * Checked skb allocation in `iwlwifi/mld/rx.c` (`alloc_skb`): `skb->cb` is zeroed on skb allocation (`memset(skb->cb, 0, sizeof(skb->cb))`).
  * Checked `mt76/mac80211.c`: Explicit `memset(status, 0, sizeof(*status));` in `mt76_rx_convert`.
  * Checked `mac80211/rx.c`: `status->link_id` is explicitly assigned in `ieee80211_invoke_fast_rx` and `ieee80211_prepare_and_rx_handle`:
    `status->link_id = rx->link_id < 0 ? 0 : rx->link_id;`
  * Checked `ieee80211_rx_list`: `status->rx_flags = 0;`.

* **Logic / Array Index Bug Checking in `mac80211/rx.c`**:
  * Examined `ieee80211_rx_handlers` around line 4239:
    ```c
    if (ieee80211_vif_is_mld(&rx->sdata->vif) &&
        unlikely(rx->link_id != status->link_id)) {
        if (rx->sta) {
            rx->link_sta =
                rcu_dereference(rx->sta->link[rx->link_id]); // Note: uses rx->link_id before update
            if (!rx->link_sta)
                rx->link_sta = &rx->sta->deflink;
        }
        rx->link_id = status->link_id;
        rx->link = rcu_dereference(rx->sdata->link[status->link_id]);
    }
    ```
    * If `rx->link_id` is invalid or refers to the old link instead of `status->link_id`, or if `status->link_id` could be out of bounds, this is an array bounds or pointer logic issue (KASAN catches OOB/UAF/null deref), NOT an uninitialized memory read.

### 2. Current Hypotheses and KMSAN vs KASAN Applicability

* **KMSAN Relevance**:
  * Uninitialized memory reads / info-leaks:
    * All structs passed to userspace or across subsystem boundaries (`cfg80211_rx_info`, `cfg80211_mgmt_tx_params`) are initialized either via C99 designated initializers (`= { ... }`) or `memset`.
    * Netlink attribute added (`NL80211_ATTR_FRAME_NO_STA`) is a flag attribute (`NLA_FLAG`), transferring no data bytes or uninitialized padding to userspace.
    * Bitfield change in `ieee80211_rx_status` removed `link_valid:1`; `link_id:4` remains. Drivers zero `skb->cb` or `rx_status`, and `mac80211` explicitly sets `status->link_id` before processing handlers.
    * No uninitialized data sources or sinks created or exposed.
* **KASAN / Standard Sanitizers Relevance**:
  * The potential bugs in this patch are logic errors, incorrect array indexing (`rx->sta->link[rx->link_id]` vs `status->link_id`), NULL pointer dereferences (`rcu_dereference` returning NULL), or bounds issues with `link_id`.
  * These are strictly in the domain of KASAN, UBSAN, and standard kernel debugging facilities, not KMSAN.
* **Conclusion Hypothesis**:
  * `NeedsKMSAN = false`.

### 3. Critical Files, Code Snippets, and Details

* **`include/uapi/linux/nl80211.h`**:
  * Added `NL80211_ATTR_FRAME_NO_STA`.
* **`net/wireless/nl80211.c`**:
  * Policy entry: `[NL80211_ATTR_FRAME_NO_STA] = { .type = NLA_FLAG }`.
  * In `nl80211_send_mgmt`:
    ```c
    (info->no_sta && nla_put_flag(msg, NL80211_ATTR_FRAME_NO_STA))
    ```
* **`net/mac80211/rx.c`**:
  * Lines 3980-3987:
    ```c
    struct cfg80211_rx_info info = {
        .freq = ieee80211_rx_status_to_khz(status),
        .buf = rx->skb->data,
        .len = rx->skb->len,
        .link_id = rx->link_id,
        .have_link_id = rx->link_id >= 0,
        .no_sta = !rx->sta,
    };
    ```
  * Lines 5176 & 5247:
    ```c
    status = IEEE80211_SKB_RXCB(skb);
    status->link_id = rx->link_id < 0 ? 0 : rx->link_id;
    ```
  * Lines 4239-4251 (`ieee80211_rx_handlers`): Link adjustment during reordering.

### 4. Watchouts / Repetitive Loops to Avoid

* Do not re-grep generic terms like `link_id` across `net/mac80211/` (produces 4000+ matches).
* Do not re-check userspace copying of `cfg80211_rx_info`; verified that designated initializers ensure complete zeroing of stack variables and `nla_put_flag` sends no payload.
* Ready to proceed to calling `set-results` tool with `NeedsKMSAN=false` and appropriate justification.

Tokens:
							input: 69806
							output: 2186
							thoughts: 0
						

108/3 2026/09/15 18:19 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 52998
							output: 704
							thoughts: 0
						

109/3 2026/09/15 18:19 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch series updates IEEE 802.11 Multi-Link Operation (MLO) handling across wireless drivers (ath11k, ath12k, iwlwifi, mt76, rtw89, mac80211_hwsim) and mac80211/cfg80211. Specifically:\n1. It replaces passing `struct ieee80211_sta *pubsta` to `ieee80211_rx_napi`/`ieee80211_rx_list` with `struct ieee80211_link_sta *link_pubsta`.\n2. It removes the bitfield `link_valid:1` from `struct ieee80211_rx_status`, keeping `link_id:4`, and determines link IDs internally based on frequency or resolved link stations.\n3. It introduces the netlink attribute `NL80211_ATTR_FRAME_NO_STA` and corresponding `bool no_sta` flags in `struct cfg80211_mgmt_tx_params` and `struct cfg80211_rx_info`.\n4. It refactors frame resolution in `__ieee80211_rx_handle_packet` and link handling across A-MPDU reorder and fast-rx.\n\nEvaluation regarding KMSAN vs. KASAN:\n- No uninitialized memory leaks to userspace: `NL80211_ATTR_FRAME_NO_STA` is a flag attribute (`NLA_FLAG`) added via `nla_put_flag()` which carries zero payload bytes. The instances of `struct cfg80211_rx_info` populated in mac80211 and cfg80211 use designated C initializers (`struct cfg80211_rx_info info = { ... }`), ensuring all fields and padding are zero-initialized.\n- No uninitialized variable or memory reads: `struct ieee80211_rx_data` is explicitly zeroed via `memset(\u0026rx, 0, sizeof(rx))` in `__ieee80211_rx_handle_8023` and `__ieee80211_rx_handle_packet`. In skb receive paths, `skb-\u003ecb` / `rx_status` is either zeroed upon allocation or explicitly cleared via `memset(status, 0, ...)` by drivers, and `status-\u003elink_id` is assigned before handlers run (`status-\u003elink_id = rx-\u003elink_id \u003c 0 ? 0 : rx-\u003elink_id`).\n- Potential issues introduced by this patch involve pointer resolution logic, RCU dereferencing (`rcu_dereference(sta-\u003elink[...])`), array bounds, or NULL pointer dereferencing, all of which fall squarely within standard bug detectors (KASAN, UBSAN, NULL checks, LOCKDEP).\n\nTherefore, there is no risk of uninitialized memory usage or kernel information leaks, and a dedicated KMSAN fuzzing session is not warranted.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch series updates IEEE 802.11 Multi-Link Operation (MLO) handling across wireless drivers (ath11k, ath12k, iwlwifi, mt76, rtw89, mac80211_hwsim) and mac80211/cfg80211. Specifically:\n1. It replaces passing `struct ieee80211_sta *pubsta` to `ieee80211_rx_napi`/`ieee80211_rx_list` with `struct ieee80211_link_sta *link_pubsta`.\n2. It removes the bitfield `link_valid:1` from `struct ieee80211_rx_status`, keeping `link_id:4`, and determines link IDs internally based on frequency or resolved link stations.\n3. It introduces the netlink attribute `NL80211_ATTR_FRAME_NO_STA` and corresponding `bool no_sta` flags in `struct cfg80211_mgmt_tx_params` and `struct cfg80211_rx_info`.\n4. It refactors frame resolution in `__ieee80211_rx_handle_packet` and link handling across A-MPDU reorder and fast-rx.\n\nEvaluation regarding KMSAN vs. KASAN:\n- No uninitialized memory leaks to userspace: `NL80211_ATTR_FRAME_NO_STA` is a flag attribute (`NLA_FLAG`) added via `nla_put_flag()` which carries zero payload bytes. The instances of `struct cfg80211_rx_info` populated in mac80211 and cfg80211 use designated C initializers (`struct cfg80211_rx_info info = { ... }`), ensuring all fields and padding are zero-initialized.\n- No uninitialized variable or memory reads: `struct ieee80211_rx_data` is explicitly zeroed via `memset(\u0026rx, 0, sizeof(rx))` in `__ieee80211_rx_handle_8023` and `__ieee80211_rx_handle_packet`. In skb receive paths, `skb-\u003ecb` / `rx_status` is either zeroed upon allocation or explicitly cleared via `memset(status, 0, ...)` by drivers, and `status-\u003elink_id` is assigned before handlers run (`status-\u003elink_id = rx-\u003elink_id \u003c 0 ? 0 : rx-\u003elink_id`).\n- Potential issues introduced by this patch involve pointer resolution logic, RCU dereferencing (`rcu_dereference(sta-\u003elink[...])`), array bounds, or NULL pointer dereferencing, all of which fall squarely within standard bug detectors (KASAN, UBSAN, NULL checks, LOCKDEP).\n\nTherefore, there is no risk of uninitialized memory usage or kernel information leaks, and a dedicated KMSAN fuzzing session is not warranted.",
  "NeedsKMSAN": false
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)