前情提要
上回说到,我靠二进制补丁——改了 .ko 里 20 个字节——把 mt7902e 驱动的 NULL 指针给修了
系统安稳了半个月,没再锁死
我以为这事就完了
然后它又死了
5 月 26 号早上,系统又双叒叕卡死了
跟以前一模一样的症状——鼠标能动但点不了东西,网断了,TTY 切不了,只能长按电源
查日志:
journalctl -b -1 -p 3kernel: BUG: kernel NULL pointer dereference, address: 0000000000000000kernel: RIP: mt7921_channel_switch_rx_beacon+0x18/0xa0 [mt7902e]kernel: CR2: 0000000000000000同一个函数,不同偏移
上回崩在 +0x9,这回升到了 +0x18——说明崩溃路径不一样
这次是怎么回事
把堆栈和寄存器看了一遍:
RAX: 0000000000000000 ← 问题在这里,RAX 是 NULL反汇编确认:
; 函数开头mov 0x58(%rdi),%rax ; dev = hw->priv (安全); 然后直接mov 0x8(%rax),%rax ; 上一条 RAX 非空,这步也安全; ...mov (%rax),%rcx ; CRASH! RAX=0跟上回的区别:
- 上回:第一层指针
phy就是 NULL,mov 0x8(%rax)直接崩 - 这回:第一层指针拿到了,但走到后面
dev->new_ctx是 NULL,解引用new_ctx->def才崩
// 罪魁祸首if (cfg80211_chandef_identical(&chsw->chandef, &dev->new_ctx->def) && // ← new_ctx 可能 NULL chsw->count)dev->new_ctx 是什么时候被清空的?remove_chanctx 会把它设 NULL。很简单的时序问题:AP 发了 CSA 信标,驱动收到信标时 new_ctx 已经被清了
这次换了个玩法
上回是二进制改动 .ko 文件,因为那时候用的是内核自带的 stock 模块
但这回我换方案了——改用 DKMS 外部驱动(mediatek-mt7927),理由就一个:DKMS 编译的模块在内核更新后会自动重建,不用每次 pacman -Syu 都重新打补丁
# 源码路径/usr/src/mediatek-mt7927-2.10/mt76/mt7921/main.cDKMS 驱动的爽点:改完源码 dkms build + install 就完事,内核更新后 dkms autoinstall 自动搞定
但 DKMS 有个坑——dkms.conf 里没有设 PATCH 指令,所以 patches/ 目录下那些 .patch 文件只是放着好看,build 的时候根本不会自动打进去。第一次跑的时候就被坑了,build 成功了但补丁没生效
修复
跟上次一样的补丁逻辑——一行 NULL 守卫:
if (cfg80211_chandef_identical(&chsw->chandef,if (dev->new_ctx && cfg80211_chandef_identical(&chsw->chandef, &dev->new_ctx->def) && chsw->count) {手动 sudo patch 到源码 → sudo dkms build → sudo dkms install → sudo modprobe -r mt7902e && sudo modprobe mt7902e
WiFi 回来了,正常连上
这次和上次的区别
| 项目 | 上次 | 这次 |
|---|---|---|
| 编译方式 | stock kernel 内置 | DKMS 外部驱动 |
| 修哪里 | 二进制改 .ko | 源码改 .c |
| 崩溃路径 | phy 指针 NULL | dev->new_ctx 指针 NULL |
| 补丁方式 | 改 20 字节汇编 | 加一行 dev->new_ctx && |
| 内核更新后 | 要重新打补丁 | 自动重建 |
| 对用户影响 | WiFi 断连重连 | 同左 |
以后内核更新?跑一下 dkms-patch.sh 就行
所以为什么同一个函数崩两次
回头看,mt7921_channel_switch_rx_beacon 这个函数是 mac80211 的回调,AP 发 CSA 信标时触发。但这个驱动在信道切换的整个流程里,有好几处指针可能会在时序竞争中被清 NULL
库里的二进制补丁修的是第一级指针路径,这次源码补丁修的是 new_ctx 路径。两条不同的崩溃路径,同一个入口函数
驱动是 2025 年底编译的,那时候上游 mt76 可能还没修全这些路径。不过现在有了源码级补丁,以后不管内核怎么升级,驱动都不会再因为这个崩了
补丁仓库
源码补丁和打包脚本也一并更新到了之前的仓库:
新增了 mt7902-null-ptr-csa-beacon.patch(源码级补丁)和 dkms-patch.sh(自动打补丁+重建脚本)
一点感受
距离上次修这个 bug 已经过了半个月,当时我以为二进制补丁就能一劳永逸了
结果同一行代码,换了个时机,又坑了我一次
好在这次更彻底——源码级修复、DKMS 自动构建、更新不丢补丁
而且这次修完我还顺带把整个驱动源码都扫了一遍,确认了没有其他类似的危险解引用。应该不会再有三回了,吧
补丁已推送,等下次内核更新验证
我觉得应该不会有第四次了……吧?
系统: Arch Linux内核: 7.0.3-zen1-2-zen网卡: MediaTek MT7902 (Filogic 310) PCIe驱动: mediatek-mt7927 DKMS 2.10补丁仓库:
::github{repo="GT001well/mt7902e-null-fix"}Saya提供技术支持
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时
