Contents Menu Expand Light mode Dark mode Auto light/dark, in light mode Auto light/dark, in dark mode Skip to content
CppVerify
Light Logo Dark Logo
CppVerify

Book

  • The CppVerify Book
    • Part I — Foundations
      • Chapter 1 — Correctness and trust
      • Chapter 2 — The limits of testing
      • Chapter 3 — Logic and specifications
      • Chapter 4 — Hoare logic
      • Chapter 5 — Invariants and loops
      • Chapter 6 — Ghost reasoning and lemmas
      • Chapter 7 — Memory, aliasing, and frame conditions
      • Chapter 8 — SMT solvers and automation
    • Part II — CppVerify
      • Chapter 9 — Why CppVerify on Clang
      • Chapter 10 — Getting started
      • Chapter 11 — Your first verified function
      • Chapter 12 — Loops in practice
      • Chapter 13 — Spec and proof functions
      • Chapter 14 — Pointers, frames, and modifies
      • Chapter 15 — Toolchain and flags
      • Chapter 16 — When verification fails
      • Chapter 17 — Backends, modular calls, and debugging
      • Chapter 18 — Undefined behavior
      • Chapter 19 — Dynamic storage and lifetime
      • Chapter 20 — From mathematics to verified code

Reference

  • Language reference
    • Contract syntax
    • Contract expressions
    • Functions and loops
    • Ghost, spec, and proofs
    • Structs and type invariants
    • Pointers
    • Dynamic storage
    • Integers
    • Commands and flags
    • Current limitations and C++ readiness
  • C++ API reference

Case studies

  • Case studies
    • LLVM ULEB128
    • UTF-8 validation
    • Binary search

Releases

  • Release notes
Back to top
View this page
Edit this page

Binary search¶

The overflow-safe midpoint, and the one that is not.

Binary search is the standard example of an algorithm that is easy to state and easy to get wrong. Jon Bentley’s Programming Pearls published the broken form and it stood for two decades; java.util.Arrays.binarySearch shipped it for nine years before Joshua Bloch wrote Nearly All Binary Searches and Mergesorts Are Broken in 2006.

The defect is one line:

int mid = (lo + hi) / 2;

For a large array lo + hi exceeds INT_MAX. In C++ signed overflow is undefined behavior, so the program has no defined meaning at all – the division never gets the chance to bring the value back into range.

What CppVerify does with it¶

Nothing in the contract asks for an overflow check. Core expression definedness is always on, so the encoder generates the obligation itself and it fails:

error: verification failed: bsearch_broken [...::overflow@...]
  (counterexample: lo = 1073741824, hi = 1073741824)

Those two values sum to 2,147,483,648, which is INT_MAX + 1. The verifier did not merely report that something might overflow; it named the check that fails and produced the concrete state that breaks it. The solver chooses the witness, so another version may report a different pair with the same overflow.

Replacing the midpoint with the standard safe form makes the same function verify:

int mid = lo + (hi - lo) / 2;

What is proved¶

For every n and every input satisfying the precondition:

  • termination, via the cppverify::decreases clause on the search range;

  • memory safety – every a[mid] read lies inside the declared extent;

  • definedness – no signed overflow anywhere, the midpoint included;

  • the result is -1 or a valid index into the buffer.

This is what binary_search_pass.cpp proves. Its contract does not say that a non-negative result points at the key, or that -1 means the key is absent.

The full functional specification¶

The full functional specification verifies too, in about a second. Section “Universal statements: sorted arrays” of Chapter 20 — From mathematics to verified code proves it: sortedness is the precondition cppverify::forall(i, 0, n, cppverify::forall(j, i, n, a[i] <= a[j])), the postcondition is cppverify::result >= 0 ? a[cppverify::result] == x : cppverify::forall(k, 0, n, a[k] != x), and the loop returns mid when it finds the key.

Reproduce¶

./build/bin/cpp-verify clang/test/Verify/suite/binary_search_pass.cpp
./build/bin/cpp-verify clang/test/Verify/suite/binary_search_overflow_fail.cpp

Both are lit tests in clang/test/Verify/suite/; the second is expected to fail closed.

Next
Release notes
Previous
UTF-8 validation
Copyright © 2026, CppVerify contributors
Made with Sphinx and @pradyunsg's Furo
On this page
  • Binary search
    • What CppVerify does with it
    • What is proved
    • The full functional specification
    • Reproduce