Skip to content

LLMs Make Formal Verification Feasible: How to Start

LLMs are making large-scale formal verification practical. Enroll an OSS project via Theorem's agent prompt, avoid the RISC-V build traps, or try a tiny Claude + Rocq proof this week.

6 min readBeginner

Fuzzing can burn trillions of runs and still miss a crash that needs a 20 MB input or a nested corner case. That is the everyday maintainer problem. In October 2026 Anthropic’s Frontier Red Team and Theorem published a different answer: Claude produced machine-checked proofs that 239 fuzz entry points across 123 OSS-Fuzz libraries never crash (memory-safety or assertion) on inputs under 64 MB. Proofs live at compiled RISC-V assembly and sum to roughly 180 million lines of Rocq.

The guarantee is real and narrow. You get certainty under stated assumptions – not “we threw entropy at it until CI went green.” Below is what that means for a normal workflow, how to enroll through OSS Verify, and where first-time users get burned.

Step-by-step: get a project into OSS Verify

Theorem opened the same pipeline free for qualifying open-source projects (OSS Verify). Smaller binaries (under roughly 1 MB of .text) get priority. If you already sit in OSS-Fuzz, most of the work is forcing static RISC-V + musl so the build contract matches what the theorems assume.

  1. Fork theorem-labs/oss-verify.
  2. From your project checkout, paste their one-line agent prompt into Claude Code, Cursor, or similar: “Enroll this project in OSS Verify by following the instructions here: https://raw.githubusercontent.com/theorem-labs/oss-verify/refs/heads/main/AGENTS.md”. The agent drafts project.yaml, Dockerfile, and build.sh, and asks what “correct” means for your use.
  3. Run tools/validate.py and tools/check –twice until two builds emit bit-identical static riscv64 binaries. Open the PR and complete the checklist.

After merge they pin binary hashes, state the theorems, generate proofs, and mail private findings (patches when the model has them). No forced disclosure clock. Wall time is often multiple days per target; proof work can continue after the first email.

Common pitfalls that bite first-time users

Bit-identical static riscv64 is not a suggestion. Dynamic linking, glibc, threads, or a non-RISC-V production target sit outside the proved model. You still learn a lot about the logic – you do not receive a certificate for the x86-64 glibc binary you actually ship. Porting those environments is separate work.

Treat every AI finding and patch as untrusted. Theorem’s docs are blunt: models generate the reports; there is no promise a human already reviewed them. Re-run your own tests, keep the original fuzz targets, and watch for reward-hacking (the model deleting behavior just to make the theorem true). Follow your project’s AI contribution rules before merge, especially on parsers and anything security-touched.

Hundreds of millions of tokens for one serious proof. That is the current cost shape as of the Oct 2026 write-up. On 36 smaller tasks Opus 5.5 used about 40% fewer tokens than Opus 5; agents that share proof pieces shaved another ~17%. Personal full-library runs without reuse infrastructure still hurt. Start with a tiny pure function or a short parser if you are paying the bill yourself.

What “proved crash-free” actually buys you

Classic formal verification meant hand-building a math model. seL4’s oft-cited baseline – about 2 person-years to implement and 20 to verify – is why most teams never tried. Here Claude writes the sprawling Rocq scripts; the Rocq kernel checks each step. Humans only need to trust the RISC-V and Linux process models, the exact theorem text, and the checker (Anthropic’s framing of the trusted base).

Theorems are lifted from existing OSS-Fuzz entry points: this binary does not crash under ASan or assertions for any input smaller than 64 MB, given the modeled OS responses. While searching for proofs Claude also patched bugs, including a counter underflow on ~20 MB inputs and a rediscovery of libexpat CVE-2026-56132 – classes that ordinary fuzzing rarely hits even after enormous budgets.

Pro tip: Think of the finished proof as a fuzzer that expanded every path once, then retired. Specs still matter. Weak theorems produce weak guarantees.

Median shape from the same post: ~600,000 lines of Rocq over an ~18,000-instruction binary. Generation is agentic; checking still costs real CPU. Formally verified is not bug-free – gaps remain in the trusted components, callers, dead code, non-ASan issues, hangs without a halt proof, and other architectures or linkers (Anthropic research post).

How this stacks up against the alternatives

Approach Guarantee strength Effort now Best for
Classic fuzzing (OSS-Fuzz) Statistical, incomplete Low ongoing Continuous regression hunting
Manual formal verification Mathematical, full for chosen props Extremely high expert time Kernels, avionics, crypto cores
LLM + Rocq agents (this work) Mathematical under model assumptions Medium (agent + human review) Libraries that already have fuzz harnesses
Source-level helpers (Dafny, Iris-style) Varies; often higher-level lemmas Lower for small new code Greenfield modules written with contracts

Fuzzing stays the daily driver. Manual proofs stay the gold standard for tiny critical cores. The LLM assembly-level route sits in the middle: machine-checked coverage up to the size cap for properties you already encode in harnesses, with cost falling as models improve and proofs get reused. Brand-new code? Writing the property first and letting the model prove (or implement) it is usually cleaner than reverse-engineering a use later.

I still fuzz by default and only escalate when blast radius is high or the bug class is hostile to random testing. That split still matches how I ship in 2026.

Try a tiny proof yourself this week

Wondering whether the tooling will feel alien before you file any PR? Good – that hesitation is useful.

Skip the full Theorem stack for a learning pass. Install Rocq (or an online playground), hand Claude a pure function and one boring statement (“for all T, f(x) never panics / stays inside range R”), and iterate on compile errors. Keep the trusted base small enough that you can read the final script. When that loop feels normal, point the OSS Verify agent at a real fuzz target. “Claude found a bug” and “Claude proved this crash class is gone under the model” are different emotional states.

FAQ

Does this make my library completely bug-free?

No. Property and model only. Callers, dead code, non-ASan bugs, and anything past the 64 MB bound can still hurt you.

I’m not an OSS maintainer – can I still use the technique?

Yes. Stand up your own Claude + Rocq/Lean loop on one internal module with a blast radius you care about – auth token parsing, a compression boundary, a small protocol decoder. Use the free OSS Verify path when the code is public and you want private reports without building the orchestration yourself. Full-scale private runs without proof reuse remain expensive relative to CI fuzzing; treat that as a budget fact, not a vibe.

How long until this is as routine as CI fuzzing?

Token cost and model quality dominate. Shared proofs and better orchestration already move the needle (see the 40% / 17% figures above). Clean use + security boundary? Starting enrollment talks this month is reasonable. Everything else: keep fuzzing hard and re-read the cost curves when the next model drops.

Next action: skim the Anthropic write-up, then paste the OSS Verify agent prompt against one small library. Even a never-merged PR teaches the new baseline faster than another summary thread.