Linux kernel crash report

Origin: LKML email — 20260502125824.425d7159@bongani-mini.home.org.za

Root cause: mt7921_channel_switch_rx_beacon() dereferences dev->new_ctx without a NULL guard; new_ctx can be NULL when the channel context was removed before the queued beacon is processed.

Key elements

Field Value Implication
MSGID <20260502125824.425d7159@bongani-mini.home.org.za>
MSGID_URL 20260502125824.425d7159@bongani-mini.home.org.za
BUG_TYPE BUG: kernel NULL pointer dereference, address: 0000000000000000 NULL pointer dereference at address 0x0
UNAME 7.1.0-rc1+ Mainline linux-next / rc1 build
BUILD #4 PREEMPT(full)
DISTRO mainline No distro-specific debug packages available
PROCESS kworker/u64:1 Kernel workqueue worker thread
PID 6710
CPU 7
UID 0
TAINT Not tainted
HARDWARE Micro Computer (HK) Tech Limited EliteMini Series/F7BSI
BIOS 1.08 11/05/2024
WORKQUEUE events_unbound cfg80211_wiphy_work [cfg80211]
RIP mt7921_channel_switch_rx_beacon+0x1f/0x100 [mt7921_common] Crash site in mt7921 WiFi driver
CR2 0000000000000000 Faulting address is NULL
SOURCEDIR oops-workdir/linux Checked out at tag v7.1-rc1 (commit 254f49634ee16)
HEAD_COMMIT 254f49634ee16 Tag v7.1-rc1

Kernel modules

Module Flags Backtrace Location Flag Implication
mt7921_common Y drivers/net/wireless/mediatek/mt76/mt7921/
mac80211 Y net/mac80211/
cfg80211 Y net/wireless/
joydev
uinput
mptcp_diag
xsk_diag
tcp_diag
udp_diag
raw_diag
inet_diag
unix_diag
af_packet_diag
netlink_diag
sd_mod
scsi_mod
scsi_common
ccm
snd_seq_dummy
snd_hrtimer
snd_seq
snd
snd_pci_acp5x
snd_rn_pci_acp3x
irqbypass
aesni_intel
snd_acp_config
gf128mul
snd_soc_acpi
rapl
ecdh_generic
pcspkr
k10temp
amd_pmc
snd_pci_acp3x
soundcore
button
evdev
rfkill
libarc4
aead
msr

Backtrace

# Address Function Offset Size Context Module Source location
0 mt7921_channel_switch_rx_beacon 0x1f 0x100 Task mt7921_common drivers/net/wireless/mediatek/mt76/mt7921/main.c:1511
1 ieee80211_sta_process_chanswitch 0x67c 0xee0 Task mac80211 net/mac80211/mlme.c:2855
2 ieee80211_rx_mgmt_beacon 0x842 0x22a0 Task mac80211 net/mac80211/mlme.c:7570
3 ieee80211_sta_rx_queued_mgmt 0xa7 0xbb0 Task mac80211 net/mac80211/mlme.c:8345
4 ieee80211_iface_work 0x62e 0x890 Task mac80211 net/mac80211/iface.c:1826
5 cfg80211_wiphy_work 0x1ee 0x280 Task cfg80211
6 process_scheduled_works 0x180 0x680 Task
7 worker_thread 0x1aa 0x450 Task
8 kthread 0x181 0x1e0 Task
9 ret_from_fork 0x405 0x600 Task
10 ret_from_fork_asm 0x11 0x20 Task

CPU Registers

Register Value Note
RAX 0000000000000000 NULL — value loaded from dev->new_ctx
RBX ffff91cae1eb09e0
RCX 0000000000000000
RDX ffffb75fa1993b20
RSI ffff91ca84badfe8
RDI ffff91cae1eb09e0
RBP ffff91ca84bacac0
R08 0000000000000001
R09 0000000000000001
R10 ffff91ca8ba56128
R11 ffff91cae1eb0518
R12 0000000000000000
R13 0000000000000000
R14 ffffb75fa1993b60
R15 ffff91cae1eb09e0
RSP ffffb75fa1993af0
EFLAGS 00010202
CS 0010
DS 0000
ES 0000
CR0 0000000080050033
CR2 0000000000000000 Faulting address (NULL dereference)
CR3 0000000562a38000
CR4 0000000000f50ef0
PKRU 55555554
FS 0000000000000000(0000)
GS ffff91d18ebde000(0000)
knlGS 0000000000000000

