AnyLearn
All lessons

What Has Actually Been Verified, and What It Cost

Two landmark systems carry machine-checked proofs of real code: a C compiler and an operating system kernel. Their published effort figures give the honest price of full verification, and their trusted computing bases show exactly what a proof still leaves unproved. This lesson uses both to decide where verification pays.

Updated · AI-authored, review-gated · how lessons are made

Not signed in: your progress and quiz score won't be saved.
Progress1 / 8

Two systems that were actually done

Verification is easy to discuss in the abstract and easy to dismiss as impractical. Two projects settle the question of feasibility, because both produced machine-checked proofs about code that runs.

CompCert is a formally verified optimising C compiler, described by Xavier Leroy in "Formal verification of a realistic compiler", Communications of the ACM, volume 52, issue 7, 2009, pages 107 to 115. Its theorem is a semantic preservation result: the generated assembly behaves as the source C program's semantics specify.

seL4 is a formally verified operating system microkernel, presented by Gerwin Klein and colleagues at SOSP in 2009. Its theorem is functional correctness: the C implementation refines an abstract specification.

Both are real. CompCert compiles real C and is used where compiler-introduced bugs are unacceptable. seL4 is a working microkernel.

So the question is not whether this is possible. It is what it costs, and both projects published enough to answer that precisely.

Full lesson text

All 8 steps on one page, for reading, reference, and search.

Show

1. Two systems that were actually done

Verification is easy to discuss in the abstract and easy to dismiss as impractical. Two projects settle the question of feasibility, because both produced machine-checked proofs about code that runs.

CompCert is a formally verified optimising C compiler, described by Xavier Leroy in "Formal verification of a realistic compiler", Communications of the ACM, volume 52, issue 7, 2009, pages 107 to 115. Its theorem is a semantic preservation result: the generated assembly behaves as the source C program's semantics specify.

seL4 is a formally verified operating system microkernel, presented by Gerwin Klein and colleagues at SOSP in 2009. Its theorem is functional correctness: the C implementation refines an abstract specification.

Both are real. CompCert compiles real C and is used where compiler-introduced bugs are unacceptable. seL4 is a working microkernel.

So the question is not whether this is possible. It is what it costs, and both projects published enough to answer that precisely.

2. The seL4 numbers

The seL4 team published their effort in detail, and the figures are the most useful data point in the field precisely because they are unflattering.

kernel implementation      8,700 lines of C  +  600 lines of assembler
proof                    ~200,000 lines of Isabelle script
                         (~165,000 written by hand)

implementation effort    ~30 person-months
proof effort             ~20 person-years
                         (~11 py attributable to seL4 itself;
                          the rest went into reusable libraries)

Two ratios follow, and both are worth sitting with.

The proof is roughly 23 lines per line of code, or 19 counting only what was written by hand.

The effort ratio is starker: 20 person-years of proof against about 2.5 person-years of implementation, so roughly eight times as much work went into proving the kernel as into building it.

The parenthetical matters for anyone extrapolating. Around 9 of those 20 person-years produced reusable infrastructure, so a second project of similar kind would not pay it again. The marginal cost is closer to 11 person-years, which is still more than four times the implementation.

3. What the proof does not cover

A verified system still rests on assumptions, and being precise about them is what separates an honest claim from marketing.

Everything a proof depends on but does not establish is the trusted computing base. For a verified kernel it typically includes:

The specification. The proof says the code matches the spec. If the spec is wrong, the proof is intact and the system is wrong.

The proof checker. Isabelle's kernel must be correct. It is small and heavily scrutinised for exactly this reason, and it is still trusted rather than proved.

The hardware. The proof assumes the processor implements its documented semantics, and that memory holds its contents.

Whatever the model omits. The original seL4 proof did not cover hand-written assembly, the boot code, or hardware-managed caches and timing behaviour.

Note what these have in common: they are all enumerable. That is the real achievement. In an unverified system the assumptions are unbounded and unknown; in a verified one they are a short, written list you can inspect and argue about.

