CAVEAT : technique and tool for Computer Aided VErification And Transformation

E. Pascal Gribomont and Didier Rossetto

Institut Montefiore, Université de Liège, Sart-Tilman B28, B-4000 Liège (Belgium) gribomon,rossetto@montefiore.ulg.ac.be

accepted for presentation to CAV’95 (LNCS) .

Abstract. We describe caveat, a technique and a tool (under development) for the stepwise design and verification of nearly finite-state concurrent systems (NFCS). A concurrent system is nearly finite-state when most of its variables have a finite range (Booleans, bounded integers). The heart of caveat is a tool for verifying invariants, i.e., inductive safety properties. The underlying method is classical : formula I is an invariant for system S if and only if some formula Φ_{I} =_{def} {I}S{I} is valid. If S is an NFCS, the formula Φ_{I} contains only a small set of non-boolean variables. caveat uses the connection method to extract from Φ_{I} a (small) set Ψ of paths (some kind of assertions) about the non-boolean variables; Φ_{I} is valid if and only if all paths contain connections, i.e., are inconsistent. For typical NFCS given with a correct invariant, the formula Φ_{I} is rather large (more than 100 lines) but Ψ is quite small (a dozen one-line formulas). The second part of caveat (not implemented yet) supports an incremental development method that is fairly systematic, but has proved to be flexible enough in practice.

1 Introduction

From the theoretical point of view, formal methods are a rather satisfactory answer to the problem of unreliable software. However, from the practical point of view, these methods are nearly useless without appropriate tools.

It is well-known that fully automatic tools for general program design and/or verification can not exist, so we have to be satisfied with semi-automatic tools and/or restricted classes of programs.

The most classical approach to non-automatic program verification is the invariant method. Its principle is to reduce the correctness problem (“Is this program correct w.r.t. this specification ?”) to the validity problem (“Is this formula a valid formula of classical first order logic ?”). Even when automation is not considered, the invariant method has two drawbacks : it is restricted to safety properties, and it is “creative” in the sense that the validation of a safety property implies the (non-trivial) design of an adequate invariant, that is, a stronger safety property which can be proved by induction. The first problem has been satisfactorily solved by the introduction of temporal logic; the second problem is dealt with in more or less satisfactory ways, and for more or less general classes of programs. The pragmatic view (and caveat is / will be a pragmatic tool) is that formal methods become interesting when, first, testing methods really prove disappointing, second, reliability is really required, and third, programs are subtle and tricky even when they are not long. This is often the case for concurrent, distributed, reactive systems, and the problem of invariant construction seems especially important for such systems. caveat is an attempt to automate an invariant-based stepwise design/verification method introduced in [13, 14, 16].

Most earlier approaches to (semi-)automatic program verification have been based on (semi-)automatic theorem proving, for classical logic and sometimes for temporal logic. A pragmatic drawback of theorem provers is that they are mostly interactive. Even if the prover really performs the biggest part of the verification task, the user has to oversee the whole verification process, and from time to time needs to interact with it. The problem lies with the rather poor ability of proving systems to extract from a large set of mostly elementary verification steps the small subset which is outside the scope of purely automatic tools. The success of the semi-automatic theorem proving approach depends on the skill of the user [11].

A more recent approach is restricted to finite-state systems. The principle is that both the finite-state system and the specification can be modelled by a formula of propositional temporal logic, or by some kind of automaton. As a result, system verification is decidable, for instance by model checking algorithms [8, 29]. Recent improvements in the performances of computer systems, and also in the search algorithms, have led to rather powerful tools. This induced attempts to extend these techniques to some classes of infinite-state systems, but only moderately successful results have been obtained until now [20, 30]. On the contrary, some severe theoretical restrictions to this approach have been obtained [1]. Besides, when a tested finite-state program is incorrect, the verification system gives little high-level insight about how the program should be corrected; similarly, the validation of a correct program gives little insight about how the program works and why it is correct.

Another promising track comes from recent improvements in tautology checking, especially the connection method (see [5, 31]) and the concept of (ordered) binary decision diagram (see [6, 26]). It is rather natural to wonder whether these techniques remain practically usable outside pure propositional logic. caveat has evolved from some successful experiments in this area.

Section 2 introduces caveat with a very elementary example and discusses the main choices we have made in the strategy of invariant verification. Section 3 accounts for a more significant experiment and demonstrates the usefulness of the approach in a restricted but important class of applications. It also presents an introduction to incremental design and verification. Section 4 is a brief comparison with related works.

2 The heart of caveat : tautological reduction

2.1 Position of the problem

A formula I is an invariant of a concurrent system S if, in all computations, successors of states satisfying I also satisfy I. Hoare’s axiom, or the liberal version of Dijkstra’s weakest precondition calculus, reduces the problem of invariant verification to the purely logical problem of validity checking. With familiar notation (illustrated below), the formula to validate is Φ_{I} =_{def} (I ⇒ wlp[S; I]). The construction of Φ_{I}, when S and I are given, is (usually) straightforward. The validation, however, is not, since Φ_{I} is typically a rather large formula.

