AIsaAIsaStudio English 打开 AIsa Studio

Anthropic 用 Claude 对 123 个开源库完成汇编级形式化验证

原标题:LLMs are making formal software verification feasible at a much larger scale \ Anthropic

Anthropic 用 Claude 对 123 个开源软件库(在超过 100 个库中)在 RISC-V 汇编代码层面证明安全性质,包括程序在特定条件下不会触发内存安全错误或断言失败,作者称这是首次大规模用 AI 对生产软件做汇编级形式化验证。证明过程中 Claude 还发现了多年未被检测到的 bug;为防 reward hacking,团队对任何补丁施加惩罚,并奖励证明补丁未改变程序行为。

为什么值得看

Anthropic 用 Claude 对百余个开源库做汇编级形式化验证,正文包含具体规模和防 reward hacking 的做法,读者可了解其方法与局限。

阅读原文 ↗10月23日 AI 日报

本页的摘要、推荐理由与解读出自 AIsa Studio 的 AI 风向智能体,原文版权归原作者所有。

深度解读

发生了什么

Anthropic 与 Theorem 合作发布了一项成果:用 Claude 对 123 个开源软件库完成汇编级形式化验证。团队把 Google OSS-Fuzz 中这些库的 fuzz 测试自动转化为形式化规约,再由 Claude 生成证明、Rocq 内核机械检查,最终在 239 个 fuzz 入口点上证明了「对于任何小于 64 MB 的输入都不会崩溃」。原文称,据其所知这是首次大规模用 AI 对生产级软件在汇编代码层面做形式化验证。

关键细节

  • 验证对象是编译后的 RISC-V 二进制,基于 Theorem 提供的 RISC-V 指令与 Linux 进程形式语义构建规约。
  • 全部证明约 1.8 亿行 Rocq 代码,单个典型证明约 60 万行,用于担保中位长度约 1.8 万条 RISC-V 指令的二进制。
  • 开发者需要信任的东西只有三样:RISC-V 与 Linux 进程的形式模型、被证明的定理陈述本身、Rocq 证明检查器——证明主体无需人工审查。
  • 证明过程中 Claude 发现并修补了数百个 bug,包括一个仅在某些约 20 MB 输入的特定布局下触发的解压计数器下溢(越界写)、需要 64 MB 输入和 2 GB 内存的图像解码器越界读、以及重新发现 libexpat 的 CVE-2026-56132。
  • 其他案例:Claude 为 RFC 1950 写规约并证明 Linux 内核 zlib 实现符合规约(在 x86 二进制层面);用 Rust 写的 Python 解释器经 Verus 验证后,在 170 万个随机程序上与 CPython 行为一致率达 99.95%;对某常用 PDF 读者的一周验证发现的漏洞数量相当于通常一年的报告量。
  • 成本在下降:36 个小型验证任务中,Opus 5.5 比 Opus 5 少用约 40% token;允许 agent 共享工作区互相复制证明可再省 17% token。

为什么重要

形式化验证历史上成本极高——原文举例:seL4 内核实现花 2 人年,验证却要 20 人年——因此长期只用于空客航电这类任务关键系统。LLM 使「编写比代码大许多倍的证明」这一环节自动化,把验证成本压到可扩展的水平。

更重要的是安全性背景:Linux 内核近期披露超过 1000 个漏洞,且前沿模型越来越擅长把看似无害的小 bug 串成关键利用链。形式化验证提供的「证明代码满足安全性质」的能力,正是传统测试与 fuzzing 无法给出的保证——fuzzing 是暴力搜索,永远不能穷尽所有输入路径,而证明可以。

同时原文坦承局限:「formally verified」不等于「bug-free」。证明只覆盖规约所刻画的世界:目前的证明假设程序是单线程静态二进制、固定内存预算、RISC-V + Linux(musl libc);而多数实际部署是 x86/ARM + glibc。ASan 也检测不到所有内存安全问题,且未验证每个库的所有入口点。

对谁有影响

  • 开源维护者与依赖关键库的团队:这些被证明的库正是被广泛依赖的基础组件;Theorem 还为其他开源项目提供同样的验证管线,并发布了帮助开发者上手的 Claude skill。
  • 安全从业者:验证过程本身即是高产的 bug 发现手段,即使最终证明未完成,尝试过程也能挖出「fuzzer 几乎不可能碰到」的深层漏洞。
  • 基础设施生态:由于依赖呈幂律分布,证明少量共享库(如 glibc、OpenSSL、cURL)即可覆盖大部分软件——Theorem 已公布验证整个 Nix 生态的路线图。
  • 使用旧版模型尝试过形式化验证而失败的团队:原文明确鼓励他们用更强的模型重新尝试。