Moving from "we believe this is correct" to "this is correct provided these six things hold" is a genuine change in kind, even though it is not the absolute guarantee the word proof suggests.

4. Why a verified compiler is worth more than it looks

CompCert's value is easy to underestimate until you notice where it sits in the stack.

Every other verified artefact is verified as source code. It then passes through a compiler, which rewrites it aggressively, and the machine executes the compiler's output rather than the thing that was proved.

So an unverified compiler is in the trusted computing base of every verified program on the system. A miscompilation silently invalidates the proof, and the proof gives no warning because it was never about the binary.

CompCert closes that specific gap. Its semantic preservation theorem states that the generated assembly behaves as the source program's semantics permit, so a property proved of the source carries down to what actually runs.

This is the general shape of how verification composes: each verified layer removes itself from the layer above's trusted base. Verified compiler plus verified kernel plus verified application is worth more than the sum, because each one shrinks what the others must assume.

It also explains why compilers and kernels were done first. They are the layers everything else trusts, so verifying them pays a dividend across every program that runs on top.

5. Deciding whether to verify

Two questions decide it, and the second is the one people skip.

Cost of failure is the obvious gate. Where a bug can be fixed by deploying again on Tuesday, an eightfold effort multiplier is indefensible. Where the artefact goes into a satellite, a medical device or a chip, or where a compromise is unrecoverable, the arithmetic changes completely.

Specification stability is the gate that kills projects. A proof is written against a specification, and changing the specification invalidates proofs. A component whose requirements change every sprint cannot carry a proof, because the proof is re-done each time. This, more than difficulty, is why verification concentrates in kernels, compilers, protocols and hardware: those specifications are stable for years.

The final box is the practical form. Nobody verifies a product. They verify the small core where the critical invariants live, and rely on ordinary engineering everywhere else. seL4 is 8,700 lines, which is a kernel rather than an operating system.

Choosing that core well is most of the skill.

flowchart TD
A["Considering formal verification"] --> B["What does a failure cost?"]
B --> C["Recoverable: ship and fix"]
C --> D["Tests and types are enough"]
B --> E["Catastrophic or unfixable in the field"]
E --> F["How stable is the specification?"]
F --> G["Churns constantly: proof cannot keep up"]
F --> H["Stable for years: verification pays"]
H --> I["Verify the small critical core, not the product"]

6. The cheap end of the same spectrum

Full verification is the expensive extreme, and the same ideas are available at every price point. Most of the value is not at the extreme.

Types. Every property pushed into a type is a proof the compiler checks for free, and the previous lesson's transferable question applies in any typed language: can this be made unrepresentable rather than checked?

Assertions and contracts. Writing preconditions, postconditions and invariants as executable checks costs almost nothing, catches violations at runtime, and, crucially, is the same artefact a verifier would consume later.

Property-based testing. Stating a property and having a tool generate inputs against it is dramatically cheaper than proof and finds a large share of what proof would find. It samples rather than covers, and shrinks failures to minimal cases.

Model checking the protocol. Days of effort on the concurrent core, per the previous lesson, catches the bugs testing structurally cannot.

Sound static analysis. Tools proving the absence of specific defect classes are proofs with the specification fixed in advance, requiring no annotation.

Read as a ladder, the practical advice is to climb it rather than jump. Each rung is cheaper than the next and catches a different class of error, and the marginal value of the top rung is only large when failure is genuinely unrecoverable.

7. What a proof is worth

ClaimTrue?
The code has no bugsNo. It matches its specification
The system cannot failNo. The trusted base can be wrong
The specification is rightNot established, and not establishable by proof
Behaviour holds for all inputsYes, within the model
The assumptions are knownYes, and this is the real gain
It is affordable at scaleNo. Roughly eight times implementation effort

The fifth row is the one to take away. A verified system's guarantee is not absolute correctness; it is that the remaining risk has been moved into an enumerated list.