A formula J expresses a safety property of S with initial condition A if every state of every computation satisfies J. This holds if and only if an invariant I of S exists such that (A ⇒ I) and (I ⇒ J). The standard verification problem is to determine whether some system S with initial condition A satisfies the safety property (expressed by) J.

If S is a non-parametric finite-state system, the formulas A, J, I and Φ_{I} are propositional and full automation is possible. However, the construction of the invariant I is not a trivial task. Model checking is usually more effective here, since an explicit form of the invariant I is not needed; the model checker simply verifies that all accessible states satisfy J. (The set of accessible states determines the strongest invariant implied by A, often denoted sin[A; S].)

Pure model-checking does not apply if S is an infinite-state system. In this case, A, J, I and Φ_{I} are formulas of some first-order language (for instance, the language of number theory) and the verification problem becomes theoretically unsolvable even for a rather restricted class of programs. The invariant method still works, but is not easily turned into a reasonably efficient semi-automatic method.

There is, however, a large and interesting class of “borderline” cases, for which Φ_{I} is a large formula with only few occurrences of non-boolean variables. The method illustrated in the sequel seems very promising for this class. For the sake of simplicity, it is first introduced with the help of a purely finite-state example, even though it does not show its full potential in this case.

2.2 The connection method

The connection method can be viewed as an efficient implementation of the classical tableau method, used to determine whether a formula or a set of formulas has a model. The principle of the method is to reduce the initial formula into sets of literals, in such a way that the initial formula has no model if and only if each of the sets of literals contains a connection, i.e., a tuple of contradictory elements. In a purely propositional framework, only pairs like {p, ¬p} are considered. In our framework, a connection is a bit more general; typical instances are {x > y, x = y, x < y} and {at ℓ_{0}, at ℓ_{1}}, where ℓ_{0} and ℓ_{1} are distinct locations of the same process.

The connection method can be a powerful technique [31]; it is illustrated in the sequel of this section, first with a simplistic example.

The example is a two-process naive mutual exclusion algorithm, that has to be checked for mutual exclusion. The set of processes is {P, Q}. Each process contains three locations, identified by subscripts 0 (idle state), w (waiting state) and c (critical state), so P = {p_{0}, p_{w}, p_{c}} and Q = {q_{0}, q_{w}, q_{c}}. There are two Boolean variables inP and inQ. The set of transitions is

T = \begin{cases}(p_{0}, inP := true , p_{w}) , \\ (p_{w}, ¬inQ ⟶ skip , p_{c}) , \\ (p_{c}, inP := false , p_{0}) , \end{cases} (q_{0}, inQ := true , q_{w}) ,

(q_{w}, ¬inP ⟶ skip , q_{c}) ,
(q_{c}, inQ := false , q_{0}) } .

Comment. The formal notation used here and in caveat to write programs has been introduced in [14]. It is similar in spirit to many other notations based on states and transitions, e.g. the language of Action Systems introduced in [3].

There are two Boolean variables and each process has three control locations, leading to a state space of 36 possible states. The main safety property of interest is mutual exclusion, formalized as

J =_{def} ¬(at p_{c} ∧ at q_{c}) .

An acceptable initial condition is :

A =_{def} (at p_{0} ∧ at q_{0} ∧ ¬inP ∧ ¬inQ) .

An appropriate invariant I is

\begin{matrix}(at p_{c} ⇒ (¬inQ ∨ at q_{w})) ∧ (at q_{c} ⇒ (¬inP ∨ at p_{w})) ∧ \\ (at p_{0} ≡ ¬inP) ∧ (at q_{0} ≡ ¬inQ) . \end{matrix}

One sees easily that both A ⇒ I and I ⇒ J holds,^{1} and caveat is used to check that I is really an invariant. It is sufficient to show that formula (I ⇒ wlp[τ; I]) holds for each transition τ, and we consider here τ = (p_{w}, ¬inQ ⟶ skip, p_{c}). The corresponding verification formula, say Φ, is obtained by wlp-calculus :

\begin{matrix}(¬¬inP ∧ (at q_{0} ⇒ ¬inQ) ∧ (¬inQ ⇒ at q_{0})) ⇒ \\ (¬inQ ⇒ [\begin{matrix}(¬inQ ∨ at q_{w}) ∧ (at q_{c} ⇒ ¬inP) ∧ ¬¬inP ∧ \\ (at q_{0} ⇒ ¬inQ) ∧ (¬inQ ⇒ at q_{0})]) . \end{matrix} \end{matrix}

With standard elementary techniques, Φ is reduced into two formulas, i.e.,

((at q_{0} ⇒ ¬inQ) ∧ (¬inQ ⇒ at q_{0})) ⇒ (¬inQ ⇒ (¬inQ ∨ at q_{w})) (1)

and (¬¬inP ∧ (at q_{0} ⇒ ¬inQ) ∧ (¬inQ ⇒ at q_{0})) ⇒ (¬inQ ⇒ (at q_{c} ⇒ ¬inP)) .

