Program Verification as Satisfiability Modulo Theories

A key driver of SMT over the past decade has been an interchange format, SMT-LIB, and a growing set of benchmarks sharing this common format. SMT-LIB captures very well an interface that is suitable for many tasks that reduce to solving first-order formulas modulo theories. Here we propose to extend these benefits into the domain of symbolic software model checking. We make a case that SMT-LIB can be used, and to a limited extent adapted, for exchanging symbolic software model checking benchmarks. We believe this layer facilitates dividing innovations in modeling, developing program logics and frontends, from developing algorithms for solving constraints over recursive predicates. 1 SMT and checking properties of recursive predicates Progress in modern SMT solvers has been driven by a large set of applications in diverse areas. Applications reduce problems to common first-order formats, SMT-LIB [5] and now also the THF [13] format in TPTP, for exchanging benchmarks. They benefit from the progress in automated deduction in SMT solvers and utilize the smorgasbord of theories that are supported with efficient and dedicated solvers. Common to SMT solvers is their support for simply typed first-order classical logic augmented with theories. They solve satisfiability of formulas over the supported language. When SMT solvers are used to discharge verification conditions from program verifiers, it is important to be able to check validity, or dually unsatisfiability. When SMT solvers are used in dynamic symbolic simulation, the more interesting question is to determine satisfiability and find a satisfying assignment to formulas. A key driver of progress in these applications has been the ability to exchange problems using standardized and welldefined logics, theories and syntax, especially as embodied in the SMT-LIB standard. Here we propose to extend these benefits into the domain of symbolic software model checking. This problem reduces to the problem of inference of the intermediate specifications required by various proof systems. These specifications can be, for example, loop invariants, procedure summaries, environment assumptions or dependent types. As observed in [6], the inference problem in turn reduces to the problem of satisfiability of a certain class of first-order constraints containing unknown relations. These constraints can be produced by existing verification condition generators, which already typically target the SMT-LIB standard. We will argue that, with very slight extensions, SMT-LIB can be adapted to be a standard interchange medium for software model checking and related problems. The advantage of such an approach is a clean separation of concerns: the interpretation of programming language is left to verification condition generators, while model checking is handled by purely logic-based tools. This separation could have several benefits. It relieves the algorithm implementer from the need to deal with the complexity of programming languages, it allows implementations to be easily re-targeted to different languages, and it allows algorithms to be compared directly, without concern for the ambiguity of how programming languages are modeled. At the very least it can be instrumental for comparing algorithms for checking recursive predicates whether they come from software model checking or not. Pascal Fontaine, Amit Goel (eds.); SMT 2012, pp. 3–11 3 Program Verification as SMT N. Bjorner, K. McMillan, A. Rybalchenko In this paper, we propose extensions to SMT-LIB to allow it to be used as an interchange format for software model checking benchmarks. We are not alone in suggesting ways of leveraging SMT-LIB for verifying reactive systems and software. The VMT 1 effort, see also the proceedings for this workshop (SMT 2012), proposes extending SMT-LIB with notation for transitions. Our suggestion is to utilize Horn clauses as a low-level, but uniform, exchange format. Transition systems and, more generally, push-down systems can be translated into Horn clauses. Of course, Horn clauses can be translated into transition systems by using a theory that can express stacks. The Numerical Transition Systems Library also aims to identify an interchange format for transition systems. We aim to provide a basis for comparing various tools that work on various incarnations of recursive predicates, such as [14, 2, 3, 8, 1]. 2 Program verification Tool work-flow A proof system gives us rules for decomposing the proof of a complex goal into proofs of simpler sub-goals. These rules are typically non-deterministic, requiring us to instantiate some auxiliary construct. Consider, for example, the proof rule for sequential compositions in Hoare logic: {P} s1 {Q} s2 {R} {P} s1; s2 {R} Here, the intermediate assertion Q is the auxiliary construct that must be instantiated in order to decompose the proof. The decomposition yields two sub-goals above the line, one involving s1 and the other involving s2. The difficulty in this step is to infer the predicate Q. In software model checking we are concerned with the problem of automatically inferring these auxiliary proof constructs. Here we would like to argue that this inference problem can be handled in a generic way. That is, we are interested in methods that are to some extent independent of the proof system, and require only that the proof sub-goals can be expressed in a suitable logical form. In program proving, the translation of proof sub-goals into logic is commonly referred to as verification condition (VC) generation. For example, suppose in the above example that our program has one variable x. The VC’s can be written in this form: ∀x, x′. (P (x) ∧ [s1](x, x′) ⇒ Q(x′)) ∀x, x′. (Q(x) ∧ [s2](x, x′) ⇒ R(x′)) where [si] is some relation representing the operational semantics of program statement si. If these two formulas are shown to be valid, then our proof sub-goals are discharged, and we know that {P} s1; s2 {R} holds. Notice though, that these VC’s contain one unknown relation Q that must be inferred. In a typical VC checking application, the user would provide the relation Q in logical form, and the task of the verification tool is to check that the two individual proof sub-goals are valid. On the other hand, the inference problem is to simultaneously solve the VC’s for a value of Q that makes both true. Both of these problems are satisfiability problems. In the former case, we separately check satisfiability of the negation of each condition, with a fixed value for Q, while in the latter, we check satisfiability of both constraints together, with an uninterpreted symbol standing in for Q. We will observe that the VC’s for a wide variety of proof systems have a particular form. That is, they are of the form ∀X. (B ⇒ H), where X is some set of first-order variables, H is 1https://sites.google. com/site/torino2011ic0901/programme/talks