490 likes | 688 Views
Hoare Logic and Predicate Transformers. Wishnu Prasetya wishnu@cs.uu.nl www.cs.uu.nl/docs/vakken/pv. Plan. Specification Some basic inference rules Dealing with loop Predicate transformers GCL WLP transformer for GCL WLP of loops. Specifying terminating programs.
E N D
Hoare Logic and Predicate Transformers Wishnu Prasetya wishnu@cs.uu.nl www.cs.uu.nl/docs/vakken/pv
Plan • Specification • Some basic inference rules • Dealing with loop • Predicate transformers • GCL • WLP transformer for GCL • WLP of loops
Specifying terminating programs • A popular way, using “Hoare triples” : • partial correctness or total correctness interpretation. { x>0 } Pr(x) { return = x+1 } { #S>0 } Pr(S) { return S /\ (x: xS : return≥x) }
Proving the validity of a specification • Hoare logic provides a set of inference rules, such as the one below : • So, what does the rule say? • Note : LN uses the notation |- .OP { P } S { Q } , OP------------------------------------------------ { O } S { Q }
‘Let’s see few more rules { P } S1 { Q } , { Q } S2 { R } --------------------------------------------------------- { P } S1 ; S2 { R } { P /\ g } S1 { Q } , { P /\ g } S2 { Q } ---------------------------------------------------------- { P } ifgthenS1 else S2{ Q }
Assignment • How to prove the correctness of an assignment? • Alternatively, because pre-cond can be weakened: PQ[e/x]--------------------------------------- { P } x:=e { Q } ---------------------------------------- { Q[e/x] } x:=e { Q }
Loop • A loop is correct if you can find an “invariant” : • E.g. a trivial loop: P I { g /\ I } S { I } I /\ g Q ---------------------------------------- { P } whilegdo S { Q } { i≥0 } whilei>0 doi := i-1 { i=0 }
Loop, few more examples { i=2 /\ N0} whilei<N do { i:= i*2 } { even(i) /\ iN } { true } i:=0; whilei<#a do { a[i] := 3 ; i++ } { all elements of a is 3 } To note: invariant can be used as abstraction!
Loop, few more examples • A program to check if an array b contains ‘true’ : { true } i = 0 ; found = false ; whilei<#b /\ founddo { found=b[i] ; i=i+1} { found = ( j: 0≤j<#b: b[j]) }
Rule for proving termination (of loop) • Extend the previous rule to:P I { g /\ I } S { I } I /\ g Q { I /\ g } C:=m ; S { m<C } // m decreasing I /\ g m> 0 // m bounded below ---------------------------------------- { P } whilegdo S { Q }
Examples { i=0 /\ N0} whilei<N do { i = i*2 } { even(i) /\ iN } { x0 /\ y0 } whilex+y>0 do{ if random() { x := x-1 ; y := y+2 } else y := y-1 } { true}
Hoare logic cannot be directly automated • Problem: • We’ll switch to a predicate transformer-based verification. It is a function of type : Statement Predicate Predicate { P } S1 { Q } , { Q } S2 { R } --------------------------------------------------------- { P } S1 ; S2 { R }
Forward and backward transformer • Forward : transform a pre-condition to a post-condition. Similarly: Backward • Sound transformer, if : { P } S { cp S P } { cp S Q } S { Q } • Sound and complete if : { P } S { Q } cp S P Q { P } S { Q } P cp S P
WP and WLP transformer • wpS Q : the weakest pre-condtion so that S terminates in Q. • wlp S Q : the weakest pre-condition so that S, if it terminates, will terminate in Q.wlp = weakest liberal pre-conidtion. • Though, we will see later, that we may have to drop the completeness property of wp/wlp,but we will still call them wp/wlp.
Guarded Command Language (GCL) • Simple language to start • Expressive enough to encode much larger languages • So that you can keep your logic-core simple • Constructs • skip , assignment, seq, while • raise , S ! H • S T , if-then-else can be encoded • var x in S end , uninitialized local-var. • program decl, program call • primitive types, arrays • asserte , assumee
wlp • wlp skip Q = Q • wlp (x:=e) Q = Q[e/x] • wlp (S1 ; S2) Q = wlp S1 (wlp S2 Q)We don’t need to propose our own intermediate predicate! • Example, prove:{ xy }tmp:= x ; x:=y ; y:=tmp { xy }
GCL’s exotic constructs • wlp (asserte) Q = e /\ Q • wlp (assumee) Q = eQ • wlp (S T) Q = (wlp SQ) /\ (wlpT Q) • If-then-else can be expressed as:ifgthen S1else S2 (assumeg ; S1 ) (assume g ; S2 )
wlp of ite • wlp (ifgthen S1else S2) Q = (g wlpS1Q) /\ (g wlpS2Q) • Note that this is equivalent to : (g /\wlpS1Q) \/ (g /\ wlpS2Q)
Dealing with loop • Unfortunately, no general way to calculate wlp of loops. • For annotated loop:wlp (inv I while g do S) Q = I , provided • I /\ g Q • I /\ g wlp S Q • Otherwise the wlpis g /\ Q • May not return the weakest pre-cond! Losing completeness, but still sound.
What if we have no annotation? • Note this first: whilegdo S if g then { S ; whilegdo S } else skip • let W be the wlp of the loop wrtQ. Then : W = (g /\ wlp S W) \/ (g /\ Q) • We are looking for the “weakest” solution of the above equation.
Detour.... • We’ll discuss two more approaches to deal with loops • Fix point based • Finite unrolling • For the first, we need some background in • Fix point theory • Set-interpretation of predicates; we can just as well present set-semantic of programs and specifications
set semantics of programs and specifications • We will keep it simple, just to facilitate certain concepts needed later. • But you can also use it as a starting point for more a advanced semantic. • What is a program state? Can we abstractly represent it? • set of VariableNameValue • VariableName Value • Record, e.g. { x=0 , y=9 } • Let denotes the space of all possible states of the program we are talking about.
Expression and predicate • An expression is a function val • A (state) predicate is a function boole.g. x>y is modeled by (s. s.x > s.y) • Note that in terms of this semantic, operations on predicates e.g. P /\ Q , P Q, etc operate thus on functions.|- P (the predicate P is valid), would then mean: (s: s : P s)
Predicate and set equivalence • Let P : boolbe a predicate. It describes this set:SP= { s | s /\ Ps } • Conversely, if you know the set, you can reconstruct the original P : (s. s SP ) • From this perspective, we can treat predicates as if they are sets. • Predicate operators translate to set operators, e.g. /\, \/ to , negation to complement wrt . • This ordering : |- P Q translates to P Q
Model of a deterministic program • Model a program by a function :T : pow()such that for any state s, T s gives us the set of all possible end-states if T is executed in s. • This model assumes T terminates.
Set-semantic of specification • Now we have enough to define what a Hoare-triple specification means: { P } Stmt { Q } = (s:: P s (t : tStmt s Q t))We assume S terminates (so, we use the partial correctness interpretation of Hoare triple).
Semantic of SEQ • If f : A pow(A), we lift it to pow(A) pow(A), overloading :f V = { f x | xV } • Semantic of “;” (S1 ; S2) s = (S2 . S1) {s} • Now you can prove the inference rule : { P } S1 { Q } , { Q } S2 { R } ------------------------------------------------------- { P } S1 ; S2 { R }
Some bit of fix point theory • (A,) where is a p.o., is a complete lattice, if every subset X A has a supremum and infimum. • Supremum = least upper bound = join \/ X X • Infimum = greatest lower bound = meet /\ X X • So, we will also have the supremum and infimum of the whole A, often called top and bottom. • Let f : AA. An x such that x = f(x) is a fix point of f. • Knaster-Tarski. If f : AA is monotonic over , and P is the set of all its fix points, then P is non-empty, and (P, ) is a complete lattice. • (thus, both P and P are also in P).
Some bit of fix point theory • The domain (pow(A), ) is a complete lattice. • So, what can we say aboutPred = bool ? • Recall this : W = (g /\ wlp S W) \/ (g /\ Q)We are looking for the “weakest” fix point. “Weakest” in the ordering corresponds to “greatest” in the ordering. So, we are looking for the greatest FP, over the domain (Pred,). • Is wlp monotonic ?
Some bit of fix point theory • Consider now f : pow(A) pow(A). It is -continuous if for all decreasing chain X0, X1, ... (over ) : f ( X0 X1 ... ) = f(X0) f(X1) ... • Similarly, -continuous • If f is -continuous, it is also monotonic. • Is wlp -continuous ?
FP iteration • f 0 X = X • f k+1 X = f ( f k X ) • If f is -continuous, its greatest FP is { f k (A) | k0 }Since f is also monotonic, note that f k (A) f k+1 (A) (by induction) • If f is -continuous, its least FP is { f k () | k0 }
FP iteration • X := A ;while X f(X) doX := f(X) • Will give the greatest FP, if it terminates. • For wlp : • initialize with X := true • f (W) = (g /\ wlp S W) \/ (g /\ Q)
Example 1 • Let’s try Q : y=0 • W0 = true • W1 = (y>0 /\ wlp S W0) \/ ((y>0) /\ y=0) = (y>0 /\ true) \/ (y=0) = y0 • W2 = (y>0 /\ y-10) \/ ((y>0) /\ y=0) = y 1 \/ y=0 = y0 • W2 = W1 while y>0 do { y := y−1 }
Example 2 while y>0 do { assert c ; x := x+y ; y := y−1 ; } • Let’s try Q : d -- c,d are boolvars • W0 = true • W1 = (y>0 /\ wlp S W0) \/ ((y>0) /\ d) = (y>0 /\ c) \/ (y0 /\ d) • W2 = (y>0 /\ wlp S W1) \/ (y0 /\ d)= (y>1 /\ c) \/ (y=1 /\ c /\ d) \/ (y0 /\ d) • W3 = (y>2 /\ c) \/ (y=2 /\ c /\ d) \/ (y=1 /\ c /\ d) \/ (y0 /\ d) • does not terminate
Finite unfolding • Define[while]0 (g,S) = assertg[while]k+1 (g,S) = ifg then { S ; [while]k (g,S) }else skipwhile0 (g,S) = assume gwhile k+1 (g,S) = ifg then { S ; whilek (g,S) }else skip • Iterate at most k-times. • Iterate at most k-1 times, then miracle.
Replacing while with [while]k • while y>0 do { y := y−1 } { y=0 } • wlp ([while]2 (y>0 , y := y−1)) (y=0) = y=2 \/ y=1 \/ y=0 • Sound, but incomplete. Given { P } loop { y=0 } to verify, now we have to prove that P implies the above wlp. • Won’t work if P allows y>2. • Blind to unaffected fragments (see blue) /\ z=0 ( ) /\ z=0
Replacing while with whilek • wlp (while1 (y>0 , y := y−1)) (y=0) = y>0 \/ y=0 • Given { y0 } loop { y=0 } to verify, now we have to prove that P implies the above wlp. Success! • The wlp is complete but unsound. • Can still handle unaffected fragments, wrt all iterations. /\ z=0 if y>0 then { y := y-1 ; assume(y>0) }else skip ( ) /\ z=0
In pictures... imagine, to verify {P} whilegdo S {Q} > k iterations k iterations wlp [while]k Q P P wlpwhilek k iterations < k iterations But there are also those “unaffected fragments” which are not captured in these pictures.
How about verification of OO programs? • GCL is not OO, but we can encode OO constructs in it. We’ll need few additional ingredients : • local variables • stimulant assignment • program call • array • object • method
Local variable • wlp(varx in x:=1 ; y := y+xend) (x=y) Rename loc-vars to fresh-vars, to avoid captures:wlp(varx’ in x’:=1 ; y := y+x’ end) (x=y) • Let’s try another example :wlp(varx’ inassume x’>0 ; y := y+x’ end) (x=y)
Simultant assignment • For example: x,y := y , x+y { x=y }wlp (x,y := e1,e2) Q = Q[e1,e2 / x,y] • Compare it with : (x=y) [y/x] [x+y/y]
Program and program call • Syntax: Pr(x,y | r1 , r2) body • x,yare input parameters passed by value • r1 , r2 are output parameters, acting as multiple return values. • Syntax of program call : • Pr(e1,e2), not very useful in GCL • a,b := Pr(e1,e2) • Example : inc(x | r) { r := x+1 }
Encoding program call • GCL does not have program call, but it can be encoded:a,b := Pr(e1,e2) body var x,y,r1,r2inx,y := e1,e2 ; body ; a,b := r1,r2endRename to make the loc-vars fresh. • example: wlp (x := inc(x)) (x>1)
Black-box call • What if we don’t know the body? Assuming you still have the specification, e.g. : { x>0 } inc(x | r) { r x+1 }We treat this as having this body :inc(x | r) { assert x>0 ; assume r x+1 } • example: wlp (x := inc(x)) (x>1)
Arrays • Arrays in GCL are infinite. • Introduce the notation a(i repby e) to denote a clone of the array a, but differs at i-th element, which now has the value e. • Treat a[i] := e as a := a(i repby e) • Example :wlp (a[i] := 0) (a[i] = a[3]) • Encode a[i] := 0 in an RL language e.g. as :assert 0 i < #a ; a[i] := 0
Objects • Introduce ‘heap’ H : [int][fieldname] value • Currently existing objects are stored in H[0] ... H[N-1] • Two types of values : primitive values, or reference to another object. • Encoding : • x := o , unchanged, but note o is thus an int • o.fn := 3 is encoded by H[o][fn] := 3 • Suppose C has a single int-field named a x = new C() is encoded by { H[N][a] := 0 ; x:=N ; N++ }
Methods • Let m(x|r) be a method of class C. It implicitly has the instance-object and the whole heap as input and output parameter. • Example: class C { a:int inc(d) { this.a := this.a+d ; } }The method is translated to:inc(this,H,d | H’) { H[this][a] := H[this][a] + d ; H’ := H } • call o.inc(3) is translated to ... ?
How about concurrency? • Consider x := x+y in a method m • If m has exclusive access to x,y ; wlp as usual. • There may be other threads accessing x,y, but the whole assignment can be done atomically. A possible approach is propose a system invariant J over these x,y, and require that every thread maintains it :assumeJ(x,y) ; x:=x+y ; assertJ(x,y)Additionally all assignments to x or y must assert J as well! • Can be just good enough to prove safety; but not good enough to prove progress/liveness. • If the assignment is not atomic .... • Similarly for guards if ite and loop...
Summary • You have now (almost) the full wlp-based logic to verify GCL programs. • Pending: exception. • The calculation of wlp is syntax driven. • If loops are annotated, the calculation of wlp is fully automatic, else you may have to do some trade off. • RL languages can be translated to GCD; we have shown how some core OO constructs can be translated.