一个”没有退路”的内核漏洞

Red Hat 在 7 月发布的安全公告 RHSB-2026-010 里披露了一个代号 GhostLock 的 Linux 内核漏洞,编号 CVE-2026-43499(CVSS 3.1 评分 7.8,Red Hat 定级 Important)。

它有几个特点凑在一起,让运维很难受:

  • 影响面极广:RHEL 6、7、8、9、10 全系在内,连带 RHEL CoreOS、OpenShift Container Platform、OpenStack Platform、Virtualization;
  • 本地提权 + 可导致 DoS:攻击者拿到的不是”信息泄露”级别的边角问题,而是实打实的 Use-After-Free;
  • 没有运行时缓解手段:受影响代码编译进了所有标准内核,模块卸不掉、sysctl 关不掉——公告明确写了,唯一完整的修复方式是升级内核并重启。

最后一条是关键。通常内核漏洞还能”先挂个缓解措施顶一顶,等下个维护窗口再升级”;GhostLock 不给这个选项。

漏洞速览

项目 内容
公告 RHSB-2026-010(2026-07-08 发布,2026-09-08 更新)
CVE CVE-2026-43499(另有 CVE-2026-53166,见后文说明)
代号 GhostLock
类型 Use-After-Free(CWE-416)/ Red Hat 归类为过期指针解引用(CWE-825)
评分 CVSS 3.1 7.8 High(AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)
位置 kernel/locking/rtmutex.c 的 remove_waiter()
影响 RHEL 6 / 7 / 8 / 9 / 10 及 RHEL CoreOS、OpenShift、OpenStack Platform、Virtualization
攻击条件 本地低权限账户(能调用 syscall),不可从网络直接触达
缓解 无运行时缓解;升级内核 + 重启,或使用 kpatch(见下文)

技术原理:futex PI 路径上的三个 bug

要理解这个漏洞,先要理解几个内核概念。别慌,我用最直白的方式讲。

背景:futex 与优先级继承

futex(Fast Userspace Mutex)是 Linux 用户态线程同步的基石——pthread_mutex_t、Java 的锁、Go 的 runtime 调度,底层都在用它。

优先级继承(PI, Priority Inheritance) 是 futex 的一个高级特性:当高优先级线程在等一个被低优先级线程持有的锁时,内核会临时”抬高”低优先级线程的优先级,避免优先级反转(火星探路者号当年就栽在这上面)。用 PI futex 必须走一套更复杂的内核路径:线程会在 rtmutex 的红黑树(rbtree)里排队等待,内核维护”谁在等谁”的链式结构——这就是优先级链。

漏洞点:remove_waiter() 用错了对象

这次的 bug 出在排队线程的移除逻辑上。

remove_waiter() 有两条调用路径:

  1. 慢速锁路径(slowlock):线程自己等锁、自己放弃——这条路径上 waiter::task 和 current(当前运行的线程)是同一个;
  2. proxy-lock 回滚路径:futex_requeue() 处理 FUTEX_CMP_REQUEUE_PI(批量转移等待者)时,会先”代理”某个线程去排队(rt_mutex_start_proxy_lock());如果中途检测到死锁风险,需要回滚——此时调用 remove_waiter() 的是发起 requeue 的那个线程,而被移除的 waiter 属于另一个线程。

问题就在这里:remove_waiter() 的所有出队操作都按 current(发起者)来做,而不是按 waiter::task(真正排队的人)来做。 在第二条路径上,这两个不是同一个线程。

按内核补丁的描述,这直接引发三个连锁问题:

  1. rbtree 出队时没有持有 waiter::task::pi_lock——锁保护缺失,并发下红黑树结构可能被破坏;
  2. waiter 线程的 pi_blocked_on 状态没有被清理——该字段指着优先级链上的节点,不清理就留下一个悬空指针,这是 UAF 的引信;
  3. rt_mutex_adjust_prio_chain() 操作了错误的 top-priority waiter——优先级链调整跑到了错误的线程上,链状态恶化。

三者叠加,攻击者可借此制造 Use-After-Free(悬空指针被复用)或触发内核崩溃。

攻击路径

1
2
3
4
5
6
7
本地低权限用户
└─ futex(2) 系统调用,操作码 FUTEX_CMP_REQUEUE_PI
└─ futex_requeue()
└─ rt_mutex_start_proxy_lock()
└─ 构造死锁检测错误 → proxy-lock 回滚
└─ remove_waiter() ← 按 current 操作,对象错位
└─ 悬空指针 → Use-After-Free → 提权 / DoS

