wifi: mac80211: don't allow injecting frames wider than the chanctx

Frames injected on a monitor interface can carry a radiotap
field requesting a bandwidth, which mac80211 passes down to
the driver regardless of the the actual operational bandwidth.

If the bandwidth requested is too wide, that triggers a warning
in hwsim:

  WARN_ON(hwsim_get_chanwidth(bw) > hwsim_get_chanwidth(confbw))

Drop such frames entirely instead since they cannot be sent.

Assisted-by: LLM
Fixes: 646e76bb5d ("mac80211: parse VHT info in injected frames")
Reported-by: syzbot+435fdb053cf98bfa5778@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=435fdb053cf98bfa5778
Link: https://patch.msgid.link/20260908122838.201719-13-johannes@sipsolutions.net
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2026-09-08 14:28:12 +02:00
parent c1ba7f7f18
commit e14bf37bb2
3 changed files with 31 additions and 4 deletions

View File

@ -7638,11 +7638,14 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
*
* @skb: packet injected by userspace
* @dev: the &struct device of this 802.11 device
* @chandef: the channel definition the frame will be transmitted on, or
* %NULL to skip the bandwidth checks
*
* Return: %true if the radiotap header was parsed, %false otherwise
*/
bool ieee80211_parse_tx_radiotap(struct sk_buff *skb,
struct net_device *dev);
struct net_device *dev,
const struct cfg80211_chan_def *chandef);
/**
* struct ieee80211_noa_data - holds temporary data for tracking P2P NoA state

View File

@ -991,7 +991,7 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev,
/* reset flags and info before parsing radiotap header */
memset(info, 0, sizeof(*info));
if (!ieee80211_parse_tx_radiotap(skb, dev))
if (!ieee80211_parse_tx_radiotap(skb, dev, NULL))
return 0; /* doesn't matter, frame will be dropped */
len_rthdr = ieee80211_get_radiotap_len(skb->data);

View File

@ -2105,8 +2105,29 @@ static bool ieee80211_validate_radiotap_len(struct sk_buff *skb)
return true;
}
static bool ieee80211_rate_bw_usable(u16 rate_flags,
const struct cfg80211_chan_def *chandef)
{
int width;
if (!chandef)
return true;
if (rate_flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
width = 160;
else if (rate_flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
width = 80;
else if (rate_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
width = 40;
else
return true;
return width <= cfg80211_chandef_get_width(chandef);
}
bool ieee80211_parse_tx_radiotap(struct sk_buff *skb,
struct net_device *dev)
struct net_device *dev,
const struct cfg80211_chan_def *chandef)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_radiotap_iterator iterator;
@ -2280,6 +2301,9 @@ bool ieee80211_parse_tx_radiotap(struct sk_buff *skb,
struct ieee80211_supported_band *sband =
local->hw.wiphy->bands[info->band];
if (!ieee80211_rate_bw_usable(rate_flags, chandef))
return false;
info->control.flags |= IEEE80211_TX_CTRL_RATE_INJECT;
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
@ -2479,7 +2503,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
* selected chandef above to accurately set injection rates and
* retransmissions.
*/
if (!ieee80211_parse_tx_radiotap(skb, dev))
if (!ieee80211_parse_tx_radiotap(skb, dev, chandef))
goto fail_rcu;
/* remove the injection radiotap header */