SMBus is busy, can't use it! + task blocked 120s - i2c-i801 错误路径释放未获取资源致 hung task(CVE-2026-64205)

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 工具(i2cdetecti2cget)、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))

这行写操作干了什么:

  1. SMBHSTSTS_INUSE_STS 是硬件寄存器里的「使用中」锁位。BIOS/ACPI 正在占用 SMBus 时,这个位由 BIOS/ACPI 设置;
  2. 驱动在没有获取硬件所有权i801_check_pre 失败 = 没拿到锁)的情况下,强行 iowrite8 把这个位清掉(写 1 到该位 = 清除中断状态位);
  3. 同时 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 不只是「内核日志刷屏」的学术问题,在特定场景会直接演变成业务事故:

  1. 带外管理通道依赖 SMBus 的服务器:BMC/IPMI 与主机的 SMBus 共享控制器,状态机损坏后带外温度/电源监控失效,可能误触发硬件保护(如风扇全速、断电保护)。
  2. 慢速串口控制台 + 高密度日志:串口 console(9600/115200 baud)比虚拟终端慢几个数量级,同样的 printk 洪泛在串口上饿死进程的速度快得多------机房通过串口管理的老服务器是重灾区。
  3. 监控自愈脚本误判dmesg 刷屏会被监控系统当成「SMBus 硬件故障」告警,自动化脚本可能触发误重启/误切换,反而扩大故障面。
  4. 触发条件的「巧合性」: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 服务器不受影响,但排查日志时仍可能看到别的驱动刷屏------方法论通用。

复盘方法论:日志洪泛 + 挂起类故障的三步定位

这次排查可以用一条通用方法论复用到所有「刷屏 + 卡顿」故障:

  1. 先找洪泛源,再查挂起dmesg 里刷屏的那条日志通常是根因,hung task / 高负载只是受害者。先 grep -c 数刷屏,再 grep -A 20 blocked 看挂起栈。
  2. 区分「提示性日志」与「故障态日志」SMBus is busy 单条是提示,持续刷屏才是故障。任何日志都要看频率,不看单条。
  3. 追溯代码的「资源所有权」:当排障指向驱动/内核模块时,回到源码看错误路径是否「释放了未获取的资源」------这是 CVE-2026-64205 的根因,也是驱动类 bug 的高发模式。

解决方案与自检清单

修复步骤

  1. 升级内核>= 6.18.39 / >= 7.1.4(或厂商已 backport 的发行版内核)
  2. 临时止血 (升级前):
    • dmesg -n 3 压低 console 日志级别,缓解 console livelock(不治本,硬件状态机已坏)
    • 重启恢复 SMBus 硬件状态机(重启后 BIOS 重新初始化控制器)
    • 排查哪些进程在频繁访问 SMBus(lsmod | grep i2c、lm-sensors、i2c-tools 脚本),故障态下停止访问
  3. 验证
    • 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。

相关推荐
starrysky81013 天前
kubectl logs 看不了日志?fsnotify 报 too many open files 的真相是 inotify 实例耗尽
angular.js
shmily麻瓜小菜鸡15 天前
vue3里“devDependencies 是开发环境依赖,dependencies 是生产环境依赖”
前端·javascript·vue.js·vscode·vue·angular.js
starrysky81018 天前
RuntimeError: dictionary changed size during iteration——多线程 Pydantic cached_property 竞态条件
angular.js
starrysky81018 天前
Agent 安全 #06:Agent 灰度发布与回滚 —— 上线新 Prompt 炸了生产,你敢回滚吗?
angular.js
starrysky81018 天前
CPU 70% idle 但 load average 84?D 状态进程不可中断睡眠的深度排查
angular.js
shmily麻瓜小菜鸡19 天前
浏览器在请求外部图片(403 Forbidden)问题
vue.js·vscode·echarts·angular.js
界面开发小八哥21 天前
界面控件DevExtreme v26.1新版亮点——支持Angular 22
前端·javascript·angular.js·devexpress·ui开发·devextreme
巴勒个啦25 天前
Vue 3.6 Vapor Mode 实战:我把一个 Vue3 项目的渲染性能提升了 4 倍
前端·angular.js
shmily麻瓜小菜鸡1 个月前
在 Angular 项目中实现国际化(i18n)和翻译 使用ngx-translate
前端·javascript·angular.js