That is a genuine change. An unverified system's assumptions are unbounded and mostly unknown, so "what could go wrong" has no answer. A verified one's assumptions are written down, and each can be examined, argued about, or removed by verifying another layer.

The honest summary of the whole path: verification does not eliminate uncertainty, it relocates it from the implementation into the specification and the trusted base, both of which are far smaller and far more scrutable than the code was.

Whether that relocation is worth eight times the effort is an engineering judgement, and the two questions from the decision diagram answer it in most cases without any theory at all.

8. What the path establishes

Four lessons, three traditions, one boundary.

Deductive proof turns a program into a relation between assertions, and the loop invariant is the step no machine can supply, which is Rice's theorem showing up as a daily engineering constraint. Type-level encoding removes the gap between spec and code by making them the same artefact, paying for it with a total language. Model checking abandons proof for exhaustive search, which is decidable because the state space is finite, and is limited by exactly that finiteness.

Each hits a different wall, which is why none replaced the others and why real systems use all three at different layers.

The transferable habit is the question the decision diagram asks: what does failure cost, and how stable is the specification? Those two answers place a component on the ladder from tests to types to model checking to proof, and they are answerable without knowing any of the theory.

And the general lesson, which outlasts any of these tools: correctness is always relative to a specification. Every technique here verifies agreement between two artefacts. None of them can tell you that you asked for the right thing, and that remains the hardest and least automatable part of building software.

Check your understanding

The lesson ends with a 5-question quiz. Take it in the player above to see your score.

  1. What does CompCert's theorem establish?
    • That the generated assembly behaves as the source program's semantics permit
    • That compiled C programs are free of undefined behaviour
    • That the compiler terminates on every input
    • That optimisations never change program performance
  2. The seL4 kernel is about 8,700 lines of C with roughly 200,000 lines of proof. What is the effort ratio?
    • Roughly equal effort for proof and implementation
    • About twice as much proof effort as implementation
    • About eight times as much proof effort as implementation
    • About a hundred times as much proof effort as implementation
  3. What is the trusted computing base of a verified system?
    • The subset of the code that was formally verified
    • The proof scripts that the checker validates
    • The hardware instructions the kernel executes
    • Everything the proof depends on but does not establish, such as the spec, the checker and the hardware
  4. Which factor most often makes verification infeasible, beyond raw difficulty?
    • The absence of suitable proof assistants
    • Specification churn, since changing the specification invalidates the proofs
    • The size of the state space
    • The lack of verified compilers for the target language
  5. What is the honest summary of what a proof of correctness delivers?
    • It relocates risk from the implementation into the specification and trusted base, both far smaller and more scrutable
    • It guarantees the system cannot fail in production
    • It removes the need for testing entirely
    • It establishes that the specification captures the intent

Related lessons

Computer Science
advanced

Model Checking: Exhaustive Search Instead of Proof

The third tradition supplies no invariants and writes no proofs. It builds the set of states a system can reach and checks the property against all of them, which is decidable when that set is finite. This lesson covers what it buys, the state explosion that limits it, and the techniques that made it usable anyway.

8 steps·~12 min
Computer Science
advanced

What a Proof of Correctness Actually Is

Testing samples inputs; a proof covers all of them. The machinery is a logic in which programs are statements about how they change what is true. This lesson builds Hoare triples, works a proof by hand, and identifies the one step that cannot be automated and is therefore where the work is.

8 steps·~12 min
Programming
advanced

Refs: A Branch Is a File Containing a Hash

Hashes are unusable by hand, so Git names them. A branch is not a copy of anything, not a directory, and not a container for commits: it is a forty-character file. Once that is clear, checkout, reset, detached HEAD and the reflog stop being separate concepts and become one operation on one kind of file.

8 steps·~12 min
Programming
intermediate

The Object Database: Git Is a Content-Addressed Store

Git is usually taught as a set of commands. Underneath it is a key-value store where the key is a hash of the content, and four object types built on that one idea. This lesson derives the hash by hand, shows what a commit actually contains, and explains why the design makes history tamper-evident.

8 steps·~12 min