What does “verified memory safe” even mean?

Our investigation of the specifications behind Anthropic and Theorem’s verification results.

Umang MathurPrateek Saxena

Tianyu LiBo WangDiego MeyerRuishi Li

National University of Singapore

A collaboration between FOCS Lab and KISP Lab.

Audience

This post is intended for a general audience. Many of the issues we discuss will be familiar to researchers in security and formal methods. We explain them here to help readers assess what Anthropic and Theorem’s announcement means for cybersecurity. Our technical findings are in the paper.

Large-scale verification breakthrough

On 22 October 2026, Anthropic and Theorem announced that they had used AI to generate proofs for programs built from many open-source libraries.

For readers unfamiliar with the background, there are two reasons this announcement deserves attention.

First, AI is making it easier to find security bugs. This makes the search for ways to secure our software more urgent. Formal methods have long offered a promising approach. They use mathematics to reason about software. Formal verification is one part of this field: we state requirements precisely and prove that a program meets them. Such proofs can establish that entire classes of bugs are absent.

Second, making formal verification scale has long been difficult. There are fundamental limits to what can be automated, and real software is complex. Researchers have spent decades developing techniques to tackle these difficulties, and successful projects have required years of expert effort. The CompCert compiler’s correctness proof, for example, required about six person-years of work. The IronFleet project reported roughly four person-years to develop its methods and build and verify two distributed systems.

This helps explain why producing proofs for much more software, much faster, would be a major advance.

Methodology used in the breakthrough

Let us now look more closely at what Anthropic and Theorem actually proved. This will make our concerns easier to understand.

The team wanted to address memory-safety bugs, a major source of security vulnerabilities.1 For this, they turned to AddressSanitizer (ASan), a widely used tool for detecting memory-safety bugs.

In essence, when the proof effort succeeds for a program, that program does not have bugs that ASan can expose.2

How did they prove this?

Their approach made good use of OSS-Fuzz, Google’s service for finding bugs in open-source software.

OSS-Fuzz uses tools such as ASan to add checks to a program. When ASan detects a memory error, it makes the program stop and report it. A bug that might otherwise go unnoticed becomes an explicit failure. The fuzzer then runs this modified program on many different inputs, looking for one that triggers a failure.

Anthropic and Theorem turned this into a verification problem. They proved two things:

  1. The original program and the version with ASan checks behave the same.
  2. The version with ASan checks does not crash.

Together, these give the guarantee stated above.

What does this mean for security?

At this point, it is imperative to ask: how should we understand what they have achieved? Have Anthropic and Theorem solved cybersecurity? Have they solved memory safety? We don’t think so.

Memory safety ≠ freedom from bugs that ASan can expose.

This distinction will be familiar to many Computer Science researchers, especially those working in software security, formal verification and programming languages. Researchers have long discussed what memory safety means and clarified the guarantees that different tools provide.3 We explain the distinction here for readers who may not have considered it carefully.

ASan is an effective bug finder. Its original paper already reported hundreds of previously unknown bugs found in Chromium and other software.4 But ASan also misses memory-safety bugs—even when the faulty memory access actually happens.5 Proving that a program never triggers an ASan error therefore does not establish that it is memory safe.

The examples below explain this gap, including how it arises in OpenSSL. In our paper, we discuss the technical details in depth.

Examples: memory-safety bugs that ASan can miss

Writing past the end of a buffer. Imagine a structure containing two buffers, one after the other. A write intended for the first buffer spills into the second. This violates the first buffer’s bounds, but stays within the surrounding structure. ASan can miss this error because it does not ordinarily place checks between fields within a structure.5

Figure 1. Inside the allocation, outside the buffer. The write crosses A’s boundary but stays inside the surrounding structure. ASan can miss this error.

Using a temporary after its lifetime ends. A program may keep a pool of temporary objects so it can reuse their storage. A computation obtains an object, uses it and then releases it back to the pool. After release, the old reference must no longer be used. But the pool keeps the underlying memory allocated.

ASan does not automatically know when the program releases an object back to its own pool. Using the old reference can therefore violate the object’s lifetime without triggering an ASan error.

OpenSSL uses such a pool for temporary numbers in cryptographic computations. Its documentation explicitly says that references to these temporaries become invalid when they are released. Our paper examines how this lifetime rule is reflected in the specifications used in the proofs.