全文翻译

以下翻译只覆盖原文的前一部分,其余内容请看下方原文。

Keane Lucas, Aengus Lynch, Luna Tong, Luke Champine, Zygi Straznickas, Dino Distefano, Harshikaa Agrawal Evyatar Ben Asher, Ava Howell, Sami Jawhar, Chris Rohlf, Nickolai Zeldovich Nicholas Carlini, Rajashree Agrawal, Jason Gross_

大多数软件都充斥着漏洞,而 AI 现在可以轻松发现并利用它们。针对网络攻击的标准防御手段已不再足够:我们需要证明代码没有漏洞的方法。形式化软件验证可以提供这种保证,但历史上它需要专家花费数年时间,这使得它对大多数实际项目来说并不切实际。然而,在与 Theorem 的合作中,我们表明情况已不再是这样:LLM 现在可以大规模生成形式化证明,从而启用包含经过验证的安全保证的新软件开发工作流。使用 Claude,我们在超过 100 个真实世界的开源软件库中证明了汇编代码级别(RISC-V)的安全属性。据我们所知,这代表了首次大规模利用 AI 在汇编代码层面对生产级软件应用形式化验证的努力。 在编写证明的过程中,Claude 还发现了多年来一直未被检测到的漏洞,尽管这些软件已经经历了数万亿次的测试运行。随着模型能力的不断提升,我们预计形式化验证将成为安全软件开发不可或缺的一部分。

简介

即使是成熟且经过大量测试的软件也包含漏洞,而 LLM 正越来越擅长发现它们。Linux 内核——当今最受测试、部署在全球数十亿设备上的软件之一——最近披露了超过 1,000 个漏洞。随着前沿模型越来越有能力将看似无害的漏洞链接成关键利用,我们需要能够在这些漏洞被野外利用之前检测和修复它们的技术。

理想情况下,这样的技术可以找到并修补_所有漏洞_,然后提供某种证明,表明没有更多漏洞可被发现。_形式化验证_就是实现这一目标的一种方式。在形式化验证中,开发人员将他们的代码转换为数学模型,然后尝试证明它始终遵循一组定义的安全规则。如果他们无法证明这一点,那就意味着代码中可能存在违反规则的漏洞,而失败的性质暗示了漏洞可能是什么。重复此过程(每次消除失败原因后)最终将产生证明,表明代码不包含任何违反这些规则的漏洞。

