内核稳健性漏洞 #14576 事后总结
Postmortem for Kernel Soundness Bug #14576

原始链接: https://leodemoura.github.io/blog/2026-8-1-postmortem-for-kernel-soundness-bug-14576/

2026 年 7 月下旬,在一项由人工智能辅助的、成功绕过类型检查的考拉兹猜想(Collatz conjecture)“反证”被发现后,Lean 内核中的一个健全性漏洞随之浮出水面。该漏洞位于嵌套归纳类型的处理逻辑中,允许在通过直接元编程访问时构造出 `False` 的证明。 Lean 基金会(FRO)在收到报告后一小时内解决了该问题,随后加强了内核不变量并增加了新的回归测试。由于该漏洞仅限于直接与内核交互(绕过了前端细化器),因此这属于实现层面的错误,而非 Lean 元理论的失效。 值得注意的是,一个独立的基于 Rust 的内核 `nanoda` 同时也存在一个偶然的独立漏洞,使得该恶意项得以提前通过;目前两者均已修复。此次事件凸显了独立内核验证的重要性。为此,FRO 已聘请网络安全专家对内核进行审计,识别并修补了其他小型漏洞,同时更新了诸如 `nanoda` 等外部验证工具,以确保其与 Lean 官方实现保持同步。FRO 坚持认为,限制元编程并非可行的解决方案,因为内核必须始终作为证明有效性的最终且独立的权威。

最近,Lean 定理证明器内核中发现了一个可靠性漏洞(#14576)。该漏洞是在一次由 AI 辅助完成的“无 sorry”(无待证假设)的考拉兹猜想反证中被发现的,其根源在于内核处理嵌套归纳类型的方式存在缺陷。该漏洞允许证明出“假”(False),凸显了依赖自动化工具的风险。 Hacker News 上的讨论强调,尽管形式化验证提供了极强的正确性保证,但它并非万无一失。用户指出,即使是定理证明器及其内核这类关键软件,也像通用编程语言一样可能存在缺陷。评论者认为,经验证的结果应被视为高可信度,而非绝对正确,因为软件始终会受到人为或算法错误的影响。此次事件引发了关于 AI 生成证明的可靠性,以及信任任何基于软件的验证系统所固有的局限性的广泛讨论。
相关文章

原文

2026-08-01

A soundness bug in the Lean kernel (#14576) was reported and fixed during the week of July 27. It has had visibility on Zulip and social media (e.g., X, LinkedIn, and Mastodon).

What happened

On July 25, Ramana Kumar published a repository containing a sorry-free "disproof" of the Collatz conjecture, produced with AI assistance. It is not a valid proof because it exploits a bug in the kernel's handling of nested inductive types. On July 28, Kiran Gopinathan reduced it to a small proof of False and opened issue #14576. We pushed a fix one hour after the report (#14577). Joachim Breitner reviewed it and suggested improvements, and it was merged. New patch releases are out.

The bug: when the kernel eliminates a nested occurrence under an inductive type T with parameters Ds, and these parameters are phantom (not mentioned in constructor fields), they disappear from the generated auxiliary type and thus escape type checking. An ill-typed argument in that position could be used to make the kernel accept a proof of False. The bug is only reachable through metaprogramming, by sending the inductive declaration to the kernel directly. The frontend checks the arguments and catches the ill-typed term. This is an implementation bug, not a hole in Lean's meta-theory.

Why nanoda did not catch it

The original Collatz repository also passed a week-old version of nanoda, the main external checker. nanoda is an independent kernel (aka proof/type checker) for Lean implemented in Rust by Chris Bailey. The surprising part is that there are two unrelated bugs involved. The official kernel had a missing check in the nested inductive type support, as explained above. nanoda did check that spot, but did not verify the type name in a projection node. The nanoda bug was reported by Jeremy Chen and fixed a week before the Lean bug was reported. The proof was built so that the expression the kernel never inspects is one that the old nanoda accepted.

Ramana believes the timing was coincidental, but cannot rule out that the model had seen the nanoda report. Joachim proposed the hypothesis that the timing coincidence is due to the availability of strong models able to find this bug.

The practical consequence: checking with an independent kernel still works, since it required two distinct bugs in two implementations, but users who rely on it need current versions of both. lean4lean is affected by the kernel bug, since its handling of inductives is a port of the reference implementation.

Verification

Mario Carneiro's lean4lean is a Lean formalization of Lean's type theory together with a proof that the kernel implements it. The work is ongoing, the proof of consistency does not cover inductive types yet, and the to-be-verified implementation suffered from the same bug as the official kernel. The bug would have been found when attempting to conclude the verification of this part.

On removing metaprogramming

One suggestion in the discussion is to remove or restrict metaprogramming so that this attack is not expressible. This is misguided. The elaborator is untrusted by design. Soundness cannot depend on an untrusted component refusing to build a bad term. An attacker who wants to submit a malicious proof can also write .olean files directly or modify memory, both of which bypass the elaborator entirely. The kernel has to reject ill-typed declarations on its own, in its own process. This separation and isolation of concerns is one of the main advantages of proof terms.

What the FRO is doing

  • Regression tests for the exploit, and for a related non-uniform-parameter case raised by Arthur Adjedj, are in the Kernel Arena.

  • A follow-up PR (#14582) makes the kernel check that the parameters of a nested occurrence actually behave as parameters, rather than only re-type-checking them.

  • Daniel Selsam at OpenAI assisted the Lean FRO with an AI specialized in cybersecurity, and found other programming mistakes in the Lean kernel. All of them have been fixed. All of them were caught by nanoda. These bugs are also only reachable through metaprogramming. PRs: #14607, #14608, #14609, #14613, #14615, #14616.

  • We have also hardened kernel invariants. PRs: #14621, #14631, #14632.

  • comparator.live now runs nanoda by default, and nanoda is tracked daily so lean-eval and comparator stay current after upstream fixes.

  • We are reaching out to and supporting experts who can find further bugs, develop new kernels, and work on the theory or on verified kernels.

Acknowledgments

I am grateful to Joachim Breitner and Sebastian Ullrich for their revisions and suggestions on this post.

联系我们 contact @ memedata.com