Comment. The first one should have been

(¬¬inP ∧ (at q_{0} ⇒ ¬inQ) ∧ (¬inQ ⇒ at q_{0})) ⇒ (¬inQ ⇒ (¬inQ ∨ at q_{w})) but inP occurred only once, and has therefore been replaced by its polarity T, leading to formula (1). This transformation and some similar ones are automated in caveat.^{2}

The subformula tableau for formula (1) is given in Figure 1.

Each line of the subformula tableau corresponds to a node of the syntactic tree of the formula (the tree is traversed depthfirst). Let us consider formula (1). The polarity of the formula itself (root line a_{1}) is F, meaning that our “goal” (hopefully unreachable) is to falsify the (hopefully valid) formula a_{1}. This formula is an implication; it will be false if and only if its antecedent a_{2} is true and

1 Recall that each process is at exactly one location at a time; this location rule is

“wired in” caveat. 2 These simplifications are classical in resolution-based theorem provers; see e.g. [23].

Polarity  Formula  P-type S-type
a_{1}  F \begin{matrix}((at q_{0} ⇒ ¬inQ) ∧ (¬inQ ⇒ at q_{0})) \\ ⇒ \\ (¬inQ ⇒ (¬inQ ∨ at q_{w})) \end{matrix} α  −
a_{2} T  (at q_{0} ⇒ ¬inQ) ∧ (¬inQ ⇒ at q_{0})  α  α
a_{3} T  at q_{0} ⇒ ¬inQ  β  α
a_{4}  F  at q_{0}  −  β
a_{5} T  ¬inQ  α  β
a_{6}  F  inQ  −  α
a_{7} T  ¬inQ ⇒ at q_{0}  β  α
a_{8}  F  ¬inQ  α  β
a_{9} T  inQ  −  α
a_{10} T  at q_{0}  −  β
a_{11} F  ¬inQ ⇒ (¬inQ ∨ at q_{w})  α  α
a_{12} T  ¬inQ  α  α
a_{13} F  inQ  −  α
a_{14} F  (¬inQ ∨ at q_{w})  α  α
a_{15} F  ¬inQ  α  α
a_{16} T  inQ  −  α
a_{17} F  at q_{w}  −  α

Fig. 1. Subformula tableau for formula (1)

its consequent a_{11} is false. As a result, the polarities of a_{2} and a_{11} respectively are T and F. Besides, the P-type (primary type) of a_{1} is α, meaning that a_{1} is a conjunctive line.^{3} The S-type (secondary type) of a line if the P-type of its father, so the root line has no S-type and the atomic lines (corresponding to atomic subformulas) have no P-type.

The subformula tableau is used to construct the verification acyclic graph, or VAG. The VAG corresponding to formula (1) is given in Figure 2 (left).

Each node in a VAG is a sequence of subformula indices; we distinguish atomic and non-atomic indices, corresponding respectively to atomic and nonatomic subformulas. The index i of an atomic subformula a_{i} is printed in boldface. The VAG is constructed from root to leaves according to the following rule. A node has successor(s) if it contains at least one non-atomic index i. If a_{i} has P-type α, there is only one successor, obtained by replacing i by j, k, where a_{j} and a_{k} are the immediate subcomponents of a_{i} (if a_{i} is a negation, there is only one subcomponent a_{j}). If a_{i} has P-type β, there are two successors, obtained

3 Conjunctive lines are denied implications, denied disjunctions, asserted conjunctions; disjunctive lines (P-type is β) are asserted implications, asserted disjunctions and denied conjunctions. P-type is not really relevant for unary connectives, but we attribute P-type α to negations.

[figure: \underline{1}; \underline{2}, 11; 3, 7, \underline{11}; 3, 7, \underline{12}, 14; 3, 7, 13, \underline{14}; 3, 7, \underline{15}, 17; \underline{3}, 7, 16; 4, \underline{7}; \underline{5}, 7; \underline{8}; 6, \underline{7}; 9; 10]  \begin{matrix}1 & 4, 9, 13, 16, 17 & 9, 13 \\ 2 & 4, 10, 13, 16, 17 & 4, 10 \\ 3 & 6, 9, 13, 16, 17 & 6, 9 \\ 4 & 6, 10, 13, 16, 17 & 6, 16 \end{matrix}

Fig. 2. Verification acyclic graph and path list for formula (1)

by replacing i by j and k, respectively. To save place, atomic indices of a node are not inherited by its successor(s). Leaves contain atomic indices only. The construction is non-deterministic, since a node can contain several non-atomic indices (in Figure 2, selected indices are underlined). The strategy of considering indices of P-type α first leads to a smaller VAG and is therefore adopted. An example about formula (1). Three non-atomic indices 3, 7, 15 occur in node (3, 7, 15, 17); only index 15 is of P-type α, so it is selected. There is only one successor, obtained by replacing 15 by 16; besides, atomic index 17 is omitted.