GhostLock 漏洞触发路径

值得注意的是 CVSS 向量里的 AC:L(攻击复杂度低):NVD 的场景说明写得很直白——攻击者控制着所有涉及的线程和 PI-futex,可以确定性地构造出驱动回滚的死锁检测错误。也就是说,这不是”碰运气的竞态”,而是可以稳定复现的攻击。

修复方式

补丁的标题就是一行总结:

rtmutex: Use waiter::task instead of current in remove_waiter()

把所有相关操作从 current 改为 waiter::task,三个问题一并消除。补丁已进入 kernel.org 各 stable 分支,Red Hat 也已反向移植到 RHEL 各版本内核(修复版本见下文)。顺带一提,这个修复由内核 locking 子系统维护者 tglx(Thomas Gleixner)审核定稿——修复中还顺手补了 rt_mutex_adjust_prio_chain() 的一处问题。

为什么”没有缓解手段”

RHSB-2026-010 公告里有两句话值得逐字读:

“没有运行时内核参数或模块可完全解决该漏洞。”
“受影响代码通过 CONFIG_FUTEX_PI 编译进所有标准 RHEL 内核,无法在运行时关闭。”

拆开解释:

  • 卸不了模块:rtmutex 及其 futex PI 支持不是可加载模块,是编译进内核镜像本体的(CONFIG_FUTEX_PI=y)。rmmod 这条路不存在。
  • 关不掉功能:没有对应的 sysctl 开关,也没有启动参数能禁用这条代码路径。
  • “不用 PI futex 就没风险”?不一定:公告确实提到”未使用实时调度或优先级继承 futex 的环境暴露风险较低”——但代码路径存在于所有标准内核构建中,只要本地有低权限用户能发 futex 系统调用,就存在触达可能。把它当成”我们不用 RT 就没关系”过于乐观。

对比一下常规漏洞的处置节奏:通常可以”先上缓解(比如禁用某个功能/加载参数/网络 ACL)→ 排期升级”。GhostLock 直接跳过第一阶段,进入”要么升级重启,要么继续裸奔”的二选一。这就是它让运维头疼的核心原因。

补充:常规 glibc 时代的 “Ghost”(CVE-2015-0235)也是”无实际缓解、只能打补丁重启”的路子,GhostLock 这个代号多少有点致敬意味——都是”幽灵般存在、只能正面修复”的漏洞。

修复矩阵:各版本对应的内核包

Red Hat 从 7 月中旬起陆续为全线产品发布了修复内核。整理如下(均为 2026 年):

产品 修复内核包 发布日期
RHEL 10 kernel-6.12.0-211.33.1.el10_2 07-13
RHEL 10.0 EUS kernel-6.12.0-55.89.1.el10_0 07-16
RHEL 9 kernel-5.14.0-687.25.1.el9_8 07-13
RHEL 8 kernel-4.18.0-553.143.1.el8_10 07-14
RHEL 8.4 / 8.6(AMC/EUS-LL) kernel-4.18.0-305.198.1.el8_4 / 472.201.1.el8_6 07-15
RHEL 8.8(TUS/SAP) kernel-4.18.0-477.152.1.el8_8 07-16
RHEL 7 ELS kernel-3.10.0-1160.156.1.el7(含 kernel-rt) 07-17
RHEL 6 ELS kernel-2.6.32-754.62.1.el6 07-20
NVIDIA for RHEL 10 kernel-6.12.0-231.16.el10nv 07-10

几个观察:

  • 连 RHEL 6 ELS(扩展生命周期支持)和 RHEL 7 ELS 都发了修复——说明 Red Hat 对这个漏洞的定级和重视程度;
  • 各 EUS / TUS / SAP Solutions / AMC 分支都有独立修复包,订阅这些通道的用户对应自己的通道升级即可;
  • 使用 NVIDIA for RHEL 驱动的机器要留意单独的 el10nv 内核包。

处置步骤(实操)

1. 检查当前内核是否受影响

1
2
3
4
5
# 看当前运行的内核
uname -r

# 对照上面的修复矩阵比对版本号
# 例:RHEL 9 上低于 5.14.0-687.25.1.el9_8 的就是未修复

也可以用 Red Hat 的 Oval / dnf updateinfo 确认:

