Overview¶
STP is a constraint solver for the quantifier-free theories of bitvectors, arrays, and floating-point. It can solve many kinds of problems generated by program analysis tools, theorem provers, automated bug finders, cryptographic algorithms, intelligent fuzzers and model checkers.
Easy to embed or run standalone
Bindings for C and Python
Supports multiple query input formats
Open source and MIT licensed
Install instructions¶
On a Debian-like platform, from a clean checkout:
sudo apt-get install git build-essential cmake bison flex curl patch \
python3 libgmp-dev
git clone https://github.com/stp/stp
cd stp
git submodule init && git submodule update
./scripts/deps/setup-cms.sh
./scripts/deps/setup-libbf.sh
mkdir build && cd build
cmake ..
cmake --build . -j$(nproc)
sudo cmake --install .
Every step is needed. ABC, mimalloc, CLI11 and SymFPU are submodules and
are built as part of STP; LibBF is required; and a build with no SAT backend enabled
does not configure. The two setup scripts fetch and build LibBF and
the SAT solver into deps/, where the build finds them without being
told where to look. libgmp-dev is in the list for CryptoMiniSat’s sake
– it is the only thing that needs it, and a build without CryptoMiniSat
does not.
That gives a solver backed by CryptoMiniSat, which is the default when it
is the only backend compiled in. CaDiCaL is also supported and becomes the
default when it is compiled in; either way --cryptominisat, --cadical
and --minisat pick a compiled-in backend at run time. Building STP
has the recipe.
With Homebrew:
brew install stp
Or with Docker, which needs nothing installed but Docker itself, and reads the problem on standard input:
git clone https://github.com/stp/stp
cd stp
docker build -t stp .
echo "(set-logic QF_BV)
(assert (= (bvsdiv (_ bv3 2) (_ bv2 2)) (_ bv0 2)))
(check-sat)
(exit)" | docker run --rm -i stp
Building STP covers the rest: the configuration variables, the SAT backends and how to choose between them, building against dependencies you have built but not installed, static builds, and Windows.
SMT-LIB2 input language¶
The SMT-LIB2 format is the recommended file format for use with STP, in part because it is parsed by all modern bitvector solvers. STP implements a subset of the SMT-LIB2 language; not all SMT-LIB2 features are implemented. What follows is a short description of the language STP parses. For more information related to SMT-LIB, please refer to this page.
Header¶
The SMT-LIB2 format uses a header to tell the solver which type of
problem is coming. Only set-logic is needed; set-info is accepted
and ignored, and benchmark files usually carry it:
(set-logic QF_ABV)
(set-info :smt-lib-version 2.0)
The logic names the theories the problem is written in. STP accepts these, and rejects any other name:
Logic |
Theories |
|---|---|
|
bitvectors |
|
bitvectors and arrays |
|
bitvectors and arrays; the uninterpreted functions the name also allows are not supported, function declarations being nullary |
|
floating-point |
|
floating-point and bitvectors |
|
floating-point, bitvectors and arrays |
|
as the three above, plus the real constants that appear as the
argument of |
Declarations¶
Bitvector expressions (or terms) are constructed out of bitvector constants, bitvector variables and the functions listed below. In STP all variables have to be declared before the point of use. An example declaration of a bitvector variable of length, say 32, is as follows:
(declare-fun x () (_ BitVec 32))
An example of an array declaration with 32 bit indices, and 7 bit results is:
(declare-fun a () (Array (_ BitVec 32) (_ BitVec 7)))
Functions and terms¶
Bitvector variables (or terms) of length 0 are not allowed. Bitvector constants can be represented in binary or hexadecimal format. The rightmost bit is called the least significant bit (LSB), and the leftmost bit is the most significant bit (MSB). The index of the LSB is 0, and the index of the MSB is n-1 for an n-bit constant. This convention naturally extends to all bitvector expressions.
Following are some examples of bitvector constants in binary and hexadecimal:
#b0000111101010000
#x0f50
The bitvector implementation in STP supports a very large number of functions and predicates. The functions are categorised into word-level functions, bitwise functions and arithmetic functions; the predicates are listed after them.
Word-level functions¶
Name |
Symbol |
Example |
|---|---|---|
Concatenation |
|
|
Extraction |
|
|
Shift Left |
|
|
Logical Shift Right |
|
|
Arithmetic Shift Right |
|
|
Zero Extension |
|
|
Sign Extension |
|
|
Repetition |
|
|
Array READ |
|
|
Array WRITE |
|
|
Notes:
For extraction terms, say
((_ extract i j) t), n > i >= j >= 0, where n is the length of t.For left shift terms,
t << kis equal to k 0’s appended to t. The length oft << kis n.For right shift terms, say
t >> k, the term is equal to the bitvector obtained by k 0’s followed byt[n-1:k]. The length oft >> kis n.
Bitwise functions¶
Name |
Symbol |
Example |
|---|---|---|
Bitwise AND |
|
|
Bitwise OR |
|
|
Bitwise NOT |
|
|
Bitwise XOR |
|
|
The arguments of bitwise functions have the same length.
Arithmetic functions¶
Name |
Symbol |
Example |
|---|---|---|
Addition |
|
|
Subtraction |
|
|
Negation |
|
|
Multiplication |
|
|
Unsigned division |
|
|
Signed division |
|
|
Unsigned remainder |
|
|
Signed remainder |
|
|
Signed modulus |
|
|
The arguments of arithmetic functions have the same length, and the result
has that length too. Division and remainder are total, as SMT-LIB2 requires:
bvudiv by zero is the all-ones bitvector, bvsdiv by zero is all-ones
for a non-negative dividend and one for a negative one, and bvurem,
bvsrem and bvsmod by zero return their first argument.
Predicates¶
Predicates compare two bitvectors of the same length and produce a formula rather than a term.
Name |
Symbol |
Example |
|---|---|---|
Equality |
|
|
Unsigned less than |
|
|
Unsigned less or equal |
|
|
Unsigned greater than |
|
|
Unsigned greater or equal |
|
|
Signed less than |
|
|
Signed less or equal |
|
|
Signed greater than |
|
|
Signed greater or equal |
|
|
STP also accepts the overflow predicates bvsaddo, bvuaddo,
bvssubo, bvusubo, bvsmulo, bvumulo, bvsdivo and
bvnego, and the one-bit comparison bvcomp.
Examples¶
There are many SMT-LIB2 format examples in STP’s source code repository:
look for files with a .smt2 extension under tests/query-files. Signed division of -1/-2 =
0, should be satisfiable:
(set-logic QF_BV)
(set-info :smt-lib-version 2.0)
(set-info :status sat)
(assert (= (bvsdiv (_ bv3 2) (_ bv2 2)) (_ bv0 2)))
(check-sat)
(exit)
Python usage¶
import stp
s = stp.Solver()
a = s.bitvec('a', 32)
b = s.bitvec('b', 32)
c = s.bitvec('c', 32)
s.add(a == 5)
s.add(b == 6)
s.add(a + b == c)
s.check() # True
s.model() # {'a': 5, 'b': 6, 'c': 11}
C library usage¶
When STP is built it generates the libstp library – shared by
default, or static if you configured with STATICCOMPILE=ON – which
can be used with one of two header files, depending on the preferred
language:
include/stp/c_interface.hfor a C interface to STPinclude/stp/fp.hppfor the floating-point helpers
An example C header usage can be as simple as:
#include "stp/c_interface.h"
#include <assert.h>
int main(int argc, char **argv) {
VC vc = vc_createValidityChecker();
// ask for a counterexample to be built, so it can be printed below
vc_setFlags(vc, 'c');
// 32-bit variable 'c'
Expr c = vc_varExpr(vc, "c", vc_bvType(vc, 32));
// 32 bit constant value 5
Expr a = vc_bvConstExprFromInt(vc, 32, 5);
// 32 bit constant value 6
Expr b = vc_bvConstExprFromInt(vc, 32, 6);
// a+b!=c
Expr xp1 = vc_bvPlusExpr(vc, 32, a, b);
Expr eq = vc_eqExpr(vc, xp1, c);
Expr eq2 = vc_notExpr(vc, eq);
//Is a+b!=c always correct?
int ret = vc_query(vc, eq2);
//No, c=a+b is a counterexample. vc_query returns 0 for INVALID.
assert(ret == 0);
//print c = 11 counterexample
vc_printCounterExample(vc);
//Delete validity checker
vc_Destroy(vc);
return 0;
}
If you use CMake as the build system for your project it is easy to use
STP as an external project. An example can be found in the sources under examples/simple.
Awards¶
STP’s speed and accuracy compare favorably with other solvers in the bitvector (QF_BV) category.
1st in QF_BV in the Single Query track, on all five scoring schemes, at SMT-COMP 2023
1st in QF_BV for Single Query UNSAT and Single Query 24s Performance at SMT-COMP 2022
Winner of the QF_BV incremental competition at SMT-COMP 2021, and top-ranked in the cloud track, which was experimental and selected no winner
2nd in QF_BV at SMT-COMP 2014
2nd in QF_BV at SMT-COMP 2011, entered as STP2
1st in QF_BV at SMT-COMP 2010, entered as simplifyingSTP
1st at SMT-COMP 2006
Competition results are a snapshot of one year’s entrants on one year’s selection. Benchmarks is the continuous version: every SMT-LIB benchmark STP can read, re-measured against a git commit so a change in the solver shows up as a change in the numbers.
Use cases¶
KLEE, a symbolic execution engine, is using STP at its core (Professor Cristian Cadar’s group at Imperial College, London, and Professor Dawson Engler’s group at Stanford University)
Souper at the University of Utah and Google (historical: the project is archived and moved off STP)
S2E, begun at EPFL
Binary Analysis Platform (BAP) is using STP for analysis, by the CMU
EXE is a symbolic-execution based bug-finding tool that reads your C program and tries to automatically crash it (Stanford University)
MINESWEEPER is a tool that automatically analyzes certain malicious behavior in unix utilities and malware. (Carnegie Mellon University)
CATCHCONV is a bug finding tool that tries to find bugs due to type mismatch in C programs. (University of California, Berkeley)
Backward path-sensitive analysis of C programs to find bugs by Tim Leek from MIT Lincoln Labs
Bug finding in Verilog code (a major microprocessor company)
JPF-SE is a symbolic execution extension to the Java PathFinder model checker . (NASA Ames Research Center)
Avalanche bug-finding tool (Institute of Systems Programming, Moscow, Russia)
Low-level Bounded Model Checker - LLBMC (frozen at 2013.1) (Karlsruhe Institute of Technology (KIT), Germany)
FuzzGrind (ESEC Lab)
In conjunction with ACL2 to formally verify implementation of encryption algorithms in Java (Stanford University)
Hampi : A solver for string constraints used to automatically construct SQL injection and XSS exploits (MIT)
Automatic configuration: Tvl2STP (University of Namur in Belgium)
Architecture¶
STP does word-level preprocessing and then translates what is left to SAT. Architecture walks the whole pipeline, stage by stage, with a diagram of the passes and of the three places the solver repeats itself until it reaches a fixed point.
Publications¶
STP is based on, and described by, the following.
A Decision Procedure for Bit-Vectors and Arrays (bibtex) by Vijay Ganesh and David L. Dill. In Proceedings of the International Conference in Computer Aided Verification (CAV 2007), Berlin, Germany, July 2007.
EXE: Automatically Generating Inputs of Death (bibtex) by Cristian Cadar, Vijay Ganesh, Peter Pawlowski, David L. Dill and Dawson R. Engler. In Proceedings of ACM Conference on Computer and Communications Security 2006 (CCS 2006), Alexandria, Virginia, October, 2006. There is also an extended journal version (bibtex) in ACM Transactions on Information and System Security 12(2), 2008.
State Joining and Splitting for the Symbolic Execution of Binaries (bibtex) by Trevor Hansen, Peter Schachte and Harald Søndergaard. In Proceedings of the 9th International Workshop on Runtime Verification (RV 2009), Grenoble, France, June 2009. LNCS 5779, pages 76-92.
A Constraint Solver and its Application to Machine Code Test Generation (bibtex) by Trevor Hansen. PhD thesis, University of Melbourne, October 2012.