
微软FORGE Lab用AI扫描Linux内核,提交59份漏洞报告
来源标题:Lessons from 59 Linux Kernel Bug Reports
微软FORGE Lab利用代号MDASH的AI工具扫描Linux内核部分子系统,已产生59份公开上游报告和33个已分配CVE,其中8个CVE确认存在可用的本地提权利用。初步扫描两周内产生26502条原始发现,经数月人工调查与维护者协作,38份报告的修复已合并进主线。该工作由一名全职研究员和一名实习生主导,聚焦网络、文件系统、安全模块等核心子系统。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
作者:Cen Zhang / 2026年11月11日 / 阅读时长 9 分钟 在 Microsoft 的 FORGE Lab,我们使用代号 MDASH 的工具,于 2026 年 5 月底至 6 月中旬扫描了 Linux 内核的一部分子系统。一名全职研究员和一名实习生主导了这项为期三个月的工作,迄今已产生 59 份公开的上游报告和 33 个分配的 CVE。我们确认其中 8 个 CVE 存在可用的本地提权利用,并且我们报告中已有 38 份的修复被合并进主线。
发现 bug 只是工作的一部分。最初的扫描在大约两周内产生了 26,502 条原始发现,但要把这些结果转化为可复现的 bug、上游报告和补丁,还需要数月的专家调查以及与 Linux 维护者的协作。
在这篇文章中,我们回顾了如何从数千条潜在发现走到经过验证的漏洞和上游修复,以及我们在用 AI 扩展漏洞发现方面学到了什么。
我们聚焦于几个核心内核子系统,例如网络、文件系统和安全模块。选择标准很直接:我们挑选对安全敏感、且不明显依赖专用硬件的组件,使基于 QEMU 的验证简单且统一。代码扫描主要使用 Sonnet 4.6(占代码扫描 token 的 71.1%)、GPT-5.4(20.1%)和 GPT-5.5(7.1%)。
代号 MDASH 的扫描在两台服务器(64 核/256 GB 内存和 32 核/128 GB 内存)上,于约两周内产生了 26,502 条原始扫描发现,但要把这些输出变成补丁和报告,还需要数月的专家工作。我们利用代号 MDASH 的置信度等级以及我们另外构建的快速筛选 agent,过滤掉低置信度的声明,将候选池缩减到 11,769 个。随后,PoC 生成 agent 尝试在 QEMU 中复现,主要使用 GPT-5.5(占复现 token 的 77.3%)、Opus 4.6(13.2%)和 GPT-5.5-cyber(9.4%)。专家随后审查了 621 个有 PoC 证据的案例,以确定根本原因、合并重复项并准备上游提交。
大约每十个有 PoC 支持的案例会产生一份公开上游报告:621 个案例中产生了 59 份报告(9.5%)。在这些案例中,没有单独提交新报告的主要原因有:
提交时机解释了许多重叠。这 621 个案例在扫描完成时大多是新的(尽管有些可能已由他人在处理中)。我们的第一份公开报告于 2026 年 6 月 27 日发出,距扫描开始已超过五周。到 9 月 1 日,59 份报告中有 55 份已发布到公开邮件列表;其余四份在同一时间段内私下报告,并于 10 月中旬转至公开列表。
上游 Linux 通常要求漏洞报告附带可用的补丁。Agent 帮助起草了初始修复,但人工验证、子系统理解以及在审查期间与维护者反复沟通仍然必不可少。到目前为止,我们报告中已有 38 份的修复被合并进上游主线——其中一半在首次提交时即被合并,其余经过一至四次修订。从首个公开补丁到进入主线发布,中位时间为 41 天(最长 108 天),我们仍在与维护者合作处理剩余报告。
有趣的是,我们报告的大多数 bug 已经在内核中存在多年。在这 59 份报告中,从我们首次公开报告时算起,bug 的平均年龄为 8.1 年。其中最古老的一个是 cls_u32 的移位越界 bug(CVE-2026-74544),它至少已存在 21 年。
在这批目前已分配的 33 个 CVE 中,有 24 个被评为 High 或更高(CVSS v3.1 评分至少 7.0)。以下是我们已在受控实验室环境中确认存在本地 root 提权利用的八个 CVE,其中一些案例需要非默认设置或禁用漏洞缓解措施:
CVE-2026-64563 是 rhashtable(Linux 内核共享的可调整大小哈希表)中的一个单行状态重置 bug。由于调用者信任表遍历返回的对象指针,这个小小的库缺陷在 Linux 上实现了稳定的本地提权——并且影响了启用 SELinux 强制模式的 Android——且不需要特殊的内核配置或用户命名空间。
许多内核子系统将活动对象(例如 socket)存储在 rhashtable 中,并在用户空间请求列出它们时遍历该表。由于大型列表可能被拆分到多个回复中,遍历可以暂停和恢复。其迭代器保存了停止的位置,包括指向最后访问条目的指针(iter->p)。然而,在遍历暂停期间,表仍然可能发生变化:条目可能被移除和释放,调整大小可能替换底层桶表。
2018 年之前,rhashtable_walk_stop() 在暂停时总是将 iter->p 清除为 NULL。为了提高跨停止/启动周期的遍历稳定性,2018 年的一个提交(5d240a8936f6)在 walk_stop() 期间保留了 iter->p,并将重置移入 rhashtable_walk_start_check()——但仅在其两条恢复路径中的一条上: if (!iter->walker.tbl) { iter->walker.tbl = rht_dereference_rcu(ht->tbl, ht); iter->slot = 0; iter->skip = 0; return -EAGAIN; 在这次重启之后,下一次调用 rhashtable_walk_next() 时看到 iter->p 仍然被设置,于是沿着 iter->p->next 前进,而不是从 slot = 0 重新开始。如果该条目在遍历暂停期间被释放,解引用 iter->p->next 就会读取已释放的内存。
为什么这个 bug 八年都没被发现?触发它需要在同一次遍历中三个条件同时满足:调用者必须在 walk_stop() 和 walk_start() 之间暂停并恢复同一个迭代器;在此期间表调整大小必须释放旧的桶表;并且 iter->p 处的条目也必须在遍历恢复前被移除并释放。大多数 rhashtable 遍历器一次遍历就完成,从不会恢复已停止的迭代器;即使调用者确实暂停并恢复,这三个事件在正常工作负载中也很难偶然同时发生。
上游修复只是在返回前清除过期的指针:
- iter->p = NULL; 从 socket 列表到内核写入
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。