2026 年 6 月 18 日,一位名叫 Ingo Blechschmidt 的数学家在 Mastodon 上发了一条帖子,开篇第一句是:“过去几天我深陷在一场有趣且极有成就感、但同时也极其恐怖的调试之旅中。“他随后披露了一个事实:从 2024 年 5 月 Linux 内核 6.9 发布起,整整两年多的时间里,他的加密硬盘在合上笔记本盖子之后,解密密钥一直留在内存里没有擦除。
换句话说,他的全盘加密白做了。
这条帖子几天后出现在技术社区 Hacker News 上,迅速冲到 379 分、182 条评论。笔者读完整个讨论链条和相关的内核提交记录后,意识到这件事比表面上看起来复杂得多——它不是什么惊天大漏洞,却恰恰因为”不算漏洞”而变得更加耐人寻味。
图:Linux 全盘加密(LUKS)依赖内存中的密钥来解密硬盘数据。一旦密钥不擦除,物理攻击者就能提取。来源:hacknjill.com
先搞懂一个比喻:你的行李箱密码
要理解这件事,不需要懂编程。我们用一个日常生活里的比喻。
假设你有一个带密码锁的行李箱。里头的所有东西都是加密过的——别人拿到箱子,不知道密码就打不开。这个密码(在计算机里叫”密钥”)平时存放在你的大脑里(在计算机里叫”内存”)。
每次你把行李箱合上(相当于”合上笔记本盖子”),你做的第一件事应该是把密码从脑子里擦掉,这样就算有人趁你不在偷了箱子,他也没法打开。等你回来要用箱子的时候,再重新输入密码。
这就是 LUKS(Linux 全盘加密标准)的 luksSuspend 功能要干的事:笔记本电脑进入睡眠状态前,先把内存中的解密密钥擦干净。醒来后,系统会要求你重新输入密码,密钥重新加载,硬盘恢复可访问。
这个逻辑很漂亮,也很关键。因为笔记本电脑合上盖子之后并不是关机的——内存还在供电,数据还在里头。如果密钥没有擦除,一个有心人只要把你的笔记本电脑从你手里拿走(保持开机状态),就可以通过所谓的”冷启动攻击”——把内存芯片冷冻后拆下来读取数据,或者通过 Thunderbolt/USB 接口直接访问内存——把你的加密密钥偷走。
而 Ingo Blechschmidt 的发现是:从 2024 年 5 月开始,luksSuspend 这个擦除动作悄无声息地失效了。
图:加密密钥存储在内存(RAM)芯片中。如果系统进入睡眠时没有擦除,攻击者可以通过物理手段直接提取密钥。来源:sesamedisk.com
一个”合理的”内核重构,为何捅出了安全漏洞
Ingo 用的是 git bisect——一种在代码版本历史中半自动化定位问题来源的工具。他追踪到了引发问题的那个提交(commit):a28d893eb327,标题叫”md: port block device access to file”,翻译过来是”将块设备访问方式移植到文件”。
这个提交本身没有任何恶意,也不是什么低级错误。它是 Linux 内核开发者一次合理且有用的重构——把内核中处理硬盘读写的方式从旧接口迁移到新接口。类似你把家里的电线从老式铝线换成铜线,逻辑上”更干净、更现代”。
问题在于,这次重构改动了一块看似不相关的底层机制:线程密钥环(thread keyring)的生命周期管理。
这里要解释一下”密钥环”是什么。在 Linux 内核里,加密密钥存放在一个叫”密钥环”的专用数据结构里——不是随便丢在内存角落。线程密钥环是一个特殊的密钥环——它绑定在一个程序线程上,线程结束时,密钥环也应该被销毁,里头的密钥自然也就没了。
luksSuspend 的设计恰好依赖了这个特性:它把硬盘加密密钥上传到一个临时的线程密钥环里,等那个线程退出时,密钥环自动销毁,密钥随之消失。
内核文档里写得明明白白,这是官方的保证。但在 6.9 版本中引入的那个重构,阴差阳错地让线程密钥环在某些情况下不再被销毁。线程退出了,密钥环却像幽灵一样继续挂在内存里——连带着里面的硬盘解密密钥一起。
最讽刺的是,修复这个漏洞只需要一行代码。
没错,一行。Ingo 在发现漏洞后向内核邮件列表提交了一个补丁,改动极小,就是给某个结构体加了一个必要的清理调用。如果你好奇具体是什么,它是这样的逻辑:在某个内核函数中加上一行 key_put(key),确保不再使用的密钥引用能被正确释放。
但 Ingo 自己在帖子里也坦承:“没有形式化证明,我不敢说我的补丁就是正确的,也不确定它会不会引发自己的长程交互……” 这是一个真正的工程师才会说的实话。
如果不是 NixOS 的测试基础设施,这个漏洞可能永远没人发现
这个故事里还有一个关键角色:NixOS。
如果你没听说过 NixOS,简单说它是一个”可复现”的 Linux 发行版——整个系统的配置写在一个文件里,可以用 Git 管理版本,换一台机器复制粘贴就能重建完全相同的系统。NixOS 社区对自动化测试的投入在 Linux 圈子里是出了名的。
发现者 Ingo 本身就来自 NixOS 社区。他发现漏洞后做的第一件事,是给 NixOS 的代码仓库提交了一个自动化的集成测试(PR #532499)。这个测试会在未来每一次内核更新时自动运行:模拟 LUKS 加密硬盘 → 执行 luksSuspend → 检查内存里是否还有密钥残留。
换句话说,他在修好自己机器的同时,确保了这个漏洞永远不会再回来。
不止于此。Ingo 还给 cryptsetup 项目提交了另一个补丁(MR #936),让 luksSuspend 命令在失败时不再是”静默失败”——也就是说,过去两年它悄悄地没擦除密钥,没有任何报错;现在如果擦除失败,它会明确发出警告。
这两个操作折射出的是一种工程思维:发现一个问题,就加一个测试来防止它重现;发现一个静默失败,就让它变成吵闹的失败。 这比任何技术炫技都更能代表真正的好工程实践。
影响范围有多大:哪些人该紧张
读到这,可能有人会问:我用的是 Windows/Mac,这事跟我没关系吧?我用的是 Linux 笔记本,我现在是不是要立刻关机?
答案是分情况的。
第一,这个漏洞只影响使用 Debian 系发行版(Debian、Ubuntu、Linux Mint 等)且安装了 cryptsetup-suspend 软件包的用户。 因为 luksSuspend 这个功能本身是 Debian 社区自己开发的扩展,并不在上游 Linux 的默认行为里。很多其他发行版(比如 Arch Linux 的默认安装、Fedora 的默认安装)根本不会有这个功能,它们的加密密钥在睡眠期间本来就一直留在内存里——不是漏洞,是设计如此。
第二,即使受影响,漏洞只在”合盖睡眠”场景下存在。 如果你每次都正常关机(而不是合盖就走),密钥在关机时是会被正确擦除的。问题只出现在”睡眠”(suspend)模式下。
第三,攻击需要物理接触。 远程黑客不可能通过网络偷走你内存里的密钥。需要一个真实的人拿到你还没关机的电脑,然后通过冷启动、DMA 攻击或者其他物理手段来提取。对于普通人来说,这个威胁不大——偷你电脑的人大概率只想卖掉它,没兴趣做内存取证。但对于律师、记者、异见者、跨境商务人士这些”高价值目标”群体,物理攻击是真实存在的威胁。
Ingo 在 HN 的回复里也特意澄清:“这不会影响那些使用标准配置的人,原因很简单——他们本来就不期望密钥在睡眠时是安全的。” 但这个功能的整个设计初衷,恰恰就是为了在睡眠时保护密钥。两年来,那些信任这个机制的人被辜负了。
一个更深的教训:发行版自己打补丁的风险
这个漏洞背后的”反派”其实有两个。
第一个反派是那次内核重构——一个善意的代码整理,因为缺乏对全局影响的充分理解而捅出了安全漏洞。这几乎是软件工程里的经典悲剧:代码不是”坏”的,它只是复杂到没人能完全看清它的所有连锁反应。
第二个反派更有意思:发行版自行打补丁带来的维护风险。
luksSuspend 是 Debian 社区自己写的功能,不是 Linux 内核上游官方提供的。这意味着,它的正确性不归 Linus Torvalds 和他的内核维护者团队负责。当上游内核的某个底层机制发生改变时(比如 6.9 中的线程密钥环行为变化),Debian 的补丁有没有跟着适配?没人能保证。因为上游开发者甚至不知道这个补丁的存在。
这不是说发行版不应该自己打补丁。恰恰相反,很多 Linux 发行版的优秀功能都是从”自己打补丁”开始的。但这件事提醒了一个容易被忽视的现实:每个非上游的补丁都是一种”技术债务”——今天能用,明天内核一升级可能就挂了。 如果这个补丁恰好是一个安全功能,那它”挂了”的代价是信任崩塌,不只是一个功能坏了。
用 Ingo 在 Mastodon 原帖里引用的一句话来总结:“一个由受信赖的作者提出的技术论证,难以检查,又看起来与已知正确的论证相似,就几乎从来不会被详细检查。” 代码也是一样。