• No results found

SMT-based Encoding for Bounded Program Verification

3.8 Related Work

SAT-based Encoding

Many bounded program verification approaches (e.g., Jalloy [Vaziri-Farahani, 2004], JForge [Dennis et al., 2006], TACO[Galeotti et al., 2013], Miniatur[Dolby et al., 2007], Karun[Taghdiri, 2008], and MemSAT[Torlak et al., 2010]) have been developed to check programs with complex data structures. Jalloy [Vaziri-Farahani, 2004] analyzes Java programs by translating the code into an Al-loy [Jackson, 2012] formula and checking it against a property, also expressed in Alloy, using an SAT solver. Alloy is a first-order relational logic with transitive closure that makes it well-suited for specifying complex data struc-ture properties, e.g., the properties of linked lists and trees. Similar to our technique, its translation to SAT requires bounding the number of loop itera-tions and the program’s heap size. Consequently, Jalloy analysis provides a bounded verification: it exhaustively checks a program within the analyzed bounds, but cannot guarantee anything beyond that. Other techniques (e.g., JForge and TACO) have been developed to improve the translation of Java programs into Alloy formulas, thus resulting in more compact SAT formulas

3.8 Related Work 47 that can be handled more efficiently by existing SAT solvers. The technique presented in [Dennis et al., 2006] applied Alloy-based program verification to check the JML specifications of KOA [Kiniry et al., 2006], a platform for e-voting experimentation. Unlike our technique, all these techniques use propositional logic (via a relational logic) with only local simplifications at Boolean level. We, on the other hand, translates the code and specifications into an SMT logic to allow high-level simplifications.

SMT-based Encoding

The extended static checker for Java (ESC/Java [Flanagan et al., 2013] and ESC/Java2 [Cok and Kiniry, 2004]) checks Java programs with respect to functional properties. ESC/Java handles loops by unrolling them a finite num-ber of times, and replaces procedures calls with user-provided specifications.

Users express both the property and the intermediate annotations in JML.

ESC/Java translates the given program to Dijkstra’s guarded commands [Di-jkstra, 1975], encoding the property as assert commands. It then computes weakest preconditions to generate verification conditions as predicates in a first-order logic, and uses several theorem provers (e.g., Z3 [de Moura and Bjørner, 2008] and Yices [Dutertre, 2014]) to prove the verification conditions.

Failed proofs are turned into error messages and returned to the user. These techniques, however, either have undecidable logic due to quantification over infinite types, or require user-interactions to produce sufficiently precise in-variants due to their loop invariant inference schemes. Therefore, it sometimes reports a counterexample that might, with more effort, have been shown to be invalid and the solver may not terminate with a conclusive outcome.

JML Encoding

In our encoding, a JML quantifier ranges over the objects of a given class on the heap that the analyzed program manipulates. This interpretation has been used in various program verification tools, e.g., WHY3 platform [Bobot and Paskevich, 2011], TACO [Galeotti et al., 2013], PVS [van den Berg and Jacobs, 2001], KeY [Ahrendt et al., 2016], and Isabelle [Klein and Nipkow, 2006]. Such an interpretation encodes the Java semantics more precise compared to a classical one [see Leavens et al., 2006, Chapter 12 page 113] that quantifies also the objects that are constructed by the specifications. It is possible to obtain our quantifier semantics in JML by introducing predicates to omit the objects created in the annotations. The approach presented in [Beckert and Platzer, 2006] applied the range predicate\created(o)that specifies whether the objectohas been created on the heap or not. Another alternative proposed in [Ahrendt et al., 2009] avoided the existential quantifiers by introducing an instance variable of Boolean type to Java classes to indicate each object created or not.

3.9 Conclusion

We have presented an SMT-based encoding of object-oriented programs and specifications into bounded program verification. Our approach translates a Java program and a JML specification into a Quantified Bit-Vector Formula (QBVF), and solves it using an SMT solver. The novelty of our approach is exploiting the theory of quantified bit-vectors of recent SMT solvers, which allows logical constraints that are structurally closer to the original program and specification as well as, and high-level simplifications before being flat-tened into a basic logic. We aim to provide a lightweight bounded program verification approach, thus not all Java features are supported. Our encoding supports a basic subset of Java that does not include strings, real numbers, and concurrency. We support a class hierarchy definition without interfaces and abstract classes.

We have implemented our approach in the prototype tool InspectJ and compared it to JForge—a compatible SAT-based engine, on a large-scale imple-mentation of the Dijkstra’s shortest path algorithm. The results were encourag-ing; we found 3 previously-unknown bugs, and witnessed significantly better scalability over JForge. Checking programs with respect to bigger bounds allowed us to cover some execution paths that JForge could not cover. Thus it seems a viable approach to verify Java programs by translating the source code into SMT formulas and solving them, as it increases performance com-pared to other approaches and allows users to check more complex, high-level specifications within a greater scope.

Bounded program verification checks the correctness of a program with respect to user-provided class bounds and loop bounds. These two kinds of bounds, however, are not independent—their intricate relations are spread among the code and specifications, and have to be chosen carefully. When the bounds are not well chosen, it causes dead code or unused objects on the heap, thus resulting in a bad code coverage or heap coverage. In Chapter 4 we will investigate the relations between the bounds and present a calculus to compute the exact loop bounds based on class bounds for further improving the efficiency of bounded program verification technique.

CHAPTER 4

Computing Loop Bounds based on Class Bounds for