Re: [PATCH v2 1/1] wifi: mac80211: fix monitor mode frame capture for real chanctx drivers

From: Johannes Berg

Date: Mon May 18 2026 - 02:44:47 EST


On Sun, 2026-05-17 at 23:40 -0700, Devin Wittmayer wrote:
> From: 傅继晗 <fjhhz1997@xxxxxxxxx>
>
> Commit d594cc6f2c58 ("wifi: mac80211: restore non-chanctx injection
> behaviour") restored the monitor injection fallback for drivers using
> chanctx emulation but explicitly deferred the harder case of drivers
> that transitioned to real chanctx ops. mt76 falls in that category
> and still drops every injected frame when monitor coexists with
> another interface.
>
> When the monitor has no chanctx of its own and exactly one chanctx is
> in flight, fall back to that one. Otherwise refuse: picking
> arbitrarily across multiple chanctxs would inject onto an unrelated
> channel.
>
> Reran the airgeddon evil-twin flow (hostapd AP + coexisting monitor
> VIF on the same phy + aireplay-ng deauth from the monitor) against
> this patch on mt7921e PCIe and mt7921u USB, across both 2.4 GHz and
> 5 GHz, and again on a Kali Linux VM with MT7921U USB-passthrough as
> the closest match to the original reporter's setup. None of those
> reproduced the hang reported against the earlier attempt at the same
> fix (<20251216111909.25076-2-johannes@xxxxxxxxxxxxxxxx>) or against
> v1 on lore in March 2026.
>
> Cc: stable@xxxxxxxxxxxxxxx # 6.9+
> Reported-by: Oscar Alfonso Diaz <oscar.alfonso.diaz@xxxxxxxxx>
> Tested-by: Devin Wittmayer <lucid_duck@xxxxxxxxxxxxx>
> Fixes: 0a44dfc07074 ("wifi: mac80211: simplify non-chanctx drivers")
> Link: https://github.com/morrownr/USB-WiFi/issues/682
> Signed-off-by: 傅继晗 <fjhhz1997@xxxxxxxxx>
> Signed-off-by: Devin Wittmayer <lucid_duck@xxxxxxxxxxxxx>
> ---
> net/mac80211/tx.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
> --- a/net/mac80211/tx.c
> +++ b/net/mac80211/tx.c
> @@ -2402,6 +2402,10 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
> chandef = &chanctx_conf->def;
> else if (local->emulate_chanctx)
> chandef = &local->hw.conf.chandef;
> + else if (list_is_singular(&local->chanctx_list))
> + chandef = &list_first_entry(&local->chanctx_list,
> + struct ieee80211_chanctx,
> + list)->conf.def;


Quoting include/linux/rculist.h:

* Where are list_empty_rcu() and list_first_entry_rcu()?
*
* They do not exist because they would lead to subtle race conditions:
*
* if (!list_empty_rcu(mylist)) {
* struct foo *bar = list_first_entry_rcu(mylist, struct foo, list_member);
* do_something(bar);
* }
*
* The list might be non-empty when list_empty_rcu() checks it, but it
* might have become empty by the time that list_first_entry_rcu() rereads
* the ->next pointer, which would result in a SEGV.
*
* When not using RCU, it is OK for list_first_entry() to re-read that
* pointer because both functions should be protected by some lock that
* blocks writers.
*
* When using RCU, list_empty() uses READ_ONCE() to fetch the
* RCU-protected ->next pointer and then compares it to the address of the
* list head. However, it neither dereferences this pointer nor provides
* this pointer to its caller. Thus, READ_ONCE() suffices (that is,
* rcu_dereference() is not needed), which means that list_empty() can be
* used anywhere you would want to use list_empty_rcu(). Just don't
* expect anything useful to happen if you do a subsequent lockless
* call to list_first_entry_rcu()!!!
*
* See list_first_or_null_rcu for an alternative.

johannes