It is now clear why the elementary claims introduced in paragraph 2.2 are called paths : each claim corresponds to a (maximal) path in the VAG. The last step of the connection method is to explore the VAGs and to list their paths. Each path connects the root of the VAG to a leaf and is identified in the list by the atomic indices occurring in the labels of its nodes. For instance, the first path of the VAG corresponding to formula (1) is

1 → 2, 11 → … → 3, 7, 13, 14 → 3, 7, 15, 17 → 3, 7, 16 → 4, 7 → 8 → 9 ,

so this path will be identified as 4, 9, 13, 16, 17.

A formula is valid if each path of the corresponding path list contains a connection. The path list for formula (1) is given in Figure 2 (right), with a connection for each path. As a consequence, formula (1) is valid.

2.3 Concurrent construction and exploration of the VAG

An elementary but useful optimization consists in closing a path as soon as a connection is detected in it. This gives rise to shortened VAGs and path lists; those for formula (1) are given in Figure 3.

[figure: \underline{1}; \underline{2}, 11; 3, 7, \underline{11}; 3, 7, \underline{12}, 14; 3, 7, 13, \underline{14}; 3, 7, \underline{15}, 17; 3, 7, 16]  1 3, 7, 13, 16, 17 13, 16

Fig. 3. Optimized VAG and shortened path list for formula (1)

2.4 The non-propositional case

The connection method reduces the problem of checking the validity of formula

Φ =_{def} (a ∧ b ∧ c) ⇒ ¬(b ⇒ ¬a)

to the problem of finding a connection within each element of the path list

Ψ =_{def} ({T : a; T : b; T : c; F : b} , {T : a; T : b; T : c; F : a}) . The second problem is trivial, but the reduction process itself is not. Now, let us consider a rather similar case. The formula

Φ′ =_{def} (x > y ∧ b ∧ c) ⇒ ¬(b ⇒ x < y)

is valid if each path of the list Ψ′ =_{def} ({T: x > y; T: b; T: c; F: b} , {T: x > y; T: b; T: c; T: x < y}) is connected. The first path contains a trivial, propositional connection (atom b appears with both polarities) but the connection contained in the second path is {T : x > y; T : x < y}, which is non-propositional.^{4}

The interesting point is that half the verification work (in this example) remains within the propositional framework and hence fully automatic. Our working hypothesis is that, for many “nearly finite-state systems”, most of the invariant verification work will reduce to tautology checking and nearly all paths (say 99.9 %) will be closed (a connection will be found) in an automatic way. The main advantage we seek from our approach with respect to the more classical theorem-proving approaches, is the inherent ability of the connection method to “extract” the tiny fraction of the verification work which falls outside the propositional framework. This fraction is then isolated from the rest, and dealt

4 More precisely, analysis of the “atoms” x > y and x < y is needed to detect that they are contradictory.

with in a classical way; either we use a knowledge base and a theorem prover that would tell us that x > y and x < y are not simultaneously satisfiable, or we simply report to the user the sublist of unconnected paths. The example presented in the next section illustrates what can be achieved even without ATP (automatic theorem proving).

3 Ricart and Agrawala’s algorithm

It is now usual to verify some fine-grained version of a concurrent system by first considering some coarser-grained version(s). This approach was already used in [9] and [21] and is turned into a systematic method in [14] and [16]. We extract from the latter [16, p. 43] an intermediate, medium-grained version of Ricart and Agrawala’s N-process mutual exclusion algorithm, introduced in [27].

3.1 The algorithm and its invariant

The basic idea, as introduced in [27], is as follows. A node attempting to invoke mutual exclusion sends a request to all other nodes. On receipt of the request, the other nodes send an immediate reply or defer it. When all replies have been received, the access to the critical section is granted. A deferred reply is delayed until the replying node has completed its own access to the critical section.

Some notation is introduced, mostly in accordance with [27].

rcs_{p} : p needs access to the resource; [ ] : internal activity, able to alter rcs only; RCS_{p} : p requests access to the resource (Request Critical Section); OR_{p}^{q} : p waits for a reply from q (Outstanding Reply); RD_{p}^{q} : p defers a reply to q (Reply Deferred); SN_{p} : p has requested access at that time (Sequence Number)

(time-stamp, implementing Lamport’s “Bakery algorithm”);

SN_{p} <SN_{q}: p takes precedence over q; time : monotonically increasing integer, models real time; P : the set of nodes; P = {p, q, r, …}; N : the number of nodes; |P| = N; P_{p} : short for P \{p}; |P_{p}| = N − 1; X_{p} : variable ranging over subsets of P_{p}.

The transitions of S are given in Figure 4 (for all distinct stations p and q). In order to switch, say, from control location p_{2} to p_{3}, node p has to send a