然而,形式化验证并不容易。证明可能比原始代码大很多倍,编写它们需要大量的努力以及 niche 且高度专业化的技能集。例如,实现 seL4 操作系统内核花费了两个人年,但_形式化验证它_却花了_20_个人年[https://sel4.systems/Research/pdfs/comprehensive-formal-verification-os-microkernel.pdf]。因此,形式化验证仅在真正任务关键型的应用程序中才实用,例如Airbus 的航空电子代码。

但 LLM 使其变得容易得多。前沿模型现在可以完全自主地为真实世界软件生成正确性证明。为了展示这一点,我们要求 Claude 证明 123 个开源库中的有用安全属性,包括证明其中一些可执行工件在某些条件下_永远不会_崩溃。具体而言,Claude 证明了这些程序在以下假设下(即程序作为单线程静态二进制文件运行,在 RISC-V1 CPU 上使用 Linux OS 并具有固定内存预算),对于小于 64 MB 的每个输入以及每个模拟的 Linux 操作系统 (OS) 响应和内存布局,总是正常退出而不触发内存安全错误或断言失败。这距离修复_所有_可能的漏洞仍然很远,但它确实让我们比以前更接近这一目标,并提供了一条通往该目标的路线图。

我们正在发布这些证明的子集,以便任何人都可以验证和重用它们,以及RISC-V 和 OS 语义。我们在这个项目中的合作伙伴 Theorem 也表示愿意为其他开源项目运行相同的管道,并发布一个 Claude skill 以帮助开发人员开始验证他们自己的代码。

形式化验证是一个拥有多种方法论的广泛领域。在下面,我们还描述了使用其他形式化验证方法加速其他安全或软件开发任务的初步探索,例如评估 zlib 压缩算法的安全性、证明用 Rust 编写的 Python 解释器的行为等价性,以及根除关键软件中长期存在的漏洞。

形式化验证的未来充满希望。随着模型变得更加智能,编写证明的成本正在下降。此外,随着更多代码被形式化以及实现安全保证的方法变得更加标准化,跨代码库重用或修改证明的能力将使成本分摊。我们鼓励所有开发人员探索与 LLM 一起的形式化验证——_尤其是_那些之前尝试过使用能力较弱的模型并发现其无效的开发人员。

在本文的其余部分,我们将更详细地介绍我们的形式化验证实验。

将测试转化为证明

要证明一段软件是正确的,我们必须首先说明“正确”究竟意味着什么。正确性由_规范_(spec)定义。在某些程序中,有明确的方式来形式化指定程序的预期行为。例如,PNG 解码器接受一系列字节并按照 RFC 2083 将这些字节解码为相应的图像。但对于大多数程序来说,事情并没有那么简单。文本编辑器或 Web 浏览器“正确”是什么意思?获得全面且无歧义的规范是大规模验证软件的一个重大障碍。

为了解决我们要证明的开源库代码中的这个问题,我们采取的方法是通过编码“此测试套件始终通过”的规则,将其现有的可编译测试套件自动转换为规范,使用关于在任何 Linux 上执行任何 RISC-V 编译的二进制文件的含义的一般模型。历史上,鉴于编写涵盖数万条汇编指令在所有可能行为和交互上的证明的内在复杂性(以及因此证明往往极其庞大),证明此类规范是不可行的。这就是今天的 LLM 发挥作用的地方:为了实现明确的目标而处理数百万行代码是一项 LLM 非常擅长的任务。

对于这个项目,我们从_fuzz 测试_开始。在这些测试中,开发人员编写进入他们代码的_入口点_2,使得任何导致崩溃的输入都是作者关心的漏洞。然后,fuzzer(一种测试引擎)在入口点生成海量的半随机和变异输入,试图找到漏洞(如图 1 所示)。Fuzzer 运行时间越长,它能发现的漏洞就越多。但这本质上是一个蛮力过程,无论您尝试多少输入,都无法保证覆盖所有可能的代码路径。

Fuzzing 的工作原理是将海量生成的输入通过一个入口点运行,以找到导致崩溃的输入。 输入是无限的,因此搜索永远无法完成。

为了找到 fuzz 测试会暴露的所有漏洞,我们将 fuzz 测试转化为针对编译后的 RISC-V 指令定义的规范。该规范指出,对于_任何_小于 64 MB 的输入,fuzz 测试都不会崩溃3。如果 Claude 能证明这个规范,那么 RISC-V 上的 fuzz 测试就变得多余了:对该入口点的 fuzzing 永远不会发现任何漏洞。

我们在 Google 的 OSS-Fuzz 计划中对数百个项目进行了此操作,该计划为符合条件的开源项目提供持续的模糊测试。OSS-Fuzz 包含数千个由开发者编写的入口点,通过崩溃来寻找损坏的代码属性(例如,在断言失败时)。OSS-Fuzz 自动使用 AddressSanitizer4 (ASan) 对 C 和 C++ 程序进行插桩,使它们在访问内存不当(这是软件漏洞的主要原因之一)时崩溃。像内存安全这样的广谱规范适用于几乎所有软件。

_开源库及其现有的 fuzz 测试被编译为 RISC-V 可执行文件。该可执行文件,连同 Theorem 的 RISC-V 和 OS 形式化语义以及所需的无崩溃属性,机械地产生证明必须满足的规范。Claude 编写证明;Rocq 内核根据规范检查它。绿色标记表示已证明的内容,前提是受信任组件正确。__

在几周内,Claude 成功修补了它发现的漏洞,然后证明了在 123 个不同的 OSS-Fuzz 库的 239 个入口点上,对于任何输入(最多 64 MB)都没有崩溃。 这些证明作用于编译后的 RISC-V 指令(即可执行二进制文件),它们由大约 1.8 亿行 Rocq 组成,典型的证明运行约 60 万行,为中位长度为 18,000 条 RISC-V 指令的二进制文件提供安全保证。这些证明是通过使用 Theorem 的 RISC-V 指令和 Linux 进程语义将 RISC-V 汇编代码翻译成数学模型构建的。这意味着这些证明目前假设程序作为单线程静态二进制文件运行,在 RISC-V CPU 上使用 Linux OS 并具有固定内存预算。扩大这些证明的范围是完全可行的,因为它涉及为更多的汇编架构和其他操作系统进程标准化语义。请参阅专家对 Claude 编写的证明的看法。

对所有输入的正式无崩溃证明排除了从入口点可达的任何路径上的所有崩溃。 在迭代其证明的过程中,Claude 还可以找到并修复任何剩余的会导致崩溃的可达漏洞。

虽然这是一大堆证明代码,但几乎不需要人工审查。Rocq 证明检查器确保证明正确满足了规范。因此,开发人员唯一需要信任的工件是:

  • RISC-V 和 Linux 进程的形式化模型;
  • 被证明的确切实例陈述;
  • Rocq 证明检查器本身5。

这并不意味着这些库中没有漏洞。 相反,这意味着任何剩余漏洞要么存在于上述列出的工件中,要么不在 Claude 证明的范围之外。加固那些仍可能有漏洞的受信任组件(Rocq、RISC-V 和 Linux 语义)并非微不足道,但与调试每一行扩展出去的代码相比,这是一个更容易处理的任务。

查看原文

Keane Lucas, Aengus Lynch, Luna Tong, Luke Champine, Zygi Straznickas, Dino Distefano, Harshikaa Agrawal
Evyatar Ben Asher, Ava Howell, Sami Jawhar, Chris Rohlf, Nickolai Zeldovich
Nicholas Carlini, Rajashree Agrawal, Jason Gross

Most software is riddled with bugs, which AI can now easily find and exploit. The standard defenses against cyberattacks are no longer sufficient: we need ways to prove that code is free of bugs. Formal software verification could provide this guarantee, but it has historically required years of expert effort, making it impractical for most real-world projects. In a collaboration with Theorem, however, we show this is no longer the case: LLMs can now produce formal proofs at large scale, enabling new software development workflows that include proven security guarantees. Using Claude, we proved security properties at the assembly code level (RISC-V) in over 100 real-world open-source software libraries. To our knowledge, this represents the first large-scale effort to use AI to apply formal verification to production software at the assembly code level. While writing the proofs, Claude also discovered bugs that had eluded detection for years, despite trillions of test runs. As model capabilities continue to improve, we expect formal verification to become an essential part of secure software development.

Introduction

Even mature, heavily tested software contains bugs, and LLMs are increasingly adept at finding them. The Linux kernel—one of the most well-tested pieces of software today, deployed on billions of devices worldwide—recently had over 1,000 vulnerabilities disclosed. With frontier models becoming more capable of chaining seemingly benign bugs into critical exploits, we need techniques that can detect and fix these bugs before they can be exploited in the wild.

Ideally, such techniques could find and patch all bugs, and then provide some proof that there are no more to be found. Formal verification is one way to do this. In formal verification, developers translate their code into a mathematical model, then try to prove that it always follows some defined set of security rules. If they fail to prove this, that means there could be a bug in the code that would violate a rule, and the nature of the failure implies what the bug might be. Repeating this process (after eliminating the cause of failure each time) will ultimately yield proof that the code is devoid of any bug that would violate those rules.

However, formal verification isn’t easy. Proofs can be many times larger than the original code, and writing them requires significant effort and a niche, highly specialized skill set. For instance, it took two person-years to implement the seL4 operating system kernel, but 20 person-years to formally verify it. Accordingly, formal verification has been practical only in truly mission-critical applications, such as Airbus’s avionics code.

But LLMs are making it far easier. Frontier models can now produce correctness proofs for real-world software fully autonomously. To demonstrate this, we asked Claude to prove useful security properties across 123 open-source libraries, including showing that some of their executable artifacts can never crash under certain conditions. Specifically, Claude proved that these programs will always exit normally without triggering memory-safety errors or failing assertions, for every input less than 64 MB and every modeled Linux operating system (OS) response and memory layout, under certain assumptions (namely that the program runs as a single-threaded static binary with a fixed memory budget on a RISC-V1 CPU using Linux OS). This is still far away from fixing all possible bugs, but it does get us much closer than before and provides a roadmap toward that end.

We’re publishing a subset of these proofs so that anyone can verify and reuse them, along with the RISC-V and OS semantics. Theorem, our collaborator on this project, is also offering to run the same pipeline for other open-source projects, and is publishing a Claude skill to help developers get started with verifying their own code.

Formal verification is a broad field with many methodologies. Below, we also describe our initial explorations using other formal verification methods to accelerate other security or software development tasks, such as assessing the security of the zlib compression algorithm, proving behavioral equivalence of a Python interpreter written in Rust, and rooting out long-standing bugs in critical software.

The road ahead for formal verification is promising. The cost of writing proofs is falling as models become more intelligent. Moreover, as more code is formalized and methods of achieving security guarantees become more standardized, the ability to reuse or modify proofs across codebases will amortize the cost. We encourage all developers to explore formal verification with LLMs—especially those who previously tried it with less capable models and found it didn’t work.

In the rest of this post, we cover our formal verification experiments in more detail.

Turning tests into proofs

To prove that a piece of software is correct, we must first state what “correct” even means. Correctness is defined by the specification (spec). In some programs, there is a clear way to formally specify the program’s intended behavior. For example, a PNG decoder takes a sequence of bytes and (following RFC 2083) decodes these bytes into the corresponding image. But for most programs, things aren’t so straightforward. What would it mean for a text editor or a web browser to be “correct”? The difficulty of obtaining a comprehensive and unambiguous spec is a significant obstacle to verifying software at a larger scale.

To address this for the code in the open-source libraries that we sought to prove, we take the approach of automatically converting their preexisting compilable test suites into a spec by encoding the rule “this test suite always passes,” using a general model of what it means to execute any RISC-V-compiled binary on Linux. Historically, proving such specs was intractable, given the inherent complexity of writing a proof that can encompass and make an argument over all possible behaviors and interactions of tens of thousands of assembly instructions over any input (and the fact that proofs tend to be extremely large as a result). This is where today’s LLMs come in: churning through millions of lines of code in pursuit of a well-defined goal is a task that LLMs are extremely well suited for.

For this project, we started with fuzz tests. In these tests, developers write an entry point2 into their code that takes inputs, such that any input that causes a crash is a bug the authors care about. Then the fuzzer, a testing engine, generates enormous numbers of semirandom and mutated inputs at the entry point to try to find a bug (visualized in Figure 1). The longer a fuzzer runs, the more bugs it can find. But this is inherently a brute-force process, with no guarantee of exercising every possible code path, regardless of how many inputs you try.

Fuzzing works by running huge numbers of generated inputs through one entry point to find the inputs that cause a crash. There are infinite inputs, so the search can never be complete.

To find all the bugs that would’ve been surfaced by a fuzz test, we turned the fuzz test into a spec defined over the compiled RISC-V instructions. The spec says that the fuzz test won’t crash for any input less than 64 MB.3 If Claude can prove this spec, the fuzz test on RISC-V becomes superfluous: fuzzing that entry point will never find any bugs.

We did this for hundreds of projects in Google’s OSS-Fuzz initiative, which provides continuous fuzz testing for qualifying open-source projects. OSS-Fuzz contains thousands of developer-authored entry points that look for broken code properties by crashing (for example, on a failed assertion). OSS-Fuzz automatically instruments C and C++ programs with AddressSanitizer4 (ASan), causing them to crash whenever memory is accessed improperly (which is a leading cause of software vulnerabilities). Broad-spectrum specifications like memory safety apply to almost all software.

The open-source library and its existing fuzz test are compiled to a RISC-V executable. This executable, along with Theorem’s RISC-V and OS formal semantics and the desired no-crash property, mechanically yield a specification the proof must satisfy. Claude writes the proof; the Rocq kernel checks it against the specification. Green marks what’s proven, conditional on the trusted components being correct.

Over the span of a few weeks, Claude successfully patched the bugs it found and then proved that there are no crashes on any input (up to 64 MB) for 239 entry points across 123 different OSS-Fuzz libraries. These proofs operate on the compiled RISC-V instructions (i.e., executable binaries), and they comprise about 180 million lines of Rocq, with a typical proof running to about 600,000 lines to provide security guarantees on binaries with a median length of 18,000 RISC-V instructions. These proofs are built by translating the RISC-V assembly code to a mathematical model using Theorem’s RISC-V instruction and Linux process semantics. This means these proofs currently assume that the program runs as a single-threaded static binary with a fixed memory budget on a RISC-V CPU using Linux OS. Expanding the scope of these proofs is well within reach, as it would involve standardizing semantics for more assembly architectures and other operating system processes. See an expert’s take on the proofs Claude wrote.

A formal no-crash proof over all inputs rules out all crashes along any path reachable from the entry point. While iterating on its proof, Claude can also find and fix any remaining reachable crash-producing bug.

While this is an enormous volume of proof code, virtually none of it needs to be manually reviewed. The Rocq proof checker ensures that the spec is correctly satisfied by the proof. As a result, the only artifacts that a developer needs to trust are:

  • The formal models of RISC-V and of Linux processes;
  • The exact theorem statements that were proved;
  • The Rocq proof checker itself.5

This doesn’t mean these libraries contain no bugs. Instead, it means that any remaining bugs either are to be found within the artifacts listed above or are outside the scope of Claude’s proof. Hardening those still-possibly-buggy trusted components (Rocq, RISC-V, and Linux semantics) is not insignificant, but it’s a far more tractable task than debugging every line of code the scaled-out proofs cover—especially as this approach can secure an arbitrary number of projects without increasing the size of the trusted components.

We welcome independent verification of our proofs and encourage refinement of our approach. To that end, we are publishing the trusted components (above) made for this project that are not already public; Claude’s complete Rocq proofs for fuzz tests that did not require patching; and specifications for other fuzz tests we were not able to prove.

Finding and fixing bugs

Above, we showed that you can turn many open-source libraries’ developer-written tests (fuzz tests) into specs that Claude can prove will always pass, sometimes after patching the remaining bugs it found. For this reason, the process of attempting to prove these specs discovered hundreds of bugs.

Lots of bugs showed up during Claude’s attempts to prove hundreds of entry points. To ensure that Claude didn’t have to stop for a human every time this happened, we let Claude fix the bugs it found: it patched the code, which caused the spec to be regenerated with that patched code, and then Claude proceeded to prove that new spec.6

Many of the bugs we found are ones that a fuzzer would be unlikely to ever find. Fuzzers mutate small inputs, typically capped at 1 MB, and typically rely on code coverage to guide them toward bugs. While proving an entry point in one library, Claude found a counter underflow in its copy of a decompressor that led to an out-of-bounds write, but only on inputs of around 20 MB that had one rare, specific layout. Other bugs include an out-of-bounds read in an image decoder that needed a 64 MB input and over 2 GB of memory, an out-of-bounds read in another library that needed a particular sequence of calls across two handles to the same file, and a rediscovery of CVE-2026-56132 in libexpat, which needed deeply nested input. Claude also found logic bugs that never crash at all. While proving that one library was memory-safe, for example, it noticed that a feature for restricting which files an application can load isn’t always enforced.

The ultimate goal of this project was to achieve a proof that the RISC-V-compiled fuzz tests had no remaining crashes to find. However, bugs like the above show that the process has another benefit, regardless of whether that goal is achieved: even just attempting this process yields real bugs that need to be fixed.

Proving more than memory safety

Proving that a fuzz test has no more crashes to find is more significant than it sounds. It’s common for fuzz tests in real-world code to test for violations of meaningful high-level properties, such as encode-decode roundtripping, idempotence, algebraic laws, differential testing, self-consistency, or equivalence to reference implementations. For example, a fuzz test for the LZO compression algorithm asserts that if you compress an input, then decompress it, the result is the same as the original. If the property does not hold, the program crashes—so proving the test never crashes is equivalent to proving the property itself.

This can also occur even when the properties are not directly specified by the developer. For example, in order for Claude to complete its OSS-Fuzz proof for cpp-httplib, it was necessary to rule out the possibility of a stack overflow due to too much recursion in the std::set destructor. This recursion depth is bounded by the maximum depth of a tree, so Claude had to prove (on machine code!) that libc++’s red-black tree rebalancing algorithm is correctly optimized to keep the tree’s depth logarithmically bounded.

Other formal verification case studies

Formal verification is an expansive domain. The fuzz-test-to-proof example above is one instance of LLMs making formal verification more widely usable by directly applying proofs to machine code.

We’ve also seen success using LLMs with formal verification in other ways. For example, we asked Claude to write a formal spec of RFC 1950, the technical document that defines the zlib compression file format. Then, we had Claude prove that the zlib implementation in the Linux kernel correctly implements that spec. As with the examples above, Claude proved this property at the binary level, except instead of RISC-V, Claude directly proved the x86-compiled binary using Claude-generated (and heavily tested) formal semantics of the x86 ISA.7 We then repeated this process for half a dozen other widely used libraries. Along the way, we found some minor (unexploitable) bugs in corner cases, which are in the process of being filed upstream. We draw two conclusions from this exercise: (1) these extremely well-tested codebases are already almost fully correct, and (2) it is possible to prove end-to-end correctness properties about real existing codebases.

In another experiment, we asked Claude to develop an interpreter for a large subset of Python in Rust, and to prove (using Verus, a verification tool for Rust) that it adheres to the CPython semantics outlined in an academic paper. We then measured the adherence to CPython’s behavior empirically, running over 1.7 million randomly generated Python programs, finding identical behavior with CPython in 99.95% of tests, with remaining test differences as unsurprising known corner cases.

We also found issues in several widely used and critical applications via formal verification attempts of simple security properties. In one of them, we discovered a previously unknown logic bug that had been latent for more than a decade. During an effort to verify a widely used PDF reader, we found as many security bugs in one week as are typically reported in a year.

See the Appendix for more details on some of these efforts.

This is just the beginning

One of the key limitations of LLM-assisted formal verification is that the proofs can take hundreds of millions of tokens to produce. Cost-efficiency is critical if our goal is to make these methods available to the entire software ecosystem. Fortunately, there are compelling reasons to expect efficiency to improve.

Newer models need fewer tokens

We built 36 small verification tasks from real OSS-Fuzz code. Opus 5.5 proves half of them with about 40% fewer tokens than Opus 5. We expect this trend to continue as models are trained on long verification tasks.

Line chart of the share of 36 verification tasks completed by token budget per agent for Opus 5.5, Fable 5.1, and Opus 5. Opus 5.5 finishes proofs with the fewest tokens.

Opus 5.5 finishes proofs faster than earlier models. Share of 36 formal verification tasks proved by a single agent at a given token budget. Each task received three independent single-agent attempts at a 5M-token budget, and counts as proven when the first attempt finishes.

Proofs can be reused

When agents work separately, they often end up duplicating a lot of effort. Proofs, like software, are recursively built from smaller proofs—and the smaller the proofs are, the more reusable they tend to be. To measure the impact of this, we let agents copy each other’s proofs as they worked on proving their own targets. When given access to this shared workspace, agents needed 17% fewer tokens than when they worked alone, and some agents even succeeded after previously failing in a solo attempt.

Chart of 36 proofs in finishing order, with arcs joining proofs that share a file and bars showing the tokens spent on each proof compared with an agent working alone.

The 36 proofs from one shared workspace, in the order they finished. An arc joins any two proofs that share an unchanged file; bars show the tokens spent writing each proof, and black lines show the same task’s average cost for an agent working alone, which was higher in 30 of the 34 completed proofs (agents working alone were not able to complete two of the proofs).

We observed the same phenomenon occurring at scale during our OSS-Fuzz proof effort. Most of a proof concerns code that every program contains (particularly initialization of the C runtime and AddressSanitizer), so very different programs can end up with significantly overlapping proofs. Agent teams took advantage of this by copying another team’s finished proof and then adapting it to their own program (Figure 6).

Block chart of the proofs for libcups, freerdp, and libzmq, showing how much of each part was identical, lightly edited, edited more or new.

Agents copied an existing proof and adapted it to each new program. Each column represents one program’s complete proof by share of bytes. Blocks are the parts of the proof and each block shows how much was kept identical, lightly edited (same file, size within 2%), edited more, or newly written. The shared parts gained almost no new code.

This reusability of proofs is how Mathlib, a shared library of formal mathematics in Lean, grew to nearly 300,000 theorems submitted by over 700 contributors: each new proof builds on the existing body of work. We accumulated a similar body of proofs in the course of our OSS-Fuzz effort, some of which we’re including in our release (we are not releasing proofs that may reveal an unpatched bug in the open-source software).

As we graduate from these initial experiments, we need a strategy for verifying the enormous body of existing software, starting with the most critical dependencies. As we’ve shown, this can be achieved at scale by using generic specs and working at the binary level, sidestepping the need to rewrite entire codebases in another language.

This approach also facilitates the reuse of proofs at package-level granularity, which means that each dependency only needs to be verified once. Dependencies follow a power law: proving even a small set of shared libraries makes a huge swath of software feasible to prove. For more details, see Theorem’s roadmap for verifying the entirety of the Nix ecosystem, which includes critical infrastructure like glibc, OpenSSL, and cURL.

Limitations

In practice, formal verification can sometimes prove less about the security of the code than we would like.

“Formally verified” does not mean “bug-free.” Claude can write a proof showing that a compiled program’s behavior satisfies the requirements of a formal spec (which is verified by an external proof checker). However, a proof checker can’t tell you whether your spec matches reality. To match reality, the spec must correctly encompass and model everything about the real-world machine that the verified program runs on, including the CPU and operating system. When bugs show up in verified code, it’s because the spec, or the assumed semantics of the machine, differs from reality. Fortunately, this also means we know where to look for remaining bugs, and how to design a system where those still-possibly-buggy gaps are minimized. Below are the ways this applies to our proofs, and ideas for future work that would close those gaps.

The real machine does something the model doesn’t describe. Our proofs are about single-threaded, static RISC-V binaries linked against musl libc. However, most people run these libraries on x86 or ARM and dynamically link them against glibc. To close this gap, we’d need system models that more closely resemble deployed systems. For example, a step toward this would be generating accurate semantics for x86.

The claim may not cover everything we care about. We proved that a crash never triggers for any input under 64 MB on many fuzzing entry points in Google OSS-Fuzz, including ASan crashes. However, ASan doesn’t detect all memory safety bugs, and we didn’t verify all the entry points for each library. Even if we had, the whole library wouldn’t be covered by verifying all entry points (especially if there is dead code). Additionally, real programs might misuse the library, leading to memory corruption in the caller. To close that gap, we’d need to specify what callers must do for each public function, and callers would have to strictly adhere to it.

We expect that as we learn more about leveraging LLMs for formal methods, and as models continue to improve, these limitations will become tractable, and we’ll be able to verify a much larger percentage of the code we rely upon.

Conclusion

The trajectory is clear: formal verification, which was historically practical only for bespoke expert-crafted guarantees in the most safety-critical code, will soon be as practical as fuzzing is today for almost all production software. Just like static types, testing frameworks, and other correctness tooling, verification will mature from “bolted-on” to “built-in,” driven not only by LLMs but also by innovations in language design and development workflows.

In today’s world—where LLMs automatically scan for and find increasingly more code vulnerabilities—we must use processes that can guarantee code security properties. LLM-scaled formal verification is a promising approach toward securing these guarantees at a scope and scale that were previously unattainable. We encourage others to experiment so we can collectively build a world in which robust software has proliferated very widely and software bugs are a well-understood and quickly addressable rarity.

Read the full appendix.

原文版权归 官方博客 · Anthropic / Meta / xAI 及原作者所有,本站转载仅供阅读。如需删除,请联系 support@aisa.one。

事件综述

Anthropic 用 Claude 对百余开源库完成汇编级形式化验证

2026 年 10 月 22 日,Anthropic 发布 Frontier Red Team 博客新研究,与 TheoremLabs 合作,用 Claude 在数周内为一百多个真实开源库证明安全属性。形式化验证可证明代码不存在特定 bug,但此前仅适用于小型代码库或需大量专家时间,AI 模型正改变这一局面。10 月 23 日的官方博客跟进披露更多细节:团队对 123 个开源库在 RISC-V 汇编代码层面证明安全性质,包括程序在特定条件下不会触发内存安全错误或断言失败,作者称这是首次大规模用 AI 对生产软件做汇编级形式化验证;过程中 Claude 还发现了多年未被检测到的 bug,团队为防 reward hacking 对任何补丁施加惩罚,并奖励证明补丁未改变程序行为。同日 The Decoder 报道称,Claude 将崩溃测试转化为数学问题、分析机器码并生成约 1.8 亿行证明,由 Rocq 独立自动校验,几乎无需人工复核;验证某广泛使用的 PDF 阅读器时,一周发现的安全漏洞相当于其一年报告量,还抓到一个潜伏十余年的逻辑错误。报道同时指出限制:证明仅在特定处理器架构、单线程和 64 MB 以内输入下成立,单个证明耗资最高数亿 token;Opus 5.5 比 Opus 5 用约少 40% 的 token 解决一半证明;Theorem 计划将方法推广到 glibc、OpenSSL 和 cURL。

其他来源的报道 2