A verification approach for system-level concurrent programs (original) (raw)

Mechanized Verification of Fine-grained Concurrent Programs

Efficient concurrent programs and data structures rarely employ coarse-grained synchronization mechanisms (i.e., locks); instead, they implement custom synchronization patterns via fine-grained primitives, such as compare-and-swap. Due to sophisticated interference scenarios between threads, reasoning about such programs is challenging and error-prone, and can benefit from mechanization. In this paper, we present the first completely formalized framework for mechanized verification of full functional correctness of fine-grained concurrent programs. Our tool is based on the recently proposed program logic FCSL. It is implemented as an embedded domain-specific language in the dependently-typed language of the Coq proof assistant, and is powerful enough to reason about programming features such as higher-order functions and local thread spawning. By incorporating a uniform concurrency model, based on state-transition systems and partial commutative monoids, FCSL makes it possible to build proofs about concurrent libraries in a thread-local, compositional way, thus facilitating scalability and reuse: libraries are verified just once, and their specifications are used ubiquitously in client-side reasoning. We illustrate the proof layout in FCSL by example, and report on our experience of using FCSL to verify a number of concurrent algorithms and data structures. 1 2015/2/6 2015/2/6 Lemma step W A B (e1 : ST W A) (e2 : A-> ST W B) i (k : cont B): verify i e1 (fun y m => verify m (e2 y) k)-> verify i (y <-e1; e2 y) k. ST is a type synonym for STsep, hiding its pre's and post's.

Generic tools for verifying concurrent systems

Science of Computer Programming, 2002

Despite the enormous strides made in automatic veri cation technology over the past decade and a half, tools such as model checkers remain relatively underused in the development of software. One reason for this is that the bewildering array of speci cation and veri cation formalisms complicates the development and adoption by users of relevant tool support. This paper proposes a remedy to this state of a airs in the case of nite-state concurrent systems by describing an approach to developing customizable yet e cient veri cation tools.

Verifiable concurrent programming using concurrency controllers

Proceedings. 19th International Conference on Automated Software Engineering, 2004., 2004

We present a framework for verifiable concurrent programming in Java based on a design pattern for concurrency controllers. Using this pattern, a programmer can write concurrency controller classes defining a synchronization policy by specifying a set of guarded commands and without using any of the error-prone synchronization primitives of Java. We present a modular verification approach that exploits the modularity of the proposed pattern, i.e., decoupling of the controller behavior from the threads that use the controller. To verify the controller behavior (behavior verification) we use symbolic and infinite state model checking techniques, which enable verification of controllers with parameterized constants, unbounded variables and arbitrary number of user threads. To verify that the threads use a controller in the specified manner (interface verification) we use explicit state model checking techniques, which allow verification of arbitrary thread implementations without any restrictions. We show that the correctness of the user threads can be verified using the concurrency controller interfaces as stubs, which improves the efficiency of the interface verification significantly. We also show that the concurrency controllers can be automatically optimized using the specific notification pattern. We demonstrate the effectiveness of our approach on a Concurrent Editor implementation which consists of 2800 lines of Java code with remote procedure calls and complex synchronization constraints.

Mechanized Verification of Fine-grained Concurrent Programs Accompanying tutorial and code commentary for PLDI 2015 artifact evaluation

2015

specification of a lock. What is a suitable abstract specification for a locking structure? To answer this question, we adopt the idea of specifying concurrent data structures via abstract predicates [1] and provide a lock interface in the form of two abstract procedures: lock and unlock. Every data structure, implementing the lock protocol, will require to provide the implementation of these procedures. The lock interface is parametrized by two abstract predicates, constraining some part of the state: is_lock and locked. The former denotes the fact that the part of state implements belongs to the locking protocol, while the latter one identifies that the lock is currently being locked by the current thread. Both predicates should satisfy a number of axioms, which are packaged into a dependent record in the section LockStruct (where the predicates are referred to as locked_op and is_lock_op). For instance, the following axiom expresses the lock’s mutual exclusion property: forall s ...

Modular verification of shared-memory concurrent system software

2011

Abstract Software is large, complex, and error-prone. According to the US National Institute of Standards and Technology, software bugs cost the US economy an estimated $60 billion each year. The trend in hardware design of switching to multi-core architectures makes software development even more complex. Cutting software development costs and ensuring higher reliability of software is of global interest and a grand challenge.

VCC: Contract-based modular verification of concurrent C

2009 31st International Conference on Software Engineering - Companion Volume, 2009

Most system level software is written in C and executed concurrently. Because such software is often critical for system reliability, it is an ideal target for formal verification. Annotated C and the Verified C Compiler (VCC) form the first modular sound verification methodology for concurrent C that scales to real-world production code. VCC is integrated in Microsoft Visual Studio and it comes with support for verification debugging: an explorer for counterexamples of failed proofs helps to find errors in code or specifications, and a prover log analyzer helps debugging proof attempts that exhaust available resources (memory, time). VCC is currently used to verify the core of Microsoft Hyper-V, consisting of 50,000 lines of system-level C code.

