Wishnu Prasetya LTL Model Checking
Overview This pack : Abstract model of programs Temporal properties Verification (via model checking) algorithm Concurrency 2
Finite State Automaton/Machine Abstraction of a real program Choices What information do we want to put in the states? In the arrows? How to model execution? A path through the FSA, staring at an initial state. Additionally: Does it have to be finite? Do we need a concept of “acceptance” ? These choices influence what you can express, and how you can verify properties over executions
FSA Described by (S, I, , R) S : the set of possible states I S : set of possible initial states : set of labels, to decorate the arrows. They model possible actions. R : S pow(S), the arrows R(s,a) is the set of possible next-states of a if executed on s non-deterministic 4
Program compositions can be modeled by operations over FSA M 1 ; M 2 connect “terminal states” of M 1 to M 2 ’s initial states. M 1 || M 2 Parallel composition M 1 M 2 Only do executions that are possible in both 5
Example Actions are atomic; M 1 and M 2 has no common action. Their parallel composition is basically the full product over the component automata. To note: if each component has n number of states, constructing || over k components explode to n k states a b c
Parallel composition with synchronized actions Any action a 1 2 has to be executed together by both automata a a c
Intersection Not something you do in real programs A useful concept for verification later a a (0,0) (1,1) a c
Kripke Structure A finite automaton ( S, s 0, R, Prop, V ) S : the set of possible states, with s 0 the initial state. R : S pow(S), the arrows R(s) is the set of possible next-states from s non-deterministic Prop : set of atomic propositions abstractly modeling state properties. V : S pow(Prop), labeling function a V(s) means a holds in s, else it does not hold. No concept of accepting states. 9
Prop It consists of atomic propositions. We’ll require them to be non-contradictive. That is, for any subset Q of Prop : (/\Q) /\ (/\{ p | p Q}) is satisfiable. Else you may get inconsistent labeling. This is the case if they are independent of each other. Example: Prop = { x>0, y>0 } is ok. Prop = { x>0, x>1 } is not ok. E.g. the subset { x>1 } is inconsistent. 10
Example 11 With Prop = { isOdd x, x>0 } V(0) = { isOdd x } V (1) = { isOdd x, x>0 } So, which properties hold in state 0?
Execution An execution is a path through your automaton. Let’s focus on properties of infinite executions All executions are assumed infinite Extend each terminal state (states with no successor) in the original Krikpe with a stuttering loop. This induces an ‘abstract’ execution: Nat pow(Prop) infinite sequence of the set of propositions that hold along that path. We’ll often use the term execution and abstract execution interchangbly. 12
Example Exec. : Abs-exec: {isOdd x}, {isOdd x}, {isOdd x, x>0}, {isOdd x, x>0},... { isOdd x } { isOdd x, x>0 }
Run-time properties Hoare triple : express what should hold when the program terminates. Many programs are supposed to work continuously They should be “safe” They should not dead lock No process should starve Linear Temporal Logic (LTL) Originally designed by philosophers to study the way that time is used in natural language arguments Based on a number of operators to express relation over time: “next”, “always”, “eventually” Brought to Computer Science by Pnueli,
Informal meaning 15 f // always f X f // next f f U g // f holds until g f f f f f f f f f f g f
Quite expressive ( p (true U q )) // whenever p holds, eventually q will hold p U ( q U r) true U p // eventually stabilizing to p true U p // eventually p will hold infinitely many often 16
Syntax ::= p // atomic proposition from Prop | | /\ | X | U Derived operators: \/ = ( /\ ) = \/ , , W, … Interpreted over abstract executions. 17
Defining the meaning of temporal formulas First we’ll define the meaning wrt to a single abstract execution. Let be such an execution: ,i |== |== = ,0 |== If P is a Kripke structure, P |== means that holds on all abs. executions of P 18
Meaning Let be an (abstract) execution. ,i |== p= p (i)// p Prop ,i |== = not ( ,i |== ) ,i |== /\ = ,i |== and ,i |== 19
Meaning ,i |== X = ,i+1 |== ,i |== U = there is a j i such that ,j |== and for all h, i h<j, we have ,h |== . 20
Example 21 Consider : {isOdd x}, {isOdd x}, {isOdd x, x>0}, {isOdd x, x>0},... |== isOdd x U x>0 Is this a valid property of the FSA? { isOdd x } { isOdd x, x>0 }
Derived operators Eventualy = true U Always = Weak until W = \/ ( U ) Release R = W ( /\ ) 22
Some derived operators 23 <> f // eventually f f W g // weak until g R f // releases f f g,f ff f f f f g f f f f f f ffffff
Past operators Useful, e.g. to say: if P is doing something with x, then it must have asked a permission to do so. “previous” ,i |== Y = holds in the previous state “since” ,i |== S = held in the past, and since that to now holds Unfortunately, not supported by SPIN. 24
Ok, so how can I verify M |== ? We can’t directly check all executions infinite (in two dimensions). Try to prove it directly using the definitions? We’ll take a look another strategy… First, let’s view abstract executions as sentences. View M as a sentence-generator. Define: L(M) = { | is an abs-exec of M } these are sentences over pow(Prop) 25
Representing as an automaton … Let be the temporal formula we want to verify. Suppose we can construct automaton B that ‘accepts’ exactly those infinite sentences over pow(Prop) for which holds. So B is such that : L(B ) = { | |== } 26
Re-express as a language problem Well, M |== iff There is no L(M) where does not hold. In other words, there is no L(M) that will be accepted by L(B ). So: M |== iff L(M) L(B ) = 27
Automaton with “acceptance” So far, all paths are accepted. What if we only want to accept some of them? Add acceptance states. Accepted sentence: “aba” and “aa” is accepted “bb” is not accepted. But this is for finite sentences. For infinite...? 28 a b q1q1 q2q2 a b
Buchi Automaton “abab” not an infinite “ababab…” accepted “abbbb…” not accepted! Pass an acceptance state infinitely many times. Examples 29 a b q1q1 q2q2 a b
Expressing temporal formulas as Buchi We’ll take Prop = { p } {p}{p} {p}{p} Use pow(Prop) as the alphabet of arrow-labels. Example: Xp ( = X p) Indirectly saying that p is false. We can drop this, since we only need to (fully) cover accepted sentences. {p}{p} 30
To make the drawing less verbose Xp, using Prop = {p} * * Xp, using Prop = {p,q} * * pp Stands for all subsets of Prop that do not contain p; thus implying “p does not hold”. Stands for all subsets of Prop that contain p; thus implying “p holds”. pp So we have 4 subsets. pp
Always and Eventually 32 <>p pp * * []p pp <>[]p pp pp *
Until 33 p U q : pp * qq p U Xq : pp * qq *
Not Until 34 Formula: ( p U q ) p,q * pqpq Note first these properties: (p U q) = p /\ q W p /\ q (p W q) = p /\ q U p /\ q (also generally when p,q are LTL formulas) = q W p /\ q = q U p /\ q
Generalized Buchi Automaton 35 []<>p /\ []<>q 0 2 pp qq * 1 * * Sets of accepting states: F = { {1}, {2} } which is different than just F = { 1, 2 } in an ordinary Buchi. Every GBA can be converted to BA.
Difficult cases How about nested formulas like: (Xp) U q ( p U q ) U r Their Buchi is not trivial to construct. Still, any LTL formula can be converted to a Buchi. SPIN implements an automated conversion algorithm; unfortunately it is quite complicated. 36
Check list 1.How to construct B ? Buchi 2.We still have a mismatch, because M is a Kripke structure! Fortunately, we can easily convert it to a Buchi. 3.We still have to construct the intersection. 4.We still to figure out a way to check emptiness. M |== iff L(M) L(B ) = 37
Label on state or label on arrow a a b b c c a b c b c b b d d e e d e c c generate the same sentences
Converting Kripke to Buchi { isOdd x } { isOdd x, x>0 } z { isOdd x } { isOdd x, x>0 } Generate the same inf. sentences! Single accepting set F, containing all states { isOdd x } { isOdd x, x>0 }
Computing intersection Rather than directly checking: L(B M ) L(B ) = We check: L(B M B ) = The Buchi version of Kripke M We already discuss this! Execution over such an intersection is also called a “lock-step” execution. 40
Intersection Two buchi automata A and B over the same alphabet The set of states are respectively S A and S B. starting at respectively s0 A and s0 B. Single accepting set, respectively F A and F B. F A is assumed to be S A A B can be thought as defining lock-step execution of both: The states : S A S B, starting at (s0 A,s0 B ) Can make a transition only if A and B can both make the corresponding transition. A single acceptance set F; (s,t) is accepting if t F B. 41
Constructing Intersection, example 0 1 { p } { p,q } p : isOdd x q : x>0 AP:AP: A <>q : a qq 42 (0,a) { p } A p A <>q : (1,a) { p }
Verification Sufficient to have an algorithm to check if L(C) = , for the intersection-automaton C. So, it comes down to a cycle finding in a finite graph! Solvable. The path leading to such a cycle also acts as your counter example! L(C) iff there is a finite path from C’s initial state to an accepting state f, followed by a cycle back to f. 43
Approaches View C = A P A as a directed graph. Approach 1 : 1.Calculate all strongly connected component (SCCs) of C (e.g. with Tarjan). 2.Check if there is an SCC containing an accepting state, reachable from C’s initial state. Approach 2, based on Depth First Search (DFS); can be made lazy : the full graph is constructed as-we-go, as you search for a cycle. If M = M 1 || M 2 ||..., this means that we can lazily construct M. 44
DFS-approach (SPIN) DFS is a way to traverse a graph : This will visit all reachable nodes. You can already use this to check assertions. DFS(u) { if (u visited) return ; visited.add(u) ; for (s next(u)) DFS(s) ; } 45
Example
Adding cycle detection DFS(u) { if (u visited) return ; visited.add(u) ; for each (s next(u)) { if (u accept) { visited2 = ; checkCycle (u,s) ; } DFS(s ) ; } 47
checkCycle is another DFS checkCycle(root,s) { if (s = root) throw CycleFound ; if ( s visited2 ) return ; visited2.add(s) ; for each (t next(s)) checkCycle(root, t) ; } 48 Can be extended to keep track of the path leading to the cycle counter example. See Lecture Notes.
Example checkCycle(1,2) 49 the node currently being processed root
Tweak: lazy model checking Remember that automaton to explore is C = B M B In particular, B M can huge if M = M 1 || M 2 ||... Can we construct C lazily? Benefit : if a cycle is found (verification fails), effort is not wasted to first construct the full C. Of course if turns out to be valid, then C will in the end fully constructed. Question: don’t we get this for free in e.g. Haskell? 50
Lazily constructing the intersection Assume first that M is just a single process. Only need to change this : for each (s next(u)) …. if (u accept) Each state of C is of type S M S . (s 1,s 2 ) next C (u 1,u 2 ) ( lab:: s 1 next M (u 1,lab) /\ s 2 next (u 2,lab)) (u 1,u 2 ) accept C u 2 accept 51
Dealing with concrete states A concrete state: a vector of values of prog. variables Recall: (s 1,s 2 ) next C (u 1,u 2 ) ( lab:: s 1 next M (u 1,lab) /\ s 2 next (u 2,lab)) Suppose u 1 is a concrete state, it means we are checking: if there is an (atomic) action of M that can be executed on u 1 leading to s 1 and all propositions in lab hold in u 1 52
What if M = M 1 || M 2 ||... ? Parallel composition was define over structures like this: o o || o o Parallel composition of Kripke structures or Buchi’s as we defined them does not really making sense. E.g., what does this mean? {p} {p} || {q} 53
What if M = M 1 || M 2 ||... ? Recall: (s 1,s 2 ) next C (u 1,u 2 ) ( lab:: s 1 next M (u 1,lab) /\ s 2 next (u 2,lab)) Suppose u 1 is a concrete state of M 1 || M 2 ||... Check: if there is an atomic action of some M k that can be executed on u 1 leading to s 1 and all propositions in lab hold in u 1 54
Fairness Consider this concurrent system : Is it possible that print x is ignored forever? We may want to assume some concept of fairness; you can think of some possibilities. Importantly, it has to be reasonable. Weak fairness : any action that is persistently enabled will eventually be executed. Strong fairness : any action that is kept recurrently enabled (but not necessarily persistently enabled) will eventually be executed. Imposing fairness mean: when verifying M |== , we only need to verify wrt fair executions of M. A fair execution : an execution respecting the assumed fairness condition. P { while true do x:=(x+1)%3 } Q { (x==0) ; print x } || 55 execution blocks if false
Verifying properties under fairness We don’t have anything in the FSA itself to express fairness. But we can express it with LTL. E.g. weak fairness on the red arrows: ((p q) (p q)) ((p q) (p q)) Strong fairness on the blue arrow: ( (p q) ( p q)) To verify a given property , we now verify fair , where fair is the LTL expressing your fairness assumption { p } { p,q} 2 2
Conclusion We can use FSAs to abstractly model concurrent programs. We can use LTL to express run-time properties: safety and progress. The model checking algorithm is thorough. Rather than FSAs with atomic predicates, you can imagine letting the FSAs to have concrete states. You can then model check real programs. The FSAs could be very large, but we can bound the input domains, and the depth of the search, bounded model checking. Combination with testing: to construct an execution so that M behaves as , model-check this: L(B M B ) = 57