dmesg | grep -c "SMBus is busy" # 数量:几分钟内成千上万 dmesg | grep "i801_smbus" | tail -20 # 确认驱动 + 设备地址
vbnet
典型输出:
```text
[12345.678] i801_smbus 0000:00:1f.3: SMBus is busy, can't use it!
0000:00:1f.3 是 Intel 平台的 SMBus 控制器(PCH 上的 I2C/SMBus 主机控制器),这是几乎所有 Intel 服务器/台式机都有的设备------不是你的服务器特殊,是每个人都会碰到这条路径。
回合 2:区分「偶发 busy」与「故障态 busy」
SMBus is busy 这条日志本身,偶发出现是正常的------BIOS/ACPI 有时会占用 SMBus 做内存条 SPD 读取、温度监控、风扇控制。故障态的标志是「持续刷屏」:
bash
# 10 秒内刷屏次数(正常 0-1 次,故障态几百上千)
timeout 10 sh -c 'dmesg -w | grep -c "SMBus is busy"'
回合 3:查 hung task 与 printk 的关联
bash
# hung task 的调用栈(关键:看是否在等 mmap_lock)
cat /proc/sys/kernel/hung_task_timeout_secs # 默认 120
dmesg | grep -A 20 "blocked for more than 120 seconds" | head -40
CVE-2026-64205 报告中的复现场景(并发 fuzzing)里,hung task 的调用栈指向等待 mmap_lock 读锁------因为 printk 刷屏在慢速串口上每个字符都要等串口发送完成,CPU 时间几乎全被刷屏消耗,其他进程拿不到调度,饿死在锁等待上。
回合 4:确认内核版本受影响
bash
uname -r
# 受影响区间:>= 6.3(该版本引入无条件清理代码的回归)
# 已修复:6.18.39 / 7.1.4 / 7.2-rc1 之后的版本
触发条件 :i801_check_pre() 返回失败(典型为 -EBUSY,SMBus 控制器正被 BIOS/ACPI 使用)+ 并发访问 SMBus。fuzzing 是最容易触发的方式,但生产环境的 i2c 工具(i2cdetect、i2cget)、lm-sensors 监控脚本、BMC 带外管理通道都可能成为触发器------只要有一个访问在 BIOS/ACPI 占用期间发起,就进入损坏路径。
根因解析:一行 iowrite8 如何毁掉硬件状态机
正常流程 vs 错误流程
i801_access() 是 i2c-i801 驱动所有 SMBus 事务的入口。正常流程:
c
// drivers/i2c/busses/i2c-i801.c(修复前逻辑)
static int i801_access(struct i2c_adapter *adap, u16 addr, ...)
{
int ret;
struct i801_priv *priv = i2c_get_adapdata(adap);
pm_runtime_get_sync(&priv->pci_dev->dev); // ① 获取软件锁(pm_runtime)
mutex_lock(&priv->bus_lock); // ② 获取互斥锁
ret = i801_check_pre(priv); // ③ 检查硬件是否可用
if (ret < 0)
goto out; // ← 错误跳转
/* ... 正常执行 SMBus 事务 ... */
ret = i801_check_post(priv, status);
out:
/* 无条件硬件清理(问题代码) */
iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv)); // ④ 清硬件锁
pm_runtime_put(&priv->pci_dev->dev); // ⑤ 释放软件锁
mutex_unlock(&priv->bus_lock);
return ret;
}
问题所在
当 ③ i801_check_pre() 失败(返回 -EBUSY,因为 SMBus 控制器正被 BIOS/ACPI 占用),代码跳到 out 后,仍然执行了 ④ :iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv))。
这行写操作干了什么:
SMBHSTSTS_INUSE_STS是硬件寄存器里的「使用中」锁位。BIOS/ACPI 正在占用 SMBus 时,这个位由 BIOS/ACPI 设置;- 驱动在没有获取硬件所有权 (
i801_check_pre失败 = 没拿到锁)的情况下,强行iowrite8把这个位清掉(写 1 到该位 = 清除中断状态位); - 同时
STATUS_FLAGS把状态寄存器里的各种标志位也一起复位了。
这一写直接打断了正在进行中的 BIOS/ACPI SMBus 事务------对方的事务被硬生生中断,SMBus 控制器的内部状态机从此错乱。
蝴蝶效应链
text
i801_check_pre() 失败(-EBUSY,BIOS/ACPI 占用中)
│
▼
无条件 iowrite8 清 SMBHSTSTS(打断 BIOS/ACPI 事务)
│
▼
SMBus 硬件状态机错乱
│
▼
后续所有 i801_access() 都在 pre-check 失败
│
▼
无限 printk "SMBus is busy, can't use it!"
│
▼
慢速串口 console → Console Livelock(printk 垄断 CPU)
│
▼
其他进程饿死(等 mmap_lock_down_read)→ hung task watchdog
修复:移动一行
修复 commit(00904687b9c55 等)的核心是把 out 标签移到硬件清理之后:
c
ret = i801_check_pre(priv);
if (ret < 0)
goto out; // 错误路径不再执行 iowrite8
/* ... 正常事务 ... */
iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv)); // 只在正常路径清理
out:
pm_runtime_put(&priv->pci_dev->dev);
mutex_unlock(&priv->bus_lock);
return ret;
原则一句话:没获取的资源不得释放(do not release resources that were never acquired)。这不仅是 i2c-i801 的问题,是所有驱动错误路径的通用检查点------错误处理代码必须知道「当前持有哪些资源」,而不是盲目清理。
通用教训:错误路径的资源所有权审计
CVE-2026-64205 的根因模式,在 Linux 驱动里反复出现。做代码审查或排障时,对每个错误路径问三问:
| 问题 | 案例 |
|---|---|
| 这个清理动作对应的「获取」在错误路径里真的执行了吗? | i801:i801_check_pre 失败 → 没获取硬件锁 → 却清硬件锁 |
| 错误路径是否跳过了部分获取步骤但执行了全部释放步骤? | 常见于:先 pm_runtime_get 成功、后 mutex_lock 失败,但释放时两个都释放(或都跳过) |
| 释放顺序与获取顺序是否完全相反? | 获取 A→B,释放必须 B→A |
内核自带的检测工具:
CONFIG_DEBUG_ATOMIC_SLEEP:检测原子上下文里的睡眠CONFIG_PROVE_LOCKING(lockdep):检测锁顺序反转和错误释放CONFIG_KASAN:检测 use-after-free / 越界
如果当时跑着 lockdep,这种「未获取就清理」的问题在开发阶段就可能暴露------但硬件寄存器清理不走 lockdep 管的路,所以只能靠代码审查 + fuzzing(syzkaller 正是抓到这类问题的利器)。
排障决策表:SMBus is busy 对号入座
| 症状 | 根因 | 第一动作 |
|---|---|---|
| 偶发一次 SMBus is busy | BIOS/ACPI 瞬态占用 | 无需处理 |
| 持续刷屏 + hung task | i2c-i801 错误路径破坏状态机(CVE-2026-64205) | 升级内核(≥6.18.39 / 7.1.4),dmesg -n 3 止血 |
| 开机即刷屏 | BIOS 初始化阶段冲突 | 检查 BIOS 设置(SMBus 占用相关项) |
| i2cdetect 报 Can't use SMBus Quick Write | 工具/总线功能位问题(非内核 bug) | 与本文故障区分:不是刷屏型故障 |
| 刷屏但无 hung task | printk 洪泛尚未饿死进程 | 立即止血,防止升级成 livelock |
常见误区表
| 误区 | 为什么错 | 正确做法 |
|---|---|---|
| 「SMBus is busy 说明硬件坏了」 | 该日志只表示控制器正被占用;持续刷屏才是故障态 | 先看内核版本,再判断是否硬件 |
| 「hung task 是磁盘/文件系统问题」 | hung task 只是「进程 120 秒没调度」的通用告警,根因可能是 printk 垄断 CPU | 先看刷屏日志,hung task 往往是被牵连的 |
| 「重启就能解决,不用管」 | 重启确实恢复 SMBus 状态机,但同一触发条件会复发 | 重启后必须升级内核,否则下次并发 fuzzing/访问还会触发 |
| 「dmesg -n 3 治好了」 | 只是压低日志级别,硬件状态机已坏,SMBus 功能仍不可用 | 只做临时止血,必须升级内核 |
| 「这问题只有 fuzzing 才碰到,生产没事」 | 生产环境的 i2c 工具、lm-sensors、BMC 通道都可能成为触发器 | 高危场景(大量监控脚本读 SMBus)更该升级 |
进阶风险视角:什么时候它会变成业务事故
CVE-2026-64205 不只是「内核日志刷屏」的学术问题,在特定场景会直接演变成业务事故:
- 带外管理通道依赖 SMBus 的服务器:BMC/IPMI 与主机的 SMBus 共享控制器,状态机损坏后带外温度/电源监控失效,可能误触发硬件保护(如风扇全速、断电保护)。
- 慢速串口控制台 + 高密度日志:串口 console(9600/115200 baud)比虚拟终端慢几个数量级,同样的 printk 洪泛在串口上饿死进程的速度快得多------机房通过串口管理的老服务器是重灾区。
- 监控自愈脚本误判 :
dmesg刷屏会被监控系统当成「SMBus 硬件故障」告警,自动化脚本可能触发误重启/误切换,反而扩大故障面。 - 触发条件的「巧合性」:BIOS/ACPI 占用 SMBus 的时机不受你控制(内存 SPD 刷新、ACPI 电源事件),任何一次「恰好并发」的 i2c 访问都可能踩中------这解释了为什么故障看起来「无缘无故」。
内核版本与修复演进表
| 版本 | 状态 | 说明 |
|---|---|---|
| < 6.3 | 不受影响 | 该回归由 6.3 引入(错误路径无条件清理代码) |
| 6.3 ~ 6.18.38 | 受影响 | 触发条件:i801_check_pre() 失败 + 后续 SMBus 访问 |
| 6.18.39 | 已修复 | 首个稳定修复版本 |
| 7.1.4 | 已修复 | LTS 分支修复 |
| 7.2-rc1+ | 已修复 | 主分支修复(00904687 等 commit) |
| 各发行版 backport | 视厂商 | Ubuntu/Debian/RHEL 的 backport 版本需查厂商安全公告 |
升级前确认触发条件是否在你的环境存在:Intel PCH SMBus(0000:00:1f.3)+ 任何 i2c 访问工具 + BIOS/ACPI 并发占用。没有 SMBus 设备的纯 ARM 服务器不受影响,但排查日志时仍可能看到别的驱动刷屏------方法论通用。
复盘方法论:日志洪泛 + 挂起类故障的三步定位
这次排查可以用一条通用方法论复用到所有「刷屏 + 卡顿」故障:
- 先找洪泛源,再查挂起 :
dmesg里刷屏的那条日志通常是根因,hung task / 高负载只是受害者。先grep -c数刷屏,再grep -A 20 blocked看挂起栈。 - 区分「提示性日志」与「故障态日志」 :
SMBus is busy单条是提示,持续刷屏才是故障。任何日志都要看频率,不看单条。 - 追溯代码的「资源所有权」:当排障指向驱动/内核模块时,回到源码看错误路径是否「释放了未获取的资源」------这是 CVE-2026-64205 的根因,也是驱动类 bug 的高发模式。
解决方案与自检清单
修复步骤
- 升级内核 :
>= 6.18.39/>= 7.1.4(或厂商已 backport 的发行版内核) - 临时止血 (升级前):
dmesg -n 3压低 console 日志级别,缓解 console livelock(不治本,硬件状态机已坏)- 重启恢复 SMBus 硬件状态机(重启后 BIOS 重新初始化控制器)
- 排查哪些进程在频繁访问 SMBus(
lsmod | grep i2c、lm-sensors、i2c-tools 脚本),故障态下停止访问
- 验证 :
dmesg | grep -c "SMBus is busy"不再增长i2cdetect -l正常列出总线- 温度/风扇监控(依赖 SMBus 的 hwmon)恢复读数
自检清单
- 内核版本 >= 6.18.39 / 7.1.4(或厂商修复版)?
-
SMBus is busy不再刷屏(10 秒 0-1 次)? - hung task 告警消失?
- lm-sensors / BMC 带外通道读取正常?
- 触发场景(并发 fuzzing / 高频率 i2c 访问 + BIOS 占用)已消除?
启示
CVE-2026-64205 的价值不在 SMBus 本身,而在它演示的排障顺序:当「日志洪泛 + hung task」同时出现,先找洪泛源,别被 hung task 带偏 。刷屏的驱动日志往往是根因,hung task 只是 printk 垄断 CPU 的受害者。另外,所有「错误路径清理」代码都值得用「未获取不释放」这条铁律重新审一遍------一个看似无害的 iowrite8 在错误路径上,能通过「打断硬件状态机 → 无限刷屏 → console livelock → hung task」的连锁反应,让一台 128 核服务器看着像硬件故障,实际只是一行标签位置错了。
最后补充一个运维直觉:内核驱动日志的「单条出现」和「持续刷屏」是两个完全不同的世界 。前者是正常提示(SMBus 被 BIOS 占用瞬间),后者是故障信号(状态机已坏)。给监控系统配告警时,务必基于「频率阈值」而非「单条出现」------否则你会被 SMBus is busy 这种正常日志淹没,真正的故障反而被忽略。这次的内核修复(移动 out 标签)在 patch 里只有几行,但背后的教训------错误路径必须精确知道「我持有哪些资源」------适用于任何需要清理资源的代码,从内核驱动到应用层连接池都成立。
原始出处 :CVE-2026-64205(NVD,2026-07-20 发布,kernel.org 报告);修复 commits:
00904687b9c5527d569d9a1ca72119823e735a61/10dd1a736d557e310a77117832874729a0175d57/bb5133a7d5f3fe5c387770e25f2e00e682ce11ed等;受影响:Linux 6.3 起,修复于 6.18.39 / 7.1.4 / 7.2-rc1。