request to each member of P_{p}. In system S, the N −1 corresponding messages are already modelled by N − 1 distinct transitions, but each of them remains rather abstract and the communication itself is modelled by switching the Boolean variable OR_{p}^{q} from false (i.e. 0) to true (i.e. 1), just as if communications always were reliable and timeless; system S is not very coarse-grained, but not really fine-grained either. Also note that the N − 1 communications are performed in arbitrary order; q ∈ X_{p} means, “node q has been issued the request from p”. Intuitively, the predicate at^{q} p_{3} means : “from the point of view of station q, the

(p_{0}, ¬rcs_{p} ⟶ [ ] , p_{0}) , (p_{0}, rcs_{p} ⟶ (RCS_{p}, SN_{p}, time) := (1, time, time + 1) , p_{2}) , (p_{2}, q ∈ P_{p}\X_{p} ⟶ (OR_{p}^{q}, X_{p}) := (1, X_{p}∪{q}) , p_{2}) , (p_{2}, X_{p} = P_{p} ⟶ X_{p} := ∅ , p_{3}) , (p_{3}, q ∈ P_{p}\X_{p} ∧ RCS_{q} ∧ SN_{q} < SN_{p} ⟶ (RD_{q}^{p}, X_{p}) := (1, X_{p}∪{q}) , p_{3}) , (p_{3}, q ∈ P_{p}\X_{p} ∧ [¬RCS_{q} ∨ SN_{p} < SN_{q}] ⟶ (OR_{p}^{q}, X_{p}) := (0, X_{p}∪{q}) , p_{3}) , (p_{3}, X_{p} = P_{p} ⟶ X_{p} := ∅ , p_{4}) , (p_{4}, q ∈ P_{p}\X_{p} ∧ ¬OR_{p}^{q} ⟶ X_{p} := X_{p}∪{q} , p_{4}) , (p_{4}, X_{p} = P_{p} ⟶ X_{p} := ∅ , p_{5}) , (p_{5}, rcs_{p} ⟶ [ ] , p_{5}) , (p_{5}, ¬rcs_{p} ⟶ RCS_{p} := 0 , p_{6}) , (p_{6}, q ∈ P_{p}\X_{p} ∧ RD_{p}^{q} ⟶ (RD_{p}^{q}, OR_{q}^{p}, X_{p}) := (0, 0, X_{p}∪{q}) , p_{6}) , (p_{6}, q ∈ P_{p}\X_{p} ∧ ¬RD_{p}^{q} ⟶ X_{p} := X_{p}∪{q} , p_{6}) , (p_{6}, X_{p} = P_{p} ⟶ X_{p} := ∅ , p_{0}) .

Fig. 4. Abstract code of an intermediate version of R-A algorithm

place predicate at p_{3} is true”. Below is the formal definition of the latter and other similar predicates.

at^{q} p_{0} =_{def} ((at p_{6} ∧ q ∈ X_{p}) ∨ at p_{0}) ,
at^{q} p_{2} =_{def} (at p_{2} ∧ q ∈ P_{p}\X_{p}) ,
at^{q} p_{3} =_{def} ((at p_{2} ∧ q ∈ X_{p}) ∨ (at p_{3} ∧ q ∈ P_{p}\X_{p})) ,
at^{q} p_{4} =_{def} ((at p_{3} ∧ q ∈ X_{p}) ∨ (at p_{4} ∧ q ∈ P_{p}\X_{p})) ,
at^{q} p_{5} =_{def} ((at p_{4} ∧ q ∈ X_{p}) ∨ at p_{5}) ,
at^{q} p_{6} =_{def} (at p_{6} ∧ q ∈ P_{p}\X_{p}) .

The invariant I is the conjunction, for all distinct p and q, of the assertions

1_{p} : [X_{p} ⊂ P_{p} ∧ (at p_{05} ⇒ X_{p} = ∅)] ,
2_{p} : [at p_{06} ≡ ¬RCS_{p}] ,
3_{pq} : [SN_{p} ≠SN_{q} ∧ SN_{p} <time ∧ ((at^{q} p_{5} ∧ RCS_{q}) ⇒ SN_{p} <SN_{q})] ,
4_{pq} : [(RD_{q}^{p} ⇒ OR_{p}^{q}) ∧ (¬at^{q} p_{3} ≡ (OR_{p}^{q} ⇒ RD_{q}^{p}))] ,
5_{pq} : [(at^{q} p_{6} ∧ at^{p} q_{4}) ∨ (RD_{p}^{q} ≡ (at^{p} q_{4} ∧ RCS_{p} ∧ SN_{p} <SN_{q}))] .

(2)

Acceptable initial conditions are, for all distinct stations p and q, at p_{0} ∧ X_{p} = ∅ ∧ ¬RCS_{p} ∧ ¬OR_{p}^{q} ∧ ¬OR_{q}^{p} ∧ ¬RD_{p}^{q} ∧ ¬RD_{q}^{p} ∧ SN_{p} ≠SN_{q} .

3.2 What can be obtained in an automatic way ?

