Formal Software Verification

1.1. Why Verify Software Formally?🔗

Software controls aircraft, medical devices, financial systems, and communication networks. Errors in such systems cost money and lives. The standard way to find errors is testing, and testing examines finitely many executions of a program that admits infinitely many. Dijkstra stated the limitation precisely.E. W. Dijkstra, Notes on Structured Programming, EWD249, Technological University Eindhoven, 1970.

Program testing can be used to show the presence of bugs, but never to show their absence!

Formal verification takes the complementary route. We state a property of a program as a mathematical proposition and prove that every execution satisfies it. The proof covers all inputs at once, which no finite test suite achieves.

Proofs about real programs grow large, so we delegate their checking to a machine. A proof assistant is a program that checks every step of a proof with respect to the rules of a formal logic, and that helps the user construct the proof interactively. Lean, Rocq (formerly Coq), Isabelle/HOL, and Agda are proof assistants in current use. Landmark results include the verification of the seL4 operating-system microkernelG. Klein et al., seL4: Formal Verification of an OS Kernel, Proceedings of SOSP 2009, pp. 207–220. and of the CompCert optimizing C compiler.X. Leroy, Formal Verification of a Realistic Compiler, Communications of the ACM 52(7), 2009, pp. 107–115.

Language models now write a growing share of code. A model produces plausible text, and plausible is not the same as correct. Generated code can invoke functions that do not exist, handle only the cases that its prompt suggests, or drift from the stated requirement in ways that survive code review. The literature calls this failure mode hallucination.

Formal verification, in particular when automated, changes how we can trust such code.L. de Moura, The Lean Programming Language and Theorem Prover, ETAPS 2026. When generated code arrives with a machine-checked proof that it satisfies its specification, the proof assistant checks the proof independently of how the code came to be, so hallucinated or simply wrong code cannot pass. The burden of correctness moves from reading the code to writing the right specification. The techniques of this course apply unchanged to generated code, and the automation of the final lectures, with the mvcgen tactic, points toward verification at the pace of code generation.

In this course we use Lean. Lean is at once a programming language and a proof assistant, so we can write a program and prove its properties in the same system. Lectures 1 and 2 review classical logic while introducing Lean's proof language, following HTPIwL. Lectures 3 to 8 follow LoVe through interactive proving, functional programming, and inductive predicates. The final block treats the semantics of an imperative language, Hoare logic, and practical verification with the mvcgen tactic.