1
2
3
4
# 查看系统里待安装的内核更新及其解决的 CVE
dnf updateinfo list cves | grep CVE-2026-43499
# 或查看内核包的更新详情
dnf updateinfo info kernel | head -40

2. 升级内核

1
2
3
4
5
6
7
8
9
10
11
# 更新内核
dnf update kernel

# 或者只升级到修复版本(指定版本更可控)
dnf update kernel-5.14.0-687.25.1.el9_8 # 按你的 RHEL 版本替换

# 重启进入新内核(必须)
reboot

# 重启后验证
uname -r

注意:RHEL 系内核是多版本共存安装的,dnf update kernel 不会覆盖旧内核——重启才会进入新的。重启前确认引导加载器默认项正确:

1
2
grubby --default-kernel        # 看默认启动内核
rpm -q kernel # 列出已安装的所有内核

3. 免重启选项:kpatch

如果你的服务窗口很难排,Red Hat 为主流版本提供了 kpatch 热补丁(live patch),可以在不重启的情况下修复——2026-08-24 发布,覆盖:

  • RHEL 8(含 8.8 SAP Solutions)
  • RHEL 9(含 9.2 / 9.4 SAP Solutions、9.6 EUS)
  • RHEL 10
1
2
3
4
# 安装并加载最新的 kpatch 热补丁
dnf update kpatch-patch
systemctl status kpatch # 确认服务状态
kpatch list # 确认补丁已加载

kpatch 的安全修指向的是当前运行的内核内存,补丁已加载即已生效。但要理解两点:

  1. kpatch 是官方修复通道,不是”缓解手段”——它真的修了漏洞,但后续仍需在合适窗口升级内核以便重启后仍带修复;
  2. kpatch 的覆盖范围有限(特定版本组合),且不是所有 RHEL 版本都有。

4. 纵深防御:漏洞修复之外

内核提权类漏洞的”本地”性容易让人松懈,但现实是:

  • 多租户/共享主机:同机器上任何低权限账户都是潜在攻击者;
  • 容器场景:容器逃逸链里,”容器内拿到内核提权”常常是临门一脚;
  • CI/CD runner:跑不受信任代码的构建机是高危场景;
  • Web 应用跳板:从”打穿 Web 应用拿到一个低权限 shell”到”内核提权拿到 root”经常是一条完整攻击链。

所以除了升级内核,建议顺带检查:

1
2
3
4
5
6
7
8
# 有没有不该有 shell 的账户?
awk -F: '$3 >= 1000 || $3 == 0 {print $1, $3, $7}' /etc/passwd

# 有没有异常的高权限 SUID 文件?
find / -perm -4000 -type f 2>/dev/null | head -20

# 限制普通用户对 futex(2) 的使用不现实(几乎所有程序都在用)
# 但可以用 seccomp/容器运行时策略,在容器层收紧

一个插曲:被撤销的 CVE-2026-53166

公告标题里挂着两个 CVE,实际有个小故事:

CVE-2026-53166 的标题是 futex/requeue: Prevent NULL pointer dereference in remove_waiter() on self-deadlock——同一处代码的另一个症状(自死锁场景下的空指针解引用),Red Hat 定级 Moderate(CVSS 5.5)。

但它在 2026-07-10 被 Linux 内核 CNA 撤销(REJECTED)——上游认为它与 CVE-2026-43499 是同源问题的重复编号,两者由同一批修复解决。Red Hat 的公告和 CVE 数据库页面上至今保留两个编号的引用(RH 的跟踪体系有独立记录),但从权威 CVE 数据库(MITRE/NVD)查询时,只有 CVE-2026-43499 是有效条目。

对运维的实际影响:只需跟踪 CVE-2026-43499,升级到上述任一修复内核版本即可同时覆盖两者。查资产的时候别被一个”已撤销”的 CVE 号绕晕。

结语

GhostLock 是个典型的”配置退路被堵死”的安全事件:漏洞本身不花哨(经典 UAF),CVSS 7.8 也不算最高级别,但**”无运行时缓解 + 全系影响 + 需要重启”**这三点组合,逼着每个受影响的运维必须老老实实排维护窗口。

如果你管的机器里有 RHEL 系服务器,现在要做的三件事:

  1. uname -r 对一遍修复矩阵,找出没升级的机器;
  2. 排维护窗口 dnf update kernel + 重启(重启前做好服务检查清单);
  3. 排不了窗口的,看你的通道有没有 kpatch 可用,先打上止血。

参考来源: