ESBMC explores every input up to a bound, then tells you what breaks
The efficient SMT-based context-bounded model checker (ESBMC)
At a glance
- What is it?
- ESBMC is an SMT-based context-bounded model checker for C, C++, CUDA, CHERI, Kotlin, Python, Rust and Solidity programs. It front-ends code through Clang, Soot, the CPython parser and its own Solidity and PLC grammars, then hands the resulting path condition to Z3, Bitwuzla and other solvers.
- Who is it for?
- ESBMC belongs in the loop of teams that ship C or C++ with concurrency or memory safety requirements, and in the hands of verification engineers who want a counterexample rather than a fuzzing crash. It does not fit a team that only needs a linter, and it does not fit a codebase where the property of interest is timing rather than memory safety.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What bounded model checking replaces in a test suite
The README states the mechanism in one line: ESBMC detects software errors by simulating a finite prefix of a program's execution with all possible inputs. That is the whole difference from a test suite. A test picks the inputs you thought of; a bounded model checker takes the symbolic form of the same code and asks a solver what happens across the entire input space up to a chosen depth.
The project is described as the Efficient SMT-based Context-Bounded Model Checker, with the Efficient part sitting on top of Satisfiability Modulo Theories solvers. The check it performs is on runtime errors, and the class of errors it can decide includes user-specified assertion failures, out-of-bounds array access, illegal pointer dereferences such as null dereference, out-of-bounds dereference, double free of malloc'd memory and misaligned access, integer overflows, undefined behaviour on shift operations, aliasing of restrict qualified pointer parameters, floating point NaN, divide by zero and memory leaks.
The audience is verification engineers and safety critical development teams, specifically in embedded and systems code where a memory error is a field failure. The topic list is telling: cheri for capability-based hardware, cp-solver for the constraint programming side, incremental-learning and k-induction for the proof side, and solidity-contracts for smart contract verification. The same tool also covers Python, Kotlin and Rust, which is unusual for a checker with a C heritage and is covered further below.
One thing to keep in mind while reading anything about this project: the word bounded is in the name, and it constrains every claim that follows.
Five frontends decide what ESBMC can even read
Before any solving happens, the program has to be turned into a symbolic form, and that is entirely the frontends' job. The README lists them explicitly.
Clang serves as the frontend for C, C++, CHERI and CUDA. The Soot framework via Jimple handles Java and Kotlin. The CPython 3.10 parser is the Python frontend, and the README calls ESBMC the first SMT-based bounded model checker for Python programs, which is a claim by the project rather than an independent finding. Solidity is handled by implementing the language's grammar production rules directly, and the ESBMC-PLC frontend verifies IEC 61131-3 Ladder Diagram programs for programmable logic controllers.
The practical consequence is that the language coverage is uneven by design. Clang and the Soot path are mature third-party toolchains, so a new language feature in C++ or a new Kotlin release can change what reaches the solver, while the Solidity and PLC frontends are in-house grammars whose coverage is tied to the project itself. Floating point arithmetic is supported for various SMT solvers, with IEEE behaviour in the mix.
Each frontend also defines the error vocabulary you get back. A check that Clang's frontend can express about pointers is not automatically expressible by the Ladder Diagram frontend, so when you compare coverage between languages, you are really comparing frontends as much as solvers.
The error list and how threads are explored
The feature list is specific enough to be useful for scoping a first run, and it is worth reading as a checklist of what a single command will attempt to decide for you.
For sequential code, the checks are the runtime error classes listed earlier, plus anything you wrote as an assertion. Assertions are the lever most teams pull first, because they let you state a property in the source and have the checker decide it rather than describing it in a separate property language.
Concurrency is handled differently, and this is the part that separates ESBMC from a plain sequential checker. Programs using the pthread API are verified by explicitly exploring interleavings, producing one symbolic execution per interleaving. By default the checker looks at pointer safety, array out-of-bounds, division by zero and user-specified assertions, and there are options to add deadlock detection (only on pthread mutexes and conditional variables), data races in the sense of competing writes, atomicity violations at visible assignments, and lock acquisition ordering.
There is a search strategy choice here. By default ESBMC performs a lazy depth-first search of interleavings, which finds a failing interleaving early and is usually the right default for bug hunting. It can also encode all interleavings explicitly into a single SMT formula, which is more complete in a different sense and typically much harder for the solver. Neither mode is universally better, and the README does not give guidance on when to switch, so treat it as an experiment on your own code.
Solvers, the SMTLIB escape hatch, and two one-shot backends
The solver list is long: Z3 4.13+, Bitwuzla, Boolector 3.0+, MathSAT, CVC4, CVC5 and Yices 2.2+. All of them are driven through the SMTLIB interface, and the README also notes that ESBMC can talk the SMTLIB interactive text format over a pipe to an arbitrary solver process, with the caveat that this is not free of overhead.
Two newer backends take a different shape, and their restrictions are the part you need to read before choosing one. Both drive external one-shot solver processes over SMT-LIB2 files, and they are built by default with the CMake options ENABLE_BITWUZLLOB and ENABLE_NEUROSYM, with the external program only needed at runtime.
The bitwuzllob backend runs Bitwuzla on the massively parallel Mallob platform, with the solver program defaulting to a mallob invocation using a mono file and the SMT application. The neurosym backend runs NeuroSym, a neural-guided SMT solver where a GAN proposes candidate models and a Z3 fallback preserves soundness and completeness, for the QF_BV fragment, with the program defaulting to a Python entry point run from a NeuroSym checkout that has Python, PyTorch and Z3 installed. ESBMC flattens arrays, structs and floating point to pure bit-vectors for that path, and integer or real mode is not supported there.
The shared limitation is important: both are one-shot backends, so the incremental strategies are rejected, including --k-induction and --incremental-bmc. Counterexample extraction also needs a local interactive SMT-LIB2 model solver such as z3 -in, or you take the result only with --result-only. If your workflow depends on incremental solving or on reading the counterexample model in the same run, the one-shot backends are the wrong choice.
Installing on Ubuntu, macOS and Windows
There are three documented routes and they differ in what you get. On Ubuntu the recommended path is the project's own PPA, which provides releases for automatic installation and is supported on Ubuntu 22.04 and 24.04.
sudo add-apt-repository ppa:esbmc/esbmc
sudo apt update
sudo apt install esbmcOn macOS and Linux, Homebrew is the shortest path, and the README says it installs the esbmc binary together with its bundled SMT solvers, Z3 and Bitwuzla. Having the solvers bundled removes a whole class of setup problems, since a checker that cannot find a solver is not much use.
brew install esbmcFor Windows, and for Ubuntu users who prefer a standalone file, the releases page carries the latest binaries. The repository also links a separate building document for anyone who wants to compile from source, and the root of the repository shows what that build involves: CMakeLists.txt and CMakePresets.json at the top level, with the C++ sources under src, public headers under include, unit tests under unit, a regression directory, and helper directories for tools, scripts and documentation.
Whichever route you take, the first real run is the C example the README uses to demonstrate the feature set. The interesting part of the program is the pointer arithmetic on the malloc'd array, and the interesting part of the checker is that it can find the defect without being told where to look.
#include <stdlib.h>
int *a, *b;
int n;
#define BLOCK_SIZE 128
void foo () {
int i;
for (i = 0; i < n; i++)
a[i] = -1;
for (i = 0; i < BLOCK_SIZE - 1; i++)
b[i] = -1;
}The rest of the example allocates both buffers in main, writes through the incremented pointer, calls foo and then frees the two pointers in a branch. The post-increment on b is what makes the later free of b and the read of b[-1] interesting to a checker that reasons about aliasing and bounds symbolically rather than on one concrete input.
A workflow with a GUI, an editor extension and a CI action
The project ships companion front-ends so the checker does not have to live only in a terminal, and this is the part of the setup that decides whether a team keeps using it.
The Visual Studio Code extension verifies the file you are editing and reports results in the integrated terminal. ESBMC-Web is a self-hosted browser GUI for C, C++ and Python, with a flag picker and an interactive dashboard for violations and counterexamples; self-hosted matters if your code cannot leave the network. A Claude Code plugin provides /verify and /audit commands, a verification skill, reference documentation and examples, which puts the checker inside an agent loop rather than a human loop.
For continuous integration the README points at a GitHub Action page, so the same checks can run on pull requests. The upstream project also documents its own development tooling in the repository root: an AGENTS.md file, a CONTRIBUTIONS.md, an ARCHITECTURE.md for the main components, a CITATION.cff for academic citation, and a .clang-format file for formatting.
The Python frontend has its own small dependency file, pyproject.toml, which is worth reading because it shows that the Python path is a separate component with its own tooling. It requires Python 3.10 or newer and pulls in ast2json and mypy, with hypothesis and pytest in the development group.
[project]
name = "esbmc"
version = "8.0.0"
description = "ESBMC Python frontend development dependencies"
requires-python = ">=3.10"Note the version field there says 8.0.0 while the published releases have moved to the 8.x series at a different pace, which tells you the two are versioned separately.
The bound, the one-shot restrictions and the licence
Three limitations deserve to be stated plainly, because they decide when this tool is the wrong choice.
First, the bound. ESBMC explores a finite prefix of execution. A program that is correct within the bound and broken beyond it will be reported as correct, and the bound interacts badly with unbounded loops and deep call stacks, where the useful answer may sit far outside the reachable prefix. The alternative within the same tool is the k-induction proof rule, which can discharge properties without a bound at all, and the incremental BMC strategies, which trade memory for a longer search. The default reading of the name should make you suspicious of any claim that a program is verified.
Second, the one-shot backends described earlier, which reject k-induction and incremental BMC and need a separate model solver to print a counterexample. They are for scale, not for interactive exploration.
Third, the licence. The README describes the project as permissively licensed, but the licence field on the repository is recorded as Other, a custom licence that GitHub cannot classify, and the repository root carries a COPYING file rather than a file with a standard open source licence name. Read COPYING yourself before you ship ESBMC in a product or redistribute a build of it. This is not a licensing opinion, only a note that the classification is not the usual one and the text is the authority.
On upkeep, the project is active in the ordinary sense: the last push was on 2026-10-20, and the release line moved to v8.6 on 2026-10-15 after v8.5 on 2026-08-30 and v8.4 on 2026-07-06. That cadence means a pinned version ages within weeks, which matters if you wire the checker into CI, since a solver upgrade can change both runtime and results.
Editorial conclusion
ESBMC belongs in the loop of teams that ship C or C++ with concurrency or memory safety requirements, and in the hands of verification engineers who want a counterexample rather than a fuzzing crash. It does not fit a team that only needs a linter, and it does not fit a codebase where the property of interest is timing rather than memory safety. Before adopting it, pick one bounded property and one small function and run the C example in the README through it, then check the COPYING file, since the licence is a custom one that GitHub records as Other. The last push was on 2026-10-20 and the newest release is v8.6 from 2026-10-15.
Frequently asked questions
How do I install ESBMC on Ubuntu?
The README points to the project's own PPA, which supports Ubuntu 22.04 and 24.04, and gives the add-apt-repository, apt update and apt install esbmc commands. On macOS and Linux, brew install esbmc installs the binary together with the bundled Z3 and Bitwuzla solvers, and binaries for Ubuntu and Windows are on the releases page.
Which programming languages can ESBMC verify?
The README lists single- and multi-threaded C, C++, CUDA, CHERI, Kotlin, Python, Rust and Solidity. The frontends are Clang for C, C++, CHERI and CUDA, Soot via Jimple for Java and Kotlin, the CPython 3.10 parser for Python, an in-house Solidity grammar, and the ESBMC-PLC frontend for IEC 61131-3 Ladder Diagram.
Which SMT solvers does ESBMC support?
The README lists Z3 4.13+, Bitwuzla, Boolector 3.0+, MathSAT, CVC4, CVC5 and Yices 2.2+, and ESBMC can also pipe the SMTLIB interactive text format to an arbitrary solver process, with some overhead. Two one-shot backends, --bitwuzllob and --neurosym, drive external solvers over SMT-LIB2 files.
What errors can ESBMC detect in a concurrent program?
For pthread code it explores interleavings explicitly, one symbolic execution per interleaving. By default it checks pointer safety, array out-of-bounds, division by zero and user-specified assertions, and options add deadlock detection on mutexes and condition variables, data races, atomicity violations and lock acquisition ordering.
What licence does ESBMC use?
The README calls the project permissively licensed, but the licence field on the repository is recorded as Other, a custom licence GitHub cannot classify, and the repository root holds a COPYING file. Read that file for the terms, especially if you plan to redistribute a build.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/esbmc-esbmc)Was this useful?