23 research outputs found
Using Program Synthesis for Program Analysis
In this paper, we identify a fragment of second-order logic with restricted
quantification that is expressive enough to capture numerous static analysis
problems (e.g. safety proving, bug finding, termination and non-termination
proving, superoptimisation). We call this fragment the {\it synthesis
fragment}. Satisfiability of a formula in the synthesis fragment is decidable
over finite domains; specifically the decision problem is NEXPTIME-complete. If
a formula in this fragment is satisfiable, a solution consists of a satisfying
assignment from the second order variables to \emph{functions over finite
domains}. To concretely find these solutions, we synthesise \emph{programs}
that compute the functions. Our program synthesis algorithm is complete for
finite state programs, i.e. every \emph{function} over finite domains is
computed by some \emph{program} that we can synthesise. We can therefore use
our synthesiser as a decision procedure for the synthesis fragment of
second-order logic, which in turn allows us to use it as a powerful backend for
many program analysis tasks. To show the tractability of our approach, we
evaluate the program synthesiser on several static analysis problems.Comment: 19 pages, to appear in LPAR 2015. arXiv admin note: text overlap with
arXiv:1409.492
Exploiting shared structure in software verification conditions
Abstract. Despite many advances, today’s software model checkers and extended static checkers still do not scale well to large code bases, when verifying properties that depend on complex interprocedural flow of data. An obvious approach to improve performance is to exploit software structure. Although a tremendous amount of work has been done on exploiting structure at various levels of granularity, the fine-grained shared structure among multiple verification conditions has been largely ignored. In this paper, we formalize the notion of shared structure among verification conditions, propose a novel and efficient approach to exploit this sharing, and provide experimental results that this approach can significantly improve the performance of verification, even on pathand context-sensitive and dataflow-intensive properties.
Modular Verification of JML Contracts Using Bounded Model Checking
There are two paradigms for dealing with complex verification targets: Modularization using contract-based specifications and whole-program analysis. In this paper, we present an approach bridging the gap between the two paradigms, introducing concepts from the world of contract-based deductive verification into the domain of software bounded model checking. We present a transformation that takes Java programs annotated with contracts written in the Java Modeling Language and turns them into Java programs that can be read by the bounded model checker JBMC. A central idea of the translation is to make use of nondeterministic value assignments to eliminate JML quantifiers. We have implemented our approach and discuss an evaluation, which shows the advantages of the presented approach
A Perfect Model for Bounded Verification
A class of languages C is perfect if it is closed under Boolean operations
and the emptiness problem is decidable. Perfect language classes are the basis
for the automata-theoretic approach to model checking: a system is correct if
the language generated by the system is disjoint from the language of bad
traces. Regular languages are perfect, but because the disjointness problem for
CFLs is undecidable, no class containing the CFLs can be perfect.
In practice, verification problems for language classes that are not perfect
are often under-approximated by checking if the property holds for all
behaviors of the system belonging to a fixed subset. A general way to specify a
subset of behaviors is by using bounded languages (languages of the form w1*
... wk* for fixed words w1,...,wk). A class of languages C is perfect modulo
bounded languages if it is closed under Boolean operations relative to every
bounded language, and if the emptiness problem is decidable relative to every
bounded language.
We consider finding perfect classes of languages modulo bounded languages. We
show that the class of languages accepted by multi-head pushdown automata are
perfect modulo bounded languages, and characterize the complexities of decision
problems. We also show that bounded languages form a maximal class for which
perfection is obtained. We show that computations of several known models of
systems, such as recursive multi-threaded programs, recursive counter machines,
and communicating finite-state machines can be encoded as multi-head pushdown
automata, giving uniform and optimal underapproximation algorithms modulo
bounded languages.Comment: 14 pages, 6 figure
Enhancing active model learning with equivalence checking using simulation relations
We present a new active model-learning approach to generating abstractions of a system from its execution traces. Given a system and a set of observables to collect execution traces, the abstraction produced by the algorithm is guaranteed to admit all system traces over the set of observables. To achieve this, the approach uses a pluggable model-learning component that can generate a model from a given set of traces. Conditions that encode a certain completeness hypothesis, formulated based on simulation relations, are then extracted from the abstraction under construction and used to evaluate its degree of completeness. The extracted conditions are sufficient to prove model completeness but not necessary. If all conditions are true, the algorithm terminates, returning a system overapproximation. A condition falsification may not necessarily correspond to missing system behaviour in the abstraction. This is resolved by applying model checking to determine whether it corresponds to any concrete system trace. If so, the new concrete trace is used to iteratively learn new abstractions, until all extracted completeness conditions are true. To evaluate the approach, we reverse-engineer a set of publicly available Simulink Stateflow models from their C implementations. Our algorithm generates an equivalent model for 98% of the Stateflow models
The optimization of a symbolic execution engine for detecting runtime errors
In a software system, most of the runtime failures may come to light only during test execution, and this may have a very high cost. To help address this problem, a symbolic execution engine called RTEHunter, which has been developed at the Department of Software Engineering at the University of Szeged, is able to detect runtime errors (such as null pointer dereference, bad array indexing, division by zero) in Java programs without actually running the program in a real-life environment. Applying the theory of symbolic execution, RTEHunter builds a tree, called a symbolic execution tree, composed of all the possible execution paths of the program. RTEHunter detects runtime issues by traversing the symbolic execution tree and if a certain condition is fulfilled the engine reports an issue. However, as the number of execution paths increases exponentially with the number of branching points, the exploration of the whole symbolic execution tree becomes impossible in practice. To overcome this problem, different kinds of constraints can be set up over the tree. E.g. the number of symbolic states, the depth of the execution tree, or the time consumption could be restricted. Our goal in this study is to find the optimal parametrization of RTEHunter in terms of the maximum number of states, maximum depth of the symbolic execution tree and search strategy in order to find more runtime issues in a shorter time. Results on three open-source Java systems demonstrate that more runtime issues can be detected in the 0 to 60 basic block-depth levels than in deeper ones within the same time frame. We also developed two novel search strategies for traversing the tree based on the number of null pointer references in the program and on linear regression that performs better than the default depth-first search strategy
Program sketching
Sketching is a synthesis methodology that aims to bridge the gap between a programmer’s high-level insights about a problem and the computer’s ability to manage low-level details. In sketching, the programmer uses a partial program, a sketch, to describe the desired implementation strategy, and leaves the low-level details of the implementation to an automated synthesis procedure. In order to generate an implementation from the programmer provided sketch, the synthesizer uses counterexample-guided inductive synthesis (CEGIS). Inductive synthesis refers to the process of generating candidate implementations from concrete examples of correct or incorrect behavior. CEGIS combines a SAT-based inductive synthesizer with an automated validation procedure, a bounded model-checker, that checks whether the candidate implementation produced by inductive synthesis is indeed correct and to produce new counterexamples. The result is a synthesis procedure that is able to handle complex problems from a variety of domains including ciphers, scientific programs, and even concurrent data-structures