Code bytes (raw):

12 3d 00 eb 9a 66 0f 1f 44 00 00 f3 0f 1e fa 0f 1f 44 00 00
48 8b 47 58 48 ff 05 ec 15 3d 00 48 8b 40 08 48 8b 80 80 9c
00 00 <48> 8b 08 48 39 4a 10 74 0c 48 ff 05 81 02 3d 00 e9
f7 f4 74 ea 53

Crash instruction (marked <48> 8b 08): mov rcx, [rax] — REX.W + MOV r64,r/m64; operand [rax] with RAX=0x0 causes the NULL dereference.

Backtrace source code

0. mt7921_channel_switch_rx_beacon — crash site (drivers/net/wireless/mediatek/mt76/mt7921/main.c:1511)

drivers/net/wireless/mediatek/mt76/mt7921/main.c @ v7.1-rc1

   1503  static void mt7921_channel_switch_rx_beacon(struct ieee80211_hw *hw,
   1504                         struct ieee80211_vif *vif,
   1505                         struct ieee80211_channel_switch *chsw)
   1506  {
   1507     struct mt792x_dev *dev = mt792x_hw_dev(hw);
   1508     struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
   1509     u16 beacon_interval = vif->bss_conf.beacon_int;
   1510  
   1511     if (cfg80211_chandef_identical(&chsw->chandef,
   1512                        &dev->new_ctx->def) &&
   1513                        chsw->count) {
   1514         mod_timer(&mvif->csa_timer,
   1515               TU_TO_EXP_TIME(beacon_interval * chsw->count));
   1516     }
   1517  }

dev->new_ctx is struct ieee80211_chanctx_conf *new_ctx (field in struct mt792x_dev, declared in drivers/net/wireless/mediatek/mt76/mt792x.h:264). RAX=0x0 corresponds to dev->new_ctx being NULL.

1. ieee80211_sta_process_chanswitchnet/mac80211/mlme.c:2855

net/mac80211/mlme.c @ v7.1-rc1

2854  static void
2855  ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link,
2856                 u64 timestamp, u32 device_timestamp,
2857                 struct ieee802_11_elems *full_elems,
2858                 struct ieee802_11_elems *csa_elems,
2859                 enum ieee80211_csa_source source)
2860  {
2861    struct ieee80211_sub_if_data *sdata = link->sdata;
2862    struct ieee80211_local *local = sdata->local;
2863    struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
2864    struct ieee80211_chanctx *chanctx = NULL;
2865    struct ieee80211_chanctx_conf *conf;
2866    struct ieee80211_csa_ie csa_ie = {};
2867    struct ieee80211_channel_switch ch_switch = {
2868        .link_id = link->link_id,
2869        .timestamp = timestamp,
2870        .device_timestamp = device_timestamp,
2871    };
2872    u32 csa_time_tu;
2873    ktime_t now;
2874    int res;
2875  
2876    lockdep_assert_wiphy(local->hw.wiphy);

The call to drv_channel_switch_rx_beacon() is at line 2940:

2940            drv_channel_switch_rx_beacon(sdata, &ch_switch);
2941            return;

drv_channel_switch_rx_beacon() is defined in net/mac80211/driver-ops.h:1242 and calls local->ops->channel_switch_rx_beacon(), which dispatches to mt7921_channel_switch_rx_beacon.

What-how-where analysis

What

mt7921_channel_switch_rx_beacon() crashes at line 1511–1512 when dev->new_ctx is NULL. The expression &dev->new_ctx->def unconditionally dereferences the new_ctx pointer; RAX=0x0 in the register dump confirms dev->new_ctx is NULL at the time of the crash. The CPU faults on the mov rcx, [rax] instruction at offset +0x1f.

drivers/net/wireless/mediatek/mt76/mt7921/main.c @ v7.1-rc1

   1503  static void mt7921_channel_switch_rx_beacon(struct ieee80211_hw *hw,
   1504                         struct ieee80211_vif *vif,
   1505                         struct ieee80211_channel_switch *chsw)
   1506  {
   1507     struct mt792x_dev *dev = mt792x_hw_dev(hw);
   1508     struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
   1509     u16 beacon_interval = vif->bss_conf.beacon_int;
   1510  
1511     if (cfg80211_chandef_identical(&chsw->chandef,
1512                        &dev->new_ctx->def) &&   // ← RAX=0x0: dev->new_ctx is NULL
   1513                        chsw->count) {
   1514         mod_timer(&mvif->csa_timer,
   1515               TU_TO_EXP_TIME(beacon_interval * chsw->count));
   1516     }
   1517  }

dev->new_ctx is struct ieee80211_chanctx_conf *new_ctx declared in drivers/net/wireless/mediatek/mt76/mt792x.h:264.

How

Negative howdev->new_ctx is NULL because the channel context was already removed before this workqueue callback executed.

new_ctx lifecycle in the mt7921 driver:

The call path to the crash:

cfg80211_wiphy_work [workqueue]
  → ieee80211_iface_work
    → ieee80211_sta_rx_queued_mgmt
      → ieee80211_rx_mgmt_beacon
        → ieee80211_sta_process_chanswitch
          → drv_channel_switch_rx_beacon       (driver-ops.h:1242)
            → mt7921_channel_switch_rx_beacon  ← CRASH

ieee80211_sta_process_chanswitch() calls drv_channel_switch_rx_beacon() at net/mac80211/mlme.c:2940 when: - link->conf->csa_active is true - The beacon source is IEEE80211_CSA_SOURCE_BEACON - link->u.mgd.csa.waiting_bcn is false and the CSA IE parse result is zero

The mac80211 layer does not ensure dev->new_ctx is non-NULL before invoking the driver callback — that invariant is the driver’s responsibility.

Race / ordering scenario: 1. Connection is established; mt7921_add_chanctx() sets dev->new_ctx. 2. The AP sends a Channel Switch Announcement (CSA) beacon; it is queued. 3. The connection starts tearing down; mt7921_remove_chanctx() sets dev->new_ctx = NULL. 4. The workqueue dequeues the beacon and calls mt7921_channel_switch_rx_beacon() with dev->new_ctx == NULL. 5. Crash.

The fix is a simple NULL guard at the top of the function, matching the pattern used in mt7925_csa_work() (mt7925/main.c:2325):

if (!dev->new_ctx)
    return;

Note: mt7925_channel_switch_rx_beacon() (mt7925/main.c) has the same latent bug — it also dereferences dev->new_ctx->def without a NULL check. Both functions should be fixed.

Where

Bug introduced by: commit 8aa2f59260eb6 (“wifi: mt76: mt7921: introduce CSA support”, 2024-11-07, Leon Yen / Ming Yen Hsieh)

That commit added mt7921_channel_switch_rx_beacon() without the NULL guard. At the time the analogous mt7925 channel_switch_rx_beacon function also lacked the guard; the guard in mt7925 was added only in mt7925_csa_work().

No upstream fix for this specific crash has been found in the v7.1-rc1 tree or in lore search results.

Fix: Add if (!dev->new_ctx) return; to both mt7921_channel_switch_rx_beacon() and mt7925_channel_switch_rx_beacon(). See report.patch.

Bug introduction

Commit Date Author Subject
8aa2f59260eb6 2024-11-07 Leon Yen / Ming Yen Hsieh wifi: mt76: mt7921: introduce CSA support

The mt7921_channel_switch_rx_beacon() function was introduced in this commit without a NULL guard on dev->new_ctx. The function was modelled after the mt7925 implementation but the mt7925 csa_work guard (if (!dev->new_ctx) return;) was not carried over to either driver’s channel_switch_rx_beacon callback.

Fact Check

⚠️ Line number discrepancy: Prose references to function definition line numbers are inaccurate. Report claims mt7921_add_chanctx() at main.c:1345 but actual location is line 1372 (off by +27). Similarly, mt7921_remove_chanctx() is claimed at line 1386 but actually at line 1382 (off by -4). Verify these line numbers when implementing fixes.