S1G beacons are extension frames, so ieee80211_hdrlen() only guarantees the extension header before the generic RX path starts dispatching the frame. The RX path can then reach helpers and interface handling code that read regular 802.11 header address fields, which are not present at those offsets in an S1G beacon. Pull the complete S1G beacon fixed header, including optional fixed fields indicated by frame control, before generic RX dispatch. Also make ieee80211_get_bssid() length-safe for S1G beacons and avoid regular-header address reads for S1G frames in accept/interface/MLO address handling. Skip extension frames in duplicate detection for the same reason, since that path consumes the regular sequence-control field. Fixes: 09a740ce352e ("mac80211: receive and process S1G beacons") Cc: stable@vger.kernel.org Signed-off-by: Zhao Li --- include/linux/ieee80211.h | 13 +++++++++++++ net/mac80211/rx.c | 33 ++++++++++++++++++++++++++++----- net/mac80211/util.c | 3 +++ 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 23f9df9be8372..baee81fbb4a79 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2855,6 +2855,19 @@ struct ieee80211_tbtt_info_ge_11 { #include "ieee80211-p2p.h" #include "ieee80211-nan.h" +/** + * ieee80211_s1g_beacon_min_len - minimum length of an S1G beacon frame + * @fc: frame control bytes in little-endian byteorder + * + * Return: the minimum frame length containing the fixed S1G beacon fields and + * optional fixed fields indicated in the S1G beacon frame control. + */ +static inline size_t ieee80211_s1g_beacon_min_len(__le16 fc) +{ + return offsetof(struct ieee80211_ext, u.s1g_beacon.variable) + + ieee80211_s1g_optional_len(fc); +} + /** * ieee80211_check_tim - check if AID bit is set in TIM * @tim: the TIM IE diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 3fb40449c6c5c..2e6d0ce8509e4 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1526,6 +1526,9 @@ ieee80211_rx_h_check_dup(struct ieee80211_rx_data *rx) if (status->flag & RX_FLAG_DUP_VALIDATED) return RX_CONTINUE; + if (ieee80211_is_ext(hdr->frame_control)) + return RX_CONTINUE; + /* * Drop duplicate 802.11 retransmissions * (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery") @@ -4487,12 +4490,17 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) struct ieee80211_hdr *hdr = (void *)skb->data; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); u8 *bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type); - bool multicast = is_multicast_ether_addr(hdr->addr1) || - ieee80211_is_s1g_beacon(hdr->frame_control); + bool s1g = ieee80211_is_s1g_beacon(hdr->frame_control); + bool multicast; static const u8 nan_network_id[ETH_ALEN] __aligned(2) = { 0x51, 0x6F, 0x9A, 0x01, 0x00, 0x00 }; + if (s1g) + return sdata->vif.type == NL80211_IFTYPE_STATION && bssid; + + multicast = is_multicast_ether_addr(hdr->addr1); + switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: if (!bssid && !sdata->u.mgd.use_4addr) @@ -5175,11 +5183,13 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx, } /* Store a copy of the pre-translated link addresses for SW crypto */ - if (unlikely(is_unicast_ether_addr(hdr->addr1) && + if (unlikely(!ieee80211_is_s1g_beacon(hdr->frame_control) && + is_unicast_ether_addr(hdr->addr1) && !ieee80211_is_data(hdr->frame_control))) memcpy(rx->link_addrs, &hdr->addrs, 3 * ETH_ALEN); if (unlikely(rx->sta && rx->sta->sta.mlo) && + !ieee80211_is_s1g_beacon(hdr->frame_control) && is_unicast_ether_addr(hdr->addr1) && !ieee80211_is_probe_resp(hdr->frame_control) && !ieee80211_is_beacon(hdr->frame_control)) { @@ -5260,23 +5270,30 @@ static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx, { struct link_sta_info *link_sta; struct ieee80211_hdr *hdr = (void *)skb->data; + u8 *sta_addr = hdr->addr2; struct sta_info *sta; int link_id = -1; + if (ieee80211_is_s1g_beacon(hdr->frame_control)) { + sta_addr = ieee80211_get_bssid(hdr, skb->len, rx->sdata->vif.type); + if (!sta_addr) + return false; + } + /* * 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); + link_sta = link_sta_info_get_bss(rx->sdata, sta_addr); 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); + sta = sta_info_get_bss(rx->sdata, sta_addr); if (status->link_valid) { link_id = status->link_id; } else if (ieee80211_vif_is_mld(&rx->sdata->vif) && @@ -5347,6 +5364,12 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, return; } + if (ieee80211_is_s1g_beacon(fc) && + !pskb_may_pull(skb, ieee80211_s1g_beacon_min_len(fc))) { + dev_kfree_skb(skb); + return; + } + hdr = (struct ieee80211_hdr *)skb->data; ieee80211_parse_qos(&rx); ieee80211_verify_alignment(&rx); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 2529b01e2cd55..5bc719222a87d 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -73,6 +73,9 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, if (ieee80211_is_s1g_beacon(fc)) { struct ieee80211_ext *ext = (void *) hdr; + if (len < offsetofend(struct ieee80211_ext, u.s1g_beacon.sa)) + return NULL; + return ext->u.s1g_beacon.sa; } -- 2.50.1 (Apple Git-155)