NEWS

Verus makes it possible to mathematically prove that Rust code is correct

Amazon uses the formal verifier Verus to check Rust code against mathematical specifications, going beyond traditional testing in critical components like the Nitro Isolation Engine.

Verus makes it possible to mathematically prove that Rust code is correct
Image: Redação iMasters

The Rust language already fixes a good part of the memory bugs that haunt C code, but "safer" is not synonymous with "correct." A Rust program can compile, pass every test, and still return the wrong result, leak a secret, or break an invariant that nobody thought to test. That's the gap Verus tries to close: an open-source program verifier that mechanically checks whether Rust code matches a formal specification, for all possible inputs, not just the test cases someone remembered to write.

The post is authored by Bryan Parno, Amazon Scholar and Carnegie Mellon professor who leads the Secure Foundations Lab, and details how Amazon already uses the tool to prove the correctness of primitives used by the Nitro Isolation Engine, the software that isolates virtual machines in AWS's Nitro hypervisor.

What changes compared to testing code

Traditional testing runs the program with a few input values chosen by whoever writes the test. A program verifier does something different: it takes a mathematical specification of the expected behavior and proves, for every possible input, that the code satisfies that specification. The example the post uses is binary search. The specification says two things: if the function returns Some(index), the element at that position in the array equals the value being searched for; and if it returns None, the value being searched for is nowhere in the array. That second clause isn't trivial: without it, an implementation that always returns None would satisfy the specification, and no single test would catch that error.

The classic case of an out-of-bounds array access illustrates well the problem that goes uncovered by tests. In C this is undefined behavior; in Rust, the program panics and stops, which is already safer, but a truly correct program should never attempt that access in the first place. Verus attacks exactly this kind of corner case that Rust's type system alone doesn't cover.

How the specification enters the code

The design decision that sets Verus apart from other verification approaches for Rust is where the specification lives: inside the Rust source file itself, with syntax similar to the language's. Preconditions use the requires keyword and postconditions use ensures, annotated directly on the function. In the post's binary search example, requires demands that the array be sorted, and ensures describes the return behavior already mentioned above.

A standard Rust compiler ignores these annotations, so the same file compiles normally with cargo in projects that don't use Verus. This means specifications can be added incrementally to an existing codebase without breaking anything for those who don't run the verifier. When the proof fails, the error shows up in the style of Rust's own errors, right in the IDE (the post mentions VS Code's "red squiggles"), because the tool uses a set of solvers to discharge the proof obligations generated from the program and the specification. In practice, according to the post, feedback arrives in under a second, enough for an interactive development loop, and projects with thousands of lines of code and proof verify in the time previous tools took to check a single function.

This speed detail isn't just a comfort: it's what opens up space for AI agents to help write proofs. With strong automation and a fast cycle, the agent has less low-level work to do and can iterate faster on proof attempts, something the post explicitly cites as part of the value of Verus's design.

Unsafe and concurrency: where the compiler stops protecting you

Rust allows unsafe blocks when the standard type system gets in the way of high-performance code. That code still needs to respect all of the language's safety guarantees, except the compiler stops mechanically checking this, and the developer becomes responsible. With Verus it's possible to recover mechanical checking: the developer mathematically proves the safety of the unsafe block, restoring the guarantee that unsafe normally gives up.

The same applies to concurrency. Rust already prevents several classes of errors in concurrent code through its type system ("fearless concurrency"), but Verus goes further and allows proving that concurrent code is not just safe, but correct. The mechanism described in the post is the lock invariant: whoever adds an invariant to a lock guarantees that any thread that acquires that lock receives a value that satisfies the invariant (for example, always even), and when releasing the lock it must prove the value still satisfies the property. Verus also supports proving that the lock implementation itself is correct, which matters specifically for systems with custom, high-performance locking schemes, as is the case with the Nitro Isolation Engine mentioned in the post.

Where this already runs outside Amazon

The post lists open-source projects that already use Verus for specific properties, and it's worth looking at each one because it gives an idea of where the technique fits in practice:

  • Vest: generates Rust parsing and serialization code from the description of a binary data format, including correctness and safety proofs in Verus.
  • Verdict: an X.509 certificate validation library with proven correctness, supporting user-defined validation policies.
  • CapybaraKV: verifies correctness and crash safety of logs in persistent memory, ensuring data stays in a consistent state even if the system loses power.
  • Atmosphere: a microkernel written in Rust and verified with Verus.
  • Anvil: proves correctness and "liveness" of Kubernetes controllers, showing that under reasonable assumptions the system eventually reaches a stable state.
  • CortenMM: a memory management system with a transactional interface and scalable locking, with the concurrent code verified in Verus.

Verus itself is maintained by a distributed collaboration between academic and industry researchers, and the code is publicly available for anyone who wants to experiment with it in their own projects.

What remains open

The post is honest about the limit of the guarantee: like every program verifier, trust in Verus depends on the correctness of Verus itself, on the "top" specification (does it really describe what you wanted the program to do?), on the "base" assumptions about the runtime (for example, Rust's standard library), and on the compilation chain that turns source code into an executable. None of these layers is verified by Verus itself, and Amazon's promise is to go deeper into this point in future posts.

For those building software in Brazil, the practical takeaway is this: Verus doesn't swap testing for proofs overnight, and writing a good specification requires deeply understanding what the function should guarantee, not just what it does today. But for those already working with Rust in critical code (protocol parsers, cryptography, drivers, stateful distributed systems), the barrier to entry has dropped considerably: the syntax is Rust, feedback is fast, and the same file still compiles normally with cargo for those who don't run the verifier. It's a realistic path to raise the bar of confidence in specific, high-risk parts of a codebase, without rewriting the entire project in a separate proof language.

Translated from the Brazilian Portuguese original · Read the original