Figure 2. The storage outlives the temporary. After release, the old reference must no longer be used, even though its storage remains allocated. ASan does not automatically know the pool’s lifetime rule.

So here is the takeaway in one line:

A proof of freedom from bugs that ASan can expose does not imply memory safety.

The blog post does acknowledge this issue as part of its broader discussion of limitations. It says:

However, ASan doesn’t detect all memory safety bugs…

But this message may get lost when different limitations are clubbed together as gaps that will be easily closed in the near future. Not all disclaimers are equal. The proofs could be extended to other processors. But that would not solve memory safety. We first need to acknowledge that memory safety is a larger problem. It requires careful thinking about specifications. Producing proofs faster, or for more programs, does not by itself solve a problem on which the security of today’s infrastructure depends.

The harder problem: getting the specification right

At a high level, the message is simple: if you do not know what you are proving, you may end up proving something useless. For experts in formal verification, this has always been clear. The specification you check your program against must capture your actual intent.

As an example, we already know stronger specifications for memory safety than “ASan safety.” These account for individual objects, their boundaries and their lifetimes. (SoK: Sanitizing for Security) Our technical report examines these distinctions in the specifications used in this project.

Faster proofs do not tell us whether we chose the right requirement. Specifications deserve extra scrutiny. This is especially so when an AI is rewarded for producing an accepted proof. If the specification is too weak, the AI may find a shortcut that earns the reward while defeating the original intent. This is the risk of reward hacking: satisfying the formal target without achieving what we actually wanted.

Why getting the specification right is hard

Why is getting the specification right so hard? What even makes a good specification? At the very minimum, it must be understandable to people. A user should be able to tell what “I proved this program correct against this specification” actually guarantees. It must also be precise enough for a mathematical proof. But above all, it must capture what we actually intend. That is the hard part.

Sometimes, programming language features and types help express that intent. For example, an array type can make a buffer’s bounds explicit. Rust’s lifetime annotations let programmers express relationships between how long references remain valid.6 These and other features make Rust an attractive choice for writing memory-safe software. Many researchers are now developing ways to translate existing C software to safe Rust automatically.7 But programs can also manage their own logical objects, as in OpenSSL’s pool of temporary numbers. The memory can stay allocated even after a temporary should no longer be used. To specify that rule, we need to understand how the programmer intended the pool to work.

Sometimes, though, we do not yet know what the specification should say. We may discover new requirements years after the software has been deployed. Worse still, we may discover them only after the software fails in production or an attacker exploits a vulnerability. This also makes automatic specification inference hard. Inferring what the code does is different from deciding what it should do.

One thing is clear: with the technology we have right now, it is imperative to involve human judgement in choosing and reviewing specifications. Automating this task would require reliable ways to discover the right requirements and assess whether they capture the intended guarantees. Producing proofs at greater speed and scale is a substantial advance. But its value for security still depends on whether we are proving the right thing.

Footnotes

  1. Microsoft reported in 2019 that memory-safety issues caused roughly 70% of the vulnerabilities it fixed and assigned CVEs. Chromium reported a similar proportion for its serious security bugs. More broadly, GitHub reports a 21% increase in published CVE records across the CVE Project in 2025.

    ↑ Back to text
  2. OSS-Fuzz tests programs that use code from open-source projects. Many of these projects provide libraries that other software uses. The proofs cover the tested programs. Other software may use the same libraries differently, and those uses are not automatically covered.

    The guarantees also depend on several technical details:

    • The programs run one thread at a time.
    • They are compiled for RISC-V, a particular processor architecture.
    • Their library code is included in the executable. This is called static linking.
    • The proofs use a particular model of the operating system.
    • There are limits on input size and memory use.
    • “Behave the same” refers to observable behaviour. This includes input, output and exit status.
    ↑ Back to text
  3. See The Meaning of Memory Safety for a formal treatment, and SoK: Sanitizing for Security for an analysis of bug-finding tools and their limitations.

    ↑ Back to text
  4. AddressSanitizer: A Fast Address Sanity Checker.

    ↑ Back to text
  5. SoK: Sanitizing for Security, Sections IV-A and VIII-B, discusses missed errors, including overflows between fields within an object and accesses through stale pointers after memory reuse.

    ↑ Back to text · ↑ Back to example
  6. The Rust Programming Language: Validating References with Lifetimes.

    ↑ Back to text
  7. See ACToR, Reboot, and Ben Livshits’s discussion of automated rewrites for software security.

    ↑ Back to text