3.3 Static Techniques: Fully-Automated
3.5.3 Model Checking Tools
SPIN
SPIN [BA08] is a model checker whose main goal is to verify the correctness of distributed software models. Initially it was used for the verification of temporal logic properties of communication protocols specified in the Promela language. Promela supports simple data-types, non-deterministic assignment and conditionals, simple loops, thread creation, and message passing.
SPIN generates dedicated C source code for checking each model in a way that saves memory and improves performance. Furthermore, it offers the following options to speed up the model-checking process:
• Partial order reduction; • State compression;
• Bitstate hashing (a technique that instead of storing whole states, only stores their hash codes in a bit field).
Java Pathfinder
Java Pathfinder (JPF) [BHPV00] is an execution-based model checker for Java programs that modifies the Java Virtual Machine to implement a sys- tematic search over different thread schedules.
7
3.5. TOOLS 101
In a first version, JPF translated Java code to PROMELA and used SPIN for model checking [HP98]. However, the main problem of Promela relies on the fact that it does not support dynamic memory allocation and therefore is not well suited to modeling the Java language. So, to fill that gap, recent versions of JPF skip the translation phase and analyze the Java bytecode directly, and also handles a much larger class of Java programs than the first implementation. This new approach brings significant advantages:
• The use of the JVM makes it possible to store the visited states, which allows the model checker to use many of the standard reduction-based approaches (partial order, abstraction, etc) to prevent state-explosion. • As the visited states are stored, the model checker can use different search-order heuristics without being limited by the requirements of stateless search.
• The possibility to use techniques such as symbolic execution and ab- straction to compute inputs that force the system into states that are different from those previously visited allows for a high level of cover- age.
JPF has been successfully used inside NASA to find subtle errors in sev- eral Java components [BDG+04b].
SLAM
SLAM [BCLR04, BBC+06] automatically checks that a C program correctly uses the interface to an external library. The SLAM analysis engine forms the core of a new tool called Static Driver Verifier (SDV) that systematically analyzes the source code of Windows device drivers against a set of rules that define what it means for a device driver to properly interact with the Windows operating system kernel.
The basic idea is that checking a simple rule against a complex C program (such as a device driver) should be possible by simplifying the program to make the analysis tractable. That is, it should be possible to find an abstraction of the original C program that has all of the behavior of the original program (plus additional behavior that is not relevant for checking the rule of interest). Boolean programs [BR00b, BR00c] are the SLAM solution for such an abstraction.
The SLAM automated process can be split in three steps: abstraction, checking and refinement. In the first step, given a C program P and a set of predicates E , the goal is to efficiently construct a precise Boolean program abstraction B of P with respect to E . In the second step, given a Boolean program B and an error state, the goal is to check whether or not the error state is reachable. Finally, in the third step, if the Boolean program B contains an error path and this path is a feasible execution path
in the original program P, then the process has found a potential error. If this path is not feasible in P then the Boolean program B is refined so as to eliminate this false error path.
SLAM comprises the predicate abstraction tool C2BP [BPR01, BMMR01] and the BDD-based model checker BEBOP [BR00a] for Boolean programs.
BLAST
The Berkeley Lazy Abstraction Software Verification Tool (BLAST) [HJMS03, BHJM07] is an automatic verification tool for checking temporal safety prop- erties of C programs. The task addressed by BLAST is checking whether software satisfies the behavioral requirements of its associated interfaces. BLAST employs Counter-Example-Guided-Automatic-Abstraction (CEGAR) refinement to construct an abstract model that is then model-checked for safety properties. The abstraction is constructed on the fly, and only up to the requested precision.
Given a C program and a temporal safety property, BLAST either statically proves that the program satisfies the safety prop- erty, or provides an execution path that exhibits a violation of the property (or, since the problem is undecidable, does not ter- minate) [HJMS03].
Like SLAM, BLAST provides a language to specify reachability prop- erties. Internally, C programs are represented as Control Flow Automata (CFA), which resemble control flow graphs except that operators are placed on the edges rather than vertices. The BLAST lazy abstraction algorithm is composed of two phases. In the forward-search phase a reachability tree is built, which represents a portion of the reachable, abstract state space of the program. Each node of the tree is labeled by a vertex of the CFA and a formula, called the reachable region, constructed as a boolean combination of a finite set of abstraction predicates.
Initially the set of abstraction predicates is empty. The edges of the tree correspond to edges of the CFA and are labeled by basic program blocks or assume predicates. The reachable region of a node describes the reachable states of the program in terms of the abstraction predicates, assuming exe- cution follows the sequence of instructions labeling the edges from the root of the tree to the node.
If the algorithm finds that an error node is reachable in the tree, then it goes to the second phase, which checks if the error is real or a result of the abstraction being too coarse. In the latter case, a theorem prover is asked to suggest new abstraction predicates which rule out that particular spurious counterexample. The program is then refined locally by adding the new abstraction predicates only in the smallest subtree containing the spurious
3.5. TOOLS 103
error; the search continues from the point that is refined, without touching the part of the reachability tree outside that subtree.
VeriSoft
VeriSoft [God97] is a verification tool that tries to avoid state explosion by discarding the states it visits. VeriSoft does not store the visited states, allowing to repeatedly visit them and explore them. This method is state- less and has to limit the depth of its search to eschew non-termination.
This tool was built for systematically exploring the state space of systems composed of several concurrent processes executing arbitrary C/C++ code. The purpose of the tool is to automatically detect coordination problems between concurrent processes.
Essentially, VeriSoft takes as input the composition of several Unix pro- cesses that communicate by means of message queues, semaphores and shared variables that are visible to the VeriSoft scheduler. The scheduler traps calls made to access the shared resources, and by choosing which pro- cess will execute at each trap point, the scheduler is able to exhaustively explore all possible interleavings of the execution.
The approach used by VeriSoft is however incomplete for transition sys- tems that contain cycles.
SATABS
SATABS [CKSY04] is a model checking verification tool that uses a SAT- solver to construct abstractions and for symbolic simulation of counter- examples. This tool automatically generates and checks proof conditions for array bound violations, invalid pointer dereferencing, division by zero, and assertions provided by the user.
SATABS uses the SAT-based model checker Boppo [CKS05] to compute the reachable states of the abstract program. Boppo relies on a Quantified Boolean Formula (QBF) solver for fixed-point detection.
SATABS can verify concurrent programs that communicate via shared memory.
CBMC
CBMC [CKY03] is a tool that implements the Bounded Model Checking technique. It emulates a wide range of architectures and environments for the design under test. It supports both little and big Endian memory models, as well as header files needed for Linux, windows and Mac-OSX.
The main application of CBMC is for checking consistency of system- level circuit models given in C or SystemC with an implementation given in Verilog.
Saturn
Saturn [XA05] is a specialized implementation of BMC tailored to the proper- ties it checks. The authors of the tool have applied it to check two properties of Linux Kernel code: Null-pointer dereferences and locking API conventions. With these tests they have shown that the technique is sufficiently scalable to analyze the entire Linux kernel. Soundness is relinquished for perfor- mance: Saturn performs at most two unwindings of each loop (so bugs that require more than two unwindings are missed).
Cmc
Cmc [MPC+02] is an execution based model checker for C programs that explores different executions by controlling schedules at the level of the OS scheduler. Cmc stores a hash of each visited state. Cmc has been used to find errors in implementations of network protocols [ME04] and file sys- tems [YTEM06].
Chapter 4
State-of-the-Art: Slicing
Divide each difficulty into as many parts as is feasible and necessary to resolve it.
Ren´e Descartes, 1596 — 1650
Since Weiser first proposed the notion of slicing in 1979 in his PhD the- sis [Wei79], hundreds of papers have been proposed in this area. Tens of variants have been studied, as well was algorithms to compute them. Dif- ferent notions of slicing have different properties and different applications. These notions vary from Weiser’s syntax-preserving static slicing to amor- phous slicing which is not syntax-preserving; algorithms can be based on dataflow equations, information flow relations or dependency graphs.
Slicing was first developed to facilitate program debugging [Mar93, ADS93, WL86], but it is then found helpful in many aspects of the software devel- opment life cycle, including software testing [Bin98, HD95], software met- rics [OT93, Lak93], software maintenance [CLM96, GL91b], program com- prehension [LFM96, HHF+01], component re-use [BE93, CLM95], program integration [BHR95, HPR89b] and so on.
In this chapter, slicing techniques are presented including static slicing, dynamic slicing and the latest slicing techniques. We also discuss the con- tribution of each work and compare the major difference between them.
Structure of the chapter. In Section 4.1 is presented the concept of program slicing and its variants. The relationship among the different slicing techniques and the basic methods used to pose program slicing in practice are also discussed. In Section 4.2 the basic slicing approaches are presented. In Section 4.3 is reviewed the non-static slicing approaches. In Section 4.4 is reviewed the applications of program slicing. In Section 4.5 are presented some tools using the program slicing approach.
4.1
The Concept of Program Slicing
In this section it is presented the original static slice definition and also its most popular variants. At the end of each subsection, the respective concept will be clarified through. The examples are based on the program introduced hereafter in subsection 4.1.1.