Skip to main content
v2026.11,858 entries · CC-BY 4.0
LAC HealthMedical & Research SuppliesReagents, PPE & instruments — chain-of-custody documented.Fast, traceable sourcing built for regulated research environments, from bench consumables to instrumentation.Shop lac.us CodeCASRAIlac.us

Formal Verification: SMT & Software Proof Guide

A research guide to formal verification using Z3: proving safety invariants, extracting counterexamples, SMT-LIB setup, and free verification tools.

Written and maintained by CASRAI Editorial Board

Last updated

Formal verification is the process of using mathematical logic to prove or disprove the correctness of software, hardware, or algorithmic systems relative to a formal specification. Unlike conventional software testing—which samples discrete test inputs and cannot demonstrate the absence of bugs—formal verification evaluates all possible execution states within an input domain. In computer science research, formal verification is used to guarantee memory safety, eliminate concurrency deadlocks, verify cryptographic protocols, and validate critical algorithms prior to deployment.

What it is used for in research

Computer science researchers and systems engineers apply formal verification to establish rigorous mathematical guarantees for mission-critical software, compilers, distributed protocols, and microarchitectural designs. Key research applications include:

  • Compiler and runtime verification: Proving that optimizing compilers preserve source program semantics without introducing memory vulnerabilities or logic corruption, establishing rigorous computational reproducibility across research platforms.
  • Security protocol and vulnerability analysis: Modeling cryptographic exchanges and access control models to formally prove resistance against authorization bypasses or known attack vectors documented in CVE registries.
  • Concurrent and distributed systems: Verifying distributed consensus algorithms (such as Raft or Paxos) against state divergence, split-brain scenarios, and race conditions under network partitions.
  • High-assurance embedded systems: Demonstrating compliance with rigorous medical software engineering standards like IEC 81001-5-1 and aerospace safety requirements where unhandled runtime exceptions risk catastrophic failure.
  • Mathematical algorithm validation: Verifying numerical properties, loop invariants, and algorithmic termination in complex mathematical pipelines and numerical emulators such as surrogate models.

Install or access (official source only)

In academic research, the primary automated reasoning backend for formal verification is the Satisfiability Modulo Theories (SMT) solver. The de facto standard open-source SMT solver is the Z3 Theorem Prover, developed by Microsoft Research and distributed under the MIT License as open-source software. Researchers can access Z3 via its official command-line binary or through official language bindings.

Option 1: Official Python API (z3-solver)

The official Python binding distributes pre-compiled Z3 solver binaries across Linux, macOS, and Windows. Install the solver via Python’s package manager:

pip install z3-solver

Note: Always install z3-solver from PyPI; the package name z3 is an unrelated utility.

Option 2: Standalone Command-Line Binary (CLI)

To run formal verification on standardized SMT-LIB 2 scripts, download official compiled binaries from the official GitHub release repository (https://github.com/Z3Prover/z3/releases) or install through system package managers:

  • Ubuntu / Debian: sudo apt-get install z3
  • macOS (Homebrew): brew install z3

Verify that the Z3 executable is correctly installed and accessible in the system path by checking its version string:

z3 -version

Step-by-step task (numbered, with menu names or commands exactly as in the current docs)

This workflow demonstrates how to formally verify a program safety invariant using Z3 via the standard SMT-LIB 2 format and the Python API. The task proves that an absolute value function never produces a negative integer, and shows how the solver extracts a counterexample when a faulty specification is checked.

1. Formulate the verification condition: In Hoare logic, to prove that a program with precondition P satisfies postcondition Q (P implies Q), we test whether the negation of the property (P and not Q) is satisfiable. If the negation cannot be satisfied under any input, the original safety property is universally valid.

2. Author the verification script in SMT-LIB 2: Save the following code as verify_abs.smt2 using the standardized SMT-LIB 2.6 syntax:

(set-logic QF_LIA)
(declare-const x Int)
(declare-const result Int)

; Define program logic: result is x when x >= 0, else -x
(assert (= result (ite (>= x 0) x (- x))))

; Assert negation of the safety property (we test if result can be < 0)
(assert (< result 0))

; Check satisfiability of the violation condition
(check-sat)

3. Execute the solver via CLI: Run the verification query through the Z3 command-line interface:

z3 -smt2 verify_abs.smt2

4. Execute verification in Python: Alternatively, execute the verification condition programmatically using the official Python interface. Create verify_task.py:

from z3 import Int, If, Solver, unsat, sat

x = Int('x')
result = If(x >= 0, x, -x)

solver = Solver()
# Check whether result < 0 is possible
solver.add(result < 0)

verification_verdict = solver.check()
if verification_verdict == unsat:
    print("Verification Successful: Safety invariant holds universally.")
elif verification_verdict == sat:
    print("Verification Failed: Counterexample found:")
    print(solver.model())
else:
    print("Verification Inconclusive: Solver returned unknown.")

5. Run the Python verification script:

python verify_task.py

Output and how to read it

When an SMT solver executes a verification condition, it yields one of three formal outcomes defined by the SMT-LIB 2 standard:

  • unsat (Unsatisfiable): This is the desired outcome when checking the negation of a safety property. It proves mathematically that no assignment of input variables can ever violate the specification. The system is formally verified within the tested logic domain.
  • sat (Satisfiable): When checking a negated safety property, a sat response indicates that a property violation was found. Calling (get-model) in SMT-LIB or solver.model() in Python outputs a concrete counterexample. For example, if a bug exists in integer overflow logic, the solver prints the exact input values (e.g., x = -2147483648) that trigger the violation.
  • unknown: The solver terminated without reaching a proof or counterexample. This occurs when the problem involves undecidable theories (such as non-linear transcendental arithmetic), unbounded quantifier instantiations, or an enforced resource timeout.

Troubleshooting

Formal verification tools encounter specific computational bottlenecks. Address common issues using these documented strategies:

  • Solver times out or hangs on complex proofs: Add an explicit resource or time limit to prevent non-terminating searches. In the Python API, configure timeouts using solver.set("timeout", 10000) (value specified in milliseconds). In the CLI, pass -t:10000.
  • Quantifier instantiation loops (E-matching blowup): When using universal (forall) or existential (exists) quantifiers, first-order heuristics may generate infinite instantiation chains. Avoid unbounded quantifiers where possible by restricting formulas to quantifier-free logics (such as QF_LIA for linear integer arithmetic or QF_BV for bit-vectors). When quantifiers are necessary, annotate expressions with explicit patterns (triggers).
  • State-space explosion in model checkers: When verifying concurrent protocols, system state counts expand exponentially with process counts. Mitigate this by applying partial-order reduction, state compression, or bounded model checking with an unrolling depth limit (e.g., cbmc --unwind 10).
  • ImportError: No module named ‘z3’: Verify that z3-solver was installed into the active Python virtual environment rather than a system-wide path. Re-install with python -m pip install z3-solver.

Free alternatives (table)

Researchers utilize distinct categories of open-source formal verification tools depending on whether their target is source code, abstract protocol specifications, or mathematical logic formulas:

Tool Primary Developer / Origin Open-Source License Verification Paradigm Primary Research Use Case
Z3 Microsoft Research MIT License SMT Solver / Automated Theorem Prover Solving verification conditions, program synthesis, and constraint satisfaction.
cvc5 Stanford University & University of Iowa BSD-3-Clause SMT Solver / Automated Theorem Prover Standardized SMT-LIB 2 reasoning, syntax-guided synthesis (SyGuS), and string theory proofs.
CBMC University of Oxford & Carnegie Mellon University BSD-4-Clause Bounded Model Checker Formally verifying ANSI-C and C++ code for buffer overflows, memory leaks, and pointer safety.
SPIN Bell Labs & Gerard Holzmann BSD-3-Clause Explicit-State Model Checker Verifying concurrent software protocols specified in Promela against Linear Temporal Logic (LTL).
Dafny Microsoft Research & AWS MIT License Verification-Aware Language & Auto-Active Verifier Proving functional correctness of algorithms using Hoare-logic preconditions, postconditions, and invariants.

FAQ

What is the difference between formal verification and software testing?

Software testing checks system behavior against a finite set of test inputs or test vectors, demonstrating the presence of bugs but unable to guarantee their absence. Formal verification uses mathematical logic to evaluate system properties across all possible states, inputs, and execution paths, providing an absolute mathematical proof of correctness relative to the formal specification.

Why does a proof of correctness require checking for unsatisfiability (unsat)?

In automated reasoning and SMT solving, a property P is valid if and only if its logical negation is unsatisfiable. To prove that a program invariant cannot be broken, the solver is instructed to find any scenario where the invariant fails. If the solver determines that no such violating state exists (returning unsat), the invariant is guaranteed to hold universally.

How does bounded model checking differ from deductive verification?

Bounded model checking unrolls program loops and state transitions up to a fixed depth k, checking if a bug or violation state can be reached within k steps using propositional SAT/SMT solvers. Deductive verification relies on mathematical induction, function contracts (preconditions and postconditions), and loop invariants to prove properties for arbitrary iteration depths and infinite state spaces.

What causes an “unknown” result or timeout in an SMT solver?

An unknown outcome typically occurs when a verification query involves undecidable logical fragments, such as non-linear integer arithmetic or first-order logic with unconstrained quantifiers. It also occurs when solver resource caps (such as memory limits or execution timeouts) are reached before the search tree can be fully explored.

Can formal verification eliminate all software vulnerabilities?

Formal verification guarantees correctness only relative to the written specification and formal model. If the formal specification omits a real-world requirement, if hardware side-channels exist, or if the underlying compiler or verification tool has an unverified bug, discrepancies between theoretical proofs and physical execution may still arise.

Sources (official docs)

Follow CASRAI

Research-administration guidance, standards updates and independent tool reviews.

Ask CASRAI · free to try

Ask about Formal Verification: SMT & Software Proof Guide

Ask your first 2 questions free below. Subscribers get 150 a day for $29 a month.

An AI assistant specialized in research administration. It cites the sources behind every answer, labels web answers and says when it can't answer.

Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.

Works on this site and inside Claude, Cursor and the AI tools you already use.

Everything CASRAI publishes — this page, the dictionary, the guides and the news — stays free to read, with no account and no card.

Referenced across the research world

University of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logoUniversity of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logo
  • University of Cambridge logo
  • Columbia University logo
  • Crossref logo
  • University of Edinburgh logo
  • Harvard University logo
  • University of Oxford logo
  • Princeton University logo
  • Stanford School of Medicine logo
  • University College London logo
  • ORCID logo

View CASRAI adoption →