The effect of uncontrolled concurrency on model checking

Concurrency and Computation: Practice and Experience, 2008

Correctness of concurrent software is usually checked by techniques such as peer code reviews or code walkthroughs and testing. These techniques, however, are subject to human error, and thus do not achieve an in-depth verification of correctness. Model-checking techniques, which can systematically identify and verify every state that a system can enter, are a powerful alternative method for verifying concurrent systems. However, the usefulness of model checking is limited because the number of states for concurrent models grows exponentially with the number of processes in the system. This is often referred to as the 'state explosion problem.' Some processes are a central part of the software operation and must be included in the model. However, we have found that some exponential complexity results due to uncontrolled concurrency introduced by the programmer rather than due to the intrinsic characteristics of the software being modeled. We have performed tests on multimedia synchronization to show the effect of abstraction as well as uncontrolled concurrency using the Promela/SPIN model checker. We begin with a sequential model not expected to have exponential complexity but that results in exponential complexity. In this paper, we provide alternative designs and explain how uncontrolled concurrency can be removed from the code. synchronization and reliable communication protocols, and in these systems interaction among software/hardware modules should be coordinated to avoid unexpected failures. The verification of software and concurrent systems has been widely studied in . Also, verification methods used in different parts of the world have been surveyed . Several verification tools have been developed for verification of systems implemented in popular languages like Java . Some of the verification methods that are employed are code inspections/walkthroughs, pair programming, automated static analysis tools, coverage, capture/playback, model checking, and development testing tools . We are interested in model-checking verification since model checking is more rigorous than most other verification techniques because it can check all possible states a model can enter. There are usually three steps in model-checking verification: modeling, programming in the language of a model checker in order to implement a given model, and verification. Since all three steps are likely to be handled by a single person, we refer to a programmer as a person who models, implements, and verifies the model using a model checker. In this sense, a programmer is able to apply abstraction to the model as well as verify the model.

A comparison of runtime assertion checking and theorem proving for concurrent and distributed systems

Distributed systems play an essential role in society today. For example, distributed systems form the basis for critical infrastructure in different domains such as finance, medicine, aeronautics, telephony, and Internet services. It is of great importance that such systems work properly. However, quality assurance of distributed systems is non-trivial since they depend on unpredictable factors, such as different processing speeds of independent components. It is highly challenging to test such distributed systems after deployment under different relevant conditions. These challenges motivate frameworks combining precise modeling and analysis with suitable tool support. In particular, compositional verification systems allow the different components to be analyzed independently from their surrounding components. Thereby, it is possible to deal with systems consisting of many components. Object orientation is the leading framework for concurrent and distributed systems, recommended by the RM-ODP [15]. However, method-based communication between concurrent units may cause busy-waiting, as in the case of remote and synchronous method invocation, e.g., Java RMI [2]. Concurrent objects communicating by asynchronous method calls have been proposed as a promising framework to combine object-orientation and distribution in a natural manner. Each concurrent object encapsulates its own state and processor, and internal interference is avoided as at most one process is executing on an object at a time. Asynchronous method calls allow the caller to continue with its own activity without blocking while waiting for the reply, and a method call leads to a new process on the called object. The notion of futures [6, 19, 12, 20] improves this setting by providing a decoupling of the process invoking a method and the process reading the returned value. By sharing future identities, the caller enables other objects to wait for method results. However, futures complicate program analysis since programs become more involved compared to semantics with traditional method calls, and in particular local reasoning is a challenge. ABS [17] is a high-level imperative object-oriented modeling language, based on the concurrency and synchronization model of Creol [18]. It supports futures and concurrent objects with an asynchronous communication model suitable for loosely coupled objects in a distributed setting. In this work, we present our testing and verification tools for ABS programs. The execution of a distributed system can be represented by its communication history or trace; i.e., the sequence of observable communication events between system components [8, 14]. At any point in time the communication history abstractly captures the system state [10, 9]. In fact, traces are used in the semantics for full abstraction results (e.g., [16, 1]). The local history of an object reflects the communication visible to that object, i.e., between the object and its surroundings. A system may be specified by the finite initial segments of its communication histories, and a history invariant is a predicate which holds for all finite sequences in the set of possible histories, expressing safety properties [5]. In our reasoning system, we formalize object communication by an operational semantics

Back and Forth: Prophecy Variables for Static Verification of Concurrent Programs

2009

Several static proof systems have been developed over the years for verifying shared-memory multithreaded programs. These proof systems make use of auxiliary variables to express mutual exclusion or non-interference among shared variable accesses. Typically, the values of these variables summarize the past of the program execution; consequently, they are known as history variables. Prophecy variables, on the other hand, are