Our task is to use caveat in order to determine whether I really is an invariant of system S. This system is typically “nearly propositional”. Most of the variables are Boolean; SN_{p} and X_{p} are not, but SN_{p} <SN_{q} and q ∈ X_{p} are. Another worrying point is the parameter N (number of nodes in the network). Clearly enough, there exists a formula I(p, q) — in fact, the conjunction of formulas (2)

— such that the invariant really is

I =_{def} ∀p ∀q ≠p I(p, q) .

Due to symmetry, we can now fix two specific distinct stations p and q and decide that only the transitions explicitly written in Figure 4 ought to be checked against invariant I. Now, we have 14 transitions to consider instead of O(N^{2}), but the size of the invariant is still O(N^{2}). We cannot similarly reduce the triple {I}τ{I} to the triple {I(p, q)}τ{I(p, q)}. However, we can reduce the triple {I}τ{I} to the family of N ∗ (N − 1) triples {I}τ{I(p′, q′)}. We can further observe that, what matters about p′, q′ are whether they belong to {p, q} or not. Let us now assume that p, q, r, s are four distinct stations (and therefore, that N ≥ 4). We can reduce the aforementioned family of triples to only seven triples,^{5} listed below :

\begin{matrix}1. & \{I\} τ \{I(p, q)\} , \\ 2. & \{I\} τ \{I(q, p)\} , \\ 3. & \{I\} τ \{I(p, s)\} , \\ 4. & \{I\} τ \{I(r, q)\} , \\ 5. & \{I\} τ \{I(s, p)\} , \\ 6. & \{I\} τ \{I(q, r)\} , \\ 7. & \{I\} τ \{I(r, s)\} . \end{matrix}

Triple 3, for instance, serves as a pattern for N − 2 triples of the family since s stands for any node distinct from p and q.^{6}

A similar reduction can be operated on the precondition. For instance, triple 1 can be replaced by

{I(p, q) ∧ I(q, p) ∧ I(p, s) ∧ I(r, q) ∧ I(s, p) ∧ I(q, r) ∧ I(r, s)} τ {I(p, q)}

Such a triple, when fully developed, is a finite piece of text. The corresponding formula Φ is truly propositional, provided that predicates like SN_{p} < SN_{q} and q ∈ X_{p} are considered as atoms (we call pseudo-atoms these predicates; true atoms are location predicates and Boolean variables). The connection method will work, but only connections involving atoms will be detected with certainty (they contain the same atom with both polarities). Connections involving pseudo-atoms can remain undetected. For instance,

{T : SN_{p} < SN_{q}, F : SN_{p} < SN_{q}}

will be detected, but

{T : SN_{p} < SN_{q}, T : SN_{p} = SN_{q}}

will not. The simple default strategy followed by caveat is to suppose that when a path can not be closed using atoms only, pseudo-atoms form a connection. Such paths are collected, and the suspected connections are put into a table to be validated by the user. Even if, say, 1000 paths contain connections involving pseudo-atoms, it is possible that only a dozen distinct connections exist. So caveat should sort the paths according to suspected connections, in order to minimize the work performed by the user.

5 Only four triples are needed if N = 3, and two triples if N = 2. 6 Taking symmetry into account may allow to reduce the number of assertions and

the number of transitions. The favourable case (as for this version of Ricart and Agrawala’s algorithm) occurs when these numbers become true constants (independent from the size N of the network, or from any other parameter). An example of the unfavourable case is reported in [14].

3.3 What is obtained using caveat ?

The main data file for caveat contains the program to be verified. The declarations are rather standard and omitted here. The languages for transitions and assertions are slightly adapted from those used in Figure 4 and Formula (2).

Two differences exist between the real code and the abstract code in Figure 4. First, [ ] becomes skip, since it does not interfere with the invariant. Second, the set X_{p} is implemented as a Boolean array XP, with XP[q] meaning q ∈ X_{p} (we suppose that XP[p] is true, although that does not really matter). The constant XPempty is such that XPempty[q] holds only if q=p; the variable XPCard records the number of true elements in the array XP. The transformation induced in the real code is straightforward.

The main data file also contains the invariant to be verified.

caveat generates and explores the VAG for each of the 14 transitions; if the invariant to be checked is I(p, q)∧I(q, p), this takes ten minutes (SUN Sparc 10) since, in spite of the simplifications introduced above, the path list remains long.^{7} However, most of the paths are closed by the system, and the set of “suspected connections” submitted to the user is short : 11 small sets, all of which being inconsistent. Here they are, in abstract notation :

1. {F : SN_{q} = SN_{p}, F : SN_{q} < SN_{p}, F : SN_{p} < SN_{q}} ,
2. {T : SN_{p} < SN_{q}, T : SN_{q} < SN_{p}} ,
3. {F : q ∈ X_{p}, T : |X_{p}| = N} ,
4. {T : q ∈ {p}} ,
5. {T : SN_{q} < time, T : time = SN_{q}} ,
6. {T : |X_{q}| = 1, T : p ∈ X_{q}} ,
7. {F : time < time + 1} ,
8. {T : |X_{p}| = 1, T : q ∈ X_{p}} ,
9. {T : SN_{q} < time, T : time < SN_{q}} ,
10. {T : SN_{q} < time, T : SN_{q} = time} ,
11. {T : SN_{q} < time, F : SN_{q} < time + 1} .

In this favourable case, caveat succeeds in isolating exactly the (tiny) nonpropositional part of the verification work; in order to understand the program, the user has to know that all the eleven small lists of formulas given above are inconsistent.^{8}

3.4 Limitations of caveat

Within the restricted, but important subclass of programs caveat is intended to validate, two worrying limitations have been found. First, the gap of running time between the short version of the invariant, i.e.

I(p, q) ∧ I(q, p)

and the full version, i.e.

7 Nearly twenty hours are needed for the full version of the invariant, i.e.

I(p, q) ∧ I(q, p) ∧ I(p, s) ∧ I(r, q) ∧ I(s, p) ∧ I(q, r) ∧ I(r, s) .

8 Observe that, although the invariant is symmetric w.r.t. p and q, the code is not, and neither is the connection set.

I(p, q) ∧ I(q, p) ∧ I(p, s) ∧ I(r, q) ∧ I(s, p) ∧ I(q, r) ∧ I(r, s) ,

is clearly not acceptable (ratio is worse than 1 to 100), especially since the last five assertion groups of the full version are mostly trivial. This limitation prevents us for now to consider larger, more realistic systems. Techniques for decomposing invariants are currently investigated.

Second, caveat is not efficient for parametric systems whose parameter is not the number of processes. An example is Stenning’s “sliding window” protocol, where the parameter is the size of the window. The problem is that quantification elimination is more difficult in this case, and leads to longer propositional formulas.

3.5 A necessary extension

caveat is inspired by the classical idea that the best way to validate (the safety part of) the specifications of a concurrent system is to provide an appropriate invariant. However, as many designers are already reluctant to write specifications in a formal way, they are even less likely to be willing to also provide the invariant. Indeed, although the invariant is usually not more complex than the program code, it is more complex than the specification and not obvious to derive. The conclusion is that the construction of the invariant itself should be automated as much as possible.

The point of view adopted in [14, 16] is to view the system under study, say S_{n}, and its invariant I_{n}, as the last pair of a sequence ((S_{k}, I_{k}) : k ≤ n) of “specified systems”. Small transformation steps lead quite systematically from one version to the next, and the initial system S_{0} is very abstract, so the construction of its invariant I_{0} is usually easy. As can be seen in [16], the design/verification process is quite lengthy but much more time was devoted in verifying the “candidate-invariants” (by hand) than in their actual construction; this construction will be integrated in the next version of caveat. Note however that the construction process is not always amenable to automation. The exercise considered in [14] is probably a worst case in this respect. The extension to liveness and other temporal properties may be possible, using e.g. the technique reported in [15].

4 Related work

Several successful experiments have been made in combining model checking and theorem proving. In [19], an 8.2^{m}-bit multiplier is verified in this way. The principle is to verify the basic component of the multiplier, i.e., the 8-bit multiplier, by model-checking. Theorem proving (in temporal logic) is used to validate the recursive way in which four N-bit multipliers are combined to form a 2N-bit multiplier. This approach takes full benefits of the now powerful implementations of model-checking algorithms, but applies to a more restricted class of programs than ours. The reason is that many parametric systems (including Ricart and Agrawala’s algorithm) cannot be decomposed into non-parametric ones.

In this paper, we avoid this decomposition problem and consider the system to be verified as a whole. Simplification is performed on the verification conditions. As a result, we do not use model-checking, but tautology-checking.

Our approach is more similar to the approach reported in [25]. The system STeP uses model-checking whenever possible, and reverts to (temporal) theorem proving when model-checking fails. STeP does not rely on our incremental approach for obtaining invariants, but attempts to synthesize invariants directly from the program code. It also integrates various simplification methods, including two decision procedures for Presburger arithmetic (the first one is efficient, the second one is complete). STeP does not appear to achieve a full separation between the automatic part and the ATP-supported part, which is one of our main objectives. Indeed, in our opinion, this separation allows to reduce the ATP-part to short and elementary formulas, for which complicated ATP techniques are not really needed.

The incremental approach that is currently integrated in caveat is not the only way to transform concurrent systems, from higher-level to lower-level versions. Other approaches might be amenable to partial automation, for instance those refining atomicity with a reduction principle [2, 22], those using refinements and hierarchical design [24, 18] or phase decomposition [10, 28], and those based on property preserving abstractions [4, 12].

Symbolic model-checking and tautology-checking can be improved by using (ordered) binary decision diagrams [6]. This approach is followed in [7], and successfully applied to the verification of a simple synchronous pipeline. Besides, using Boolean automata can be more effective than using Boolean formulas, and this kind of approach is not restricted to investigating concurrent systems [17]. First experiments with (O)BDD in caveat have not been encouraging, however, since we lack an effective procedure for ordering atoms and pseudo-atoms. No experiment has been made yet in the area of digital circuits.

References

1. K.R. Apt and D.C. Kozen, Limits for Automatic Program Verification, Inform. Process. Letters 22 (1986) 307-309.

2. R.J. Back, A Method for Refining Atomicity in Parallel Algorithms, PARLE’89, Lect. Notes in Comput. Sci. 366 (1989) 199-216.

3. R.J. Back and R. Kurki-Suonio, Distributed co-operation with action systems, ACM Trans. Programming Languages Syst. 10 (1988) 513-554.

4. S. Bensalem et al., Property Preserving Abstractions for the Verification of Concurrent Systems, to appear in Formal Methods in System Design (1994).

5. W. Bibel, Deduction – Automated Logic, Academic Press, 1993.

6. R.E. Bryant, Graph-based algorithms for Boolean function manipulation, IEEE Trans. on Computers C-35 (1986) 677-691.

7. J.R. Burch et al., Symbolic Model Checking: 10^{20} States and Beyond, Proc. 5th. Symp. on Logic in Computer Science (1990) 428-439.

8. E. Clarke, E. Emerson and A. Sistla, Automatic Verification of Finite-State Concurrent Systems Using Temporal Logic Specifications, ACM Trans. Programming Languages Syst. 8 (1986) 244-263.

9. E.W. Dijkstra and al., On-the-Fly Garbage Collection: An Exercise in Cooperation, Comm. ACM 21 (1978) 966-975.

10. T. Elrad and N. Francez, Decomposition of Distributed Programs into Communication-closed Layers, Sci. Comput. Programming 2 (1982) 155-173.

11. D.M. Goldschlag, Mechanically Verifying Concurrent Programs with the Boyer-Moore Prover, IEEE Trans. on Software Eng. 16 (1990) 1005-1023.

12. S. Graf, Verification of a distributed Cache memory by using abstractions, Lect. Notes in Comput. Sci. 818 (1994) 207-219.

13. E.P. Gribomont, Synthesis of parallel programs invariants, TAPSOFT’85, Lect. Notes in Comput. Sci. 186 (1985) 325-338.

14. E.P. Gribomont, Stepwise refinement and concurrency : the finite-state case, Sci. Comput. Programming 14 (1990) 185-228.

15. E.P. Gribomont, Design, verification and documentation of concurrent systems, in Proc. 4th. Refinement workshop, J.M. Morris and R.C. Shaw (Eds), pp. 360-377, Springer-Verlag, 1991.

16. E.P. Gribomont, Concurrency without toil : a systematic method for parallel program design”. Sci. Comput. Programming 21 (1993) 1-56.

17. N. Halbwachs and F. Maraninchi, On the symbolic analysis of combinational loops in circuits and synchronous programs, REACT Report, 1994.

18. B. Jonsson, Compositional Specification and Verification of Distributed System, ACM Trans. Programming Languages Syst. 16 (1994) 259-303.

19. R.P. Kurshan and L. Lamport, Verification of a Multiplier : 64 Bits and Beyond, CAV’93, Lect. Notes in Comput. Sci. 697 (1993) 166-179.

20. R.P. Kurshan and M. McMillan, A structural induction theorem for processes, Proc. 8th ACM Symp. on Principles of Distributed Computing, Edmonton (1989).

21. L. Lamport, An Assertional Correctness Proof of a Distributed Algorithm, Sci. Comput. Programming 2 (1983) 175-206.

22. L. Lamport and F.B. Schneider, Pretending Atomicity, DEC SRC Rep. 44, May 1989.

23. R. Letz, J. Schumann, S. Bayerl and W. Bibel, SETHEO : A High-Performance Theorem Prover, Jl. of Automated Reasoning 8 (1992) 183-212.

24. N.A. Lynch and M.R. Tuttle, Hierarchical Correctness Proofs for Distributed Algorithms, Proc. 6th ACM Symp. on Principles of Distributed Computing, New-York (1987) 137-151.

25. Z. Manna et al., STeP : the Stanford Temporal Prover (Draft), June 1994.

26. J.S. Moore, Introduction to the OBDD algorithm for the ATP Community, Jl. of Automated Reasoning 12 (1994) 33-45.

27. G. Ricart and A.K. Agrawala, An optimal algorithm for mutual exclusion, Comm. ACM 24 (1981) 9-17 (corrigendum : Comm. ACM 24 (1981) 578).

28. F. Stomp and W.P. de Roever, A principle for sequential phased reasoning about distributed systems, Formal Aspects of Computing 6 (1994) 716-737.

29. M.Y. Vardi, P. Wolper, An Automata-Theoretic Approach To Automatic Program Verification, Proc. Symp. on Logic in Comput. Sci., Cambridge (1986) 322-331.

30. P. Wolper and V. Lovinfosse, Verifying Properties of large Sets of Processes with Network Invariants, CAV’89, Lect. Notes in Comput. Sci. 407 (1990) 68-80.

31. L. Wallen, Automated Deduction in Nonclassical Logics, MIT Press, 1990.