mt7996: NULL deref in mac80211 restart work on failed MCU init (WED disabled)
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 48/100
Research direction
Read mt7996_mac_full_reset(), mt7996_mac_reset_work(), and the hw_init_done guard in mt7996_reset(); the issue identifies these entry points but no test file. Check whether the failing-probe path can call ieee80211_restart_hw() before ieee80211_register_hw(), and compare the lifecycle behavior with commit 3def033. Done means the reset path cannot queue restart work against an unregistered device; validate with a failed MCU-init/probe scenario.
Written by the indexing model from the issue text.
Description
Sibling of #1147 — same board and trigger, but a different crash, and it reproduces with the #1147 workaround (WED disabled) already applied.
Summary
On a Banana Pi BPI-R4 (MT7988A + BE14 / MT7996), with WED disabled, a failed
MCU firmware-patch download on a warm reboot ends in a kernel NULL-pointer Oops
in mac80211, not in the mt76 WED path. The driver's full-reset handler calls
ieee80211_restart_hw() even though the device never finished probing (probe
later returns -EAGAIN), so ieee80211_restart_work runs against a hw that was
never registered and dereferences a NULL local->workqueue
(flush_workqueue(NULL) -> fault at 0x20 = offsetof(workqueue_struct, mutex)).
This is a sibling of the WED crash reported in #1147 (same trigger, same
board, same firmware timeout) but a different crash signature: #1147 faults
at 0x58 in mt76_dma_queue_reset() on a WED queue with q->wed->ops == NULL;
this one faults at 0x20 in mac80211's restart work with WED off.
Environment
- Board: Banana Pi BPI-R4 (2x SFP+), MT7988A, BE14 card (MT7996), hif2 on PCI
domain 1 - OpenWrt 25.12.5, kernel 6.12.94 (backports from v6.18.26)
- out-of-tree openwrt/mt76 snapshot
39c960c3(2026-03-19); MT7996 firmware
build 20251226 mt7996e wed_enable=N— WED disabled./sys/module/mt7996e/parameters/wed_enable
=N;/etc/modules.d/mt7996eis a plainmt7996ewith no options. No
attaching wed device ...lines appear at boot (contrast with #1147, where
they did).- Happened after several warm reboots in a short window. A cold boot (power
off) recovered. Same as the #1147 trigger.
What changed since #1147
In #1147 the fix we applied on our side was to disable WED. That removed the
0x58 WED-path crash — but a different crash now appears on the exact same
MCU-timeout trigger. So disabling WED is not a complete workaround for this
class of failure; it just moves the crash from the mt76 WED path into mac80211's
hardware-restart path.
dmesg (relevant excerpt, from pstore)
[ 17.221620] mt7996e 0000:01:00.0: HW/SW Version: 0x8a108a10, Build Time: 20251226095555a
[ 17.342597] sfp sfp1: module FS XGS-SFP-ONT-MACI rev 0001 sn <redacted> dc 260901
[ 17.371460] sfp sfp2: module OEM SFP-2.5G-T-R-RM rev 1.0 sn <redacted> dc 250408
[ 22.311306] mt7996e 0000:01:00.0: Message 00000007 (seq 13) timeout
[ 22.317584] mt7996e 0000:01:00.0: Failed to start patch
[ 22.322814] mt7996e 0000:01:00.0: Failed to release patch semaphore
[ 27.431300] mt7996e 0000:01:00.0: Message 00000010 (seq 15) timeout
[ 27.437577] mt7996e 0000:01:00.0: Failed to get patch semaphore
[ 32.551298] mt7996e 0000:01:00.0: Message 00000010 (seq 1) timeout
[ 32.557483] mt7996e 0000:01:00.0: Failed to get patch semaphore
... (8 more identical "Failed to get patch semaphore", ~5.12 s apart) ...
[ 73.511308] mt7996e 0000:01:00.0: Message 00000010 (seq 9) timeout
[ 73.517498] mt7996e 0000:01:00.0: Failed to get patch semaphore
[ 73.523449] mt7996e 0000:01:00.0: chip full reset failed
[ 73.528759] ieee80211 phy0: Hardware restart was requested
[ 73.534265] Unable to handle kernel read from unreadable memory at virtual address 0000000000000020
[ 73.543377] mt7996e 0000:01:00.0: probe with driver mt7996e failed with error -11
[ 73.546084] ESR = 0x0000000096000005
[ 73.562619] EC = 0x25: DABT (current EL), IL = 32 bits
[ 73.568798] FSC = 0x05: level 1 translation fault
[ 73.609431] Internal error: Oops: 0000000096000005 [#1] SMP
[ 73.667324] CPU: 2 UID: 0 PID: 242 Comm: kworker/2:7 Tainted: G O 6.12.94 #0
[ 73.684702] Workqueue: events_freezable 0xffffffc078efe180 [mac80211@...+0x7c000]
[ 73.700260] pc : 0xffffffc08094e28c
[ 73.703738] lr : 0xffffffc08005850c
[ 73.724771] x26: ffffff80c114a280 x25: ffffff80c114a2c0 x24: 0000000000000020
[ 73.781774] x2 : ffffff80c18b0000 x1 : 0000000000000000 x0 : 0000000000000020
[ 73.781774] Call trace:
[ 73.784210] 0xffffffc08094e28c
[ 73.787341] 0xffffffc078efe1b4 [mac80211@...+0x7c000]
[ 73.793597] 0xffffffc080059ef4 (process_one_work)
[ 73.796727] 0xffffffc08005ab8c (worker_thread)
[ 73.799857] 0xffffffc080061b1c (kthread)
[ 73.802988] 0xffffffc0800145a0 (ret_from_fork)
[ 73.806120] Code: d503201f d2800001 d5384102 f9800011 (c85ffc03)
[ 73.829678] Kernel panic - not syncing: Oops: Fatal exception
(Kernel has no kallsyms; module-relative addresses only. The bare 0xffffffc080...
addresses are vmlin.)
Root-cause hypothesis
- The MCU never answers the firmware patch download (
Message 7 ... Failed to start patch). The driver enters a full reset and retries the whole firmware
load — the tenFailed to get patch semaphorelines, ~5.12 s apart, are the
for (i = 0; i < 10; i++) mt7996_mac_restart()loop in
mt7996_mac_full_reset(). All ten time out ->chip full reset failed. mt7996_mac_full_reset()then callsieee80211_restart_hw()
unconditionally (printing "Hardware restart was requested"), which queues
ieee80211_restart_workonsystem_freezable_wq.- But the probe had failed at the MCU stage and is about to return
-EAGAIN
(probe ... failed with error -11) —ieee80211_register_hw()was never
called, solocal->workqueueis stillNULL. ieee80211_restart_work's first statement is
flush_workqueue(local->workqueue)->mutex_lock(&NULL->mutex). The fault
VA0x20is exactlyoffsetof(struct workqueue_struct, mutex)
(pwqs16B +list16B), and theCode:bytes
... f9800011 (c85ffc03)are the arm64mutex_lock()fast-path
prfm/ldaxrwithx0 = 0x20.
So: a failed/aborted MCU init requests a mac80211 hardware restart against a
device that was never registered, and the restart work dereferences a NULL
local->workqueue.
Confidence: trigger = proven from the log; fault mechanism = strongly supported
from the register/offset/code-byte match against mainline 6.12 source. I could
not disassemble the exact line — I don't have the matching mac80211.ko for
this build (only the mt76 binaries), so this is source-pinned, not
binary-pinned.
Possible fix direction (hypothesis only, not a patch)
A natural guard would be to not request the hardware restart when the device
never finished init — e.g. gate the ieee80211_restart_hw() calls in
mt7996_mac_full_reset() (and/or bail out of mt7996_mac_reset_work()) on
dev->recovery.hw_init_done, mirroring the guard that already exists at the top
of mt7996_reset(). Equivalently, avoid scheduling/entering the full-reset path
at all while a probe is still in flight. I haven't written or tested a diff, and
I'm not sure where the 39c960c3 snapshot actually lets reset_work start
during a failed probe (in mainline mt7996_reset() returns early when
hw_init_done is false) — so I'd rather ask than guess.
Question for the maintainer
With WED disabled, a failed MCU patch download during probe ends in
mt7996_mac_full_reset() calling ieee80211_restart_hw() on a device that
never reached ieee80211_register_hw(), and ieee80211_restart_work then
NULL-derefs local->workqueue. Is the full-reset / ieee80211_restart_hw()
path expected to run while probe is still failing (before registration)? Should
mt7996_mac_full_reset() be guarded on recovery.hw_init_done the way
mt7996_reset() already is, or is the intended fix elsewhere (e.g. not starting
reset_work at all during a failed init)? Does 3def033 ("cancel reset and rc
work on device unregister") cover the never-registered case, or only
unregister?
I'm happy to apply a patch and test it, or share the full pstore records and the
module binaries.
- Dominant language
- C
- Stars
- 895
- Forks
- 461
- PR merge metrics
- No merged PRs in 30d
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from openwrt/mt76
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
MT76xx - WLAN leds doesn't work with the testing kernelMay be free again A pull request for this issue was closed without being merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
-
mt7925: Netgear A8000 (0846:9072) reports hw_path without WF0_5G, so 5 GHz band is never registeredOpen
Difficulty 4/5 3-5 days Newbie friendliness 48/100
-
Difficulty 4/5 3-5 days Newbie friendliness 22/100
Similar issues
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
Maintainers usually reply within 1 day
-
SPIDevice selects an active-high device as soon as it is createdPossibly taken @mikeysklar claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 73/100
adafruit/circuitpython#11869 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 78/100
gnif/LookingGlass#1352 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100