Hoare logic

Hoare Logic
LN chapter 5, 6 but without 6.8, 6.12, 6.13 (to be
discussed later)
Hoare Logic is used to reason about the correctness of programs. In the
end, it reduces a program and its specification to a set of verifications
conditions.
Overview

Part I
 Hoare triple
 Rules for basic statements
 Weakest pre-condition
 Example

Part II : loop

Logic for unstructured programs
// SEQ, IF, ASG
2
Part 1 : reasoning about basic statements
3
Hoare triple







A simple way to specify the relation between a program’s
end and initial states.
{* y0 *} program(x,y) {* return = x/y *}
{* P *} statement {* Q *}
P  pre-condition; Q  post-condition.
Total correctness : if the program is executed on a state
satisfying P, it will terminate in a state satisfying Q.
Partial correctness : if the program is executed on a state
satisfying P, and if it terminates, it will terminate in a state
satisfying Q.
There are other kinds of correctness-related properties
which are difficult to express in Hoare triples.
4
Difficult to capture in classical Hoare triples

When specifying the order of certain actions within a
program is important:
 E.g. CSP

When sequences of observable states through out the
execution have to satisfy certain property:
 E.g. Temporal logic

When the environment cannot be fully trusted:
 E.g. Logic of belief
5
Rule for SEQ composition
{* P *} S1 {* Q *} , {* Q *} S2 {* R *}
----------------------------------------------------------------{* P *} S1 ; S2 {* R *}
6
Rule for IF
{* P /\ g *} S1 {* Q *}
P
S1
g
Q
g
S2
{* P /\ g *} S2 {* Q *}
{* P /\ g *} S1 {* Q *} , {* P /\ g *} S2 {* Q *}
-------------------------------------------------------------------------{* P *} if g then S1 else S2 {* Q *}
7
Rule for Assignment
??
-------------------------------{* P *} x:=e {* Q *}


Find a pre-condition W, that holds before the assignment, if
an only if Q holds after the assignment.
Then we can equivalently prove P  W
8
Assignment, examples

{* 10 = y *}
x:=10
{* x=y *}

{* x+a = y *}
x:=x+a
{* x=y *}

So, W can be obtained by Q[e/x]
9
Assignment

Theorem:
Q holds after x:=e iff Q[e/x] holds before the
assignment.

The “proof rule” :
{* P *} x:=e {* Q *}
=
P  Q[e/x]
10
How does a proof proceed now ?

{* xy *}

Rule for SEQ requires you to come up with intermediate
assertions:
{* xy *}

tmp:= x ; x:=y ; y:=tmp {* xy *}
tmp:= x {* ? *} ; x:=y {* ? *} ; y:=tmp {* xy *}
What to fill ??
11
Weakest Pre-condition (wp)

Imagine we have this function:
wp : Stmt  Pred  Pred
such that wp S Q gives a valid pre-cond: executing S in any
state in any state in this pre-cond results in states in Q.
 Partial correctness  S is assumed to terminate
 Total correctness  the weakest pre-condition must also
guarantees that S terminates.

Even better: let wp construct the weakest valid pre-cond.
12
Weaker and stronger




If p  q is valid, we say that p is stronger than q
Conversely, q is weaker than p.
A state-predicate p can also be seen as characterizing a set
of states, namely those states on which p is true.
“p is stronger than q” is the same as saying, in terms of set,
that “p is subset of q“.
13
Weakest pre-condition

We can “characterize” wp, as follows:
{* P *} S {* Q *}


P  wp S Q
Does this define a valid pre-cond? Yes, you can prove:
{* wp S Q *}


S
{* Q *}
Corollary: this reduces program verification problems to
proving implications!
Corollary: the reduction is complete. That is, if the
implication above is not valid, so is the specification.
14
Weakest pre-condition

But the previous def. is not a constructive one: we still don’t
know how to construct this wp
 Possible for assignments, if-then-else, ...
 Not possible if the code of S is not known
 Not possible for loops and recursions
wp skip Q
=
Q
wp (x:=e) Q
=
Q[e/x]
15
wp of SEQ
wp (S1 ; S2) Q
V
=
wp S1 (wp S2 Q)
S1
W
Q
S2
= wp S1 W
= wp S2 Q
16
wp of IF
wp (if g then S1 else S2) Q
Other formulation :
=
(g /\ wp S1 Q) \/ (g /\ wp S2 Q)
= wp S1 Q
S1
V
(g  wp S1 Q)
/\
(g  wp S2 Q)
Q
W
g
g
S2
= wp S2 Q
17
How does a proof proceed now ?

1.
{* xy *}
Calculate:
W
2.

tmp:= x ; x:=y ; y:=tmp {* xy *}
=
Then prove:
wp (tmp:= x ; x:=y ; y:=tmp)
xy
xy  W
We calculate the intermediate assertions, rather than
figuring them out by hand!
18
Example
{* 0i /\ ¬found /\ (found = (k : 0k<i : a[k]=x)) *}

found := a[i]=x ;
(a[i]=x)
= (k : 0k<i+1 : a[k]=x)
i:=i+1
found = (k : 0k<i+1 : a[k]=x)
{* found = (k : 0k<i : a[k]=x) *}
wp (x:=e) Q = Q[e/x]
19
Some notes about the verification approach


In our training, we prove P  W by hands.
In practice, to some degree this can be automatically
checked using e.g. a SAT solver. It checks if:
(P  W) is satisfiable
If it is, then P  W is not valid.

If the calculation of W was “complete”, the witness of the
satisfiability of (P  W) is essentially an input that will
expose a bug in the program  useful for debugging.
20
Can we prove the correctness of our inference rules?

First, give reasonable “models” of the concepts involved.
 What is a statement ?
type Stmt = State  State
but with this we can only model deterministic programs.
How about:
type Stmt = State  set of State
such that stmt s describes the set of all its possible final
states.
21
Meta

What are “;” and “if-then” ?
(if g then S1 else S2 ) s
(S1 ; S2 ) s

=
= if g s then S1 s else S2 s
 {S2 t | t  S1 s }
What is a pre-/post condition .e.g x0
we can model it by a function of type State  bool

What is Hoare triple? (partial correctness)
{* P *} S {* Q *}
= (s: P s : ( t : t S s : Q t))
22
Example, proving rules

Post-condition weakening rule :
{* P *} S {* Q *} , Q  R
-------------------------------------------{* P *} S {* R *}

[A1:] (s: P s : ( t : t S s : Q s))
[A2:] (s:: Q s  R s )
[G:] (s: P s : ( t : t S s : R s))

Pretty straight forward...


23
Btw, few more rules

Pre-condition strengthening:
{* Q *} S {* R *} , P  Q
-------------------------------------------{* P *} S {* R *}

Conjunction and disjunction
{* P1 *} S {* Q1 *} , {* P2 *} S {* Q2 *}
-------------------------------------------------------------------------{* P1 /\ P2 *} S1 {* Q1 /\ Q2 *}
{* P1 \/ P2 *} S1 {* Q1 \/ Q2 *}
24
Part II : reasoning about loops
25
How to prove this ?

{* P *} while g do S {* Q *}

Calculate wp first ?
 Unfortunately, for loop we can’t.
 Idea : give me a predicate that S will always establish at
the end of every iteration.
26
Idea

{* P *} while g do S {* Q *}

Try to come up with a predicate I that holds at the end of
every iteration.
iter1 :
iter2 :
…
itern :
exit :

// g // ; S
// g // ; S
{* I *}
{* I *}
// g // ; S
// g //
{* I *}
I /\ g holds as the loop exit!
// last iteration!
So, to establish postcond Q,
sufficient to prove:
I /\ g  Q
27
Ok, what kind of predicate is that??

while g do S

I has to holds at the end of each iteration
Sufficient to prove: {* I /\ g *} S {* I *}
… S {* I *}
iter i
// g // S {* I *}
iter i+1
Except for the first iteration !
28
Idea

{* P *}
while g do S

For the first iteration :
Additionally we need : P  I
Recall the condition: {* I /\ g *} S {* I *}
{* P *}
We know this from
the given pre-cond
{* I *}
// g // S {* I *}
Iter1
To Summarize

Capture this in an inference rule:
PI
// setting up I
{* g /\ I *} S {* I *}
// invariance
I /\ g  Q
// exit cond
---------------------------------------{* P *} while g do S {* Q *}


This rule is only good for partial correctness though.
an “I” satisfying the second premise above is called
invariant.
30
Few things to note

An I satisfying the 2nd and 3rd conditions of the previous rule
is always a valid pre-condition of the loop, towards the postcond Q. That is, we also have this rule:
{* g /\ I *} S {* I *}
I /\ g  Q
---------------------------------------{* I *} while g do S {* Q *}



Indeed, I is not necessarily the weakest one.
The previous rule can just as well be obtained from the
above rule + pre-condition weakening.
Nothing prevent us from chaining I into wp calculation,
although the resulting predicate may then not be the
weakest pre-cond.
31
Examples

Prove (partial correctness) :
{* true *} while in do i++

{* i=n *}
Prove:
{* i=0 /\ n=10 *}
while i<n do i++
{* i=n *}
32
Examples

Prove:
{* i=0 /\ s=0 *}
while i<n do { s = s+2 ; i++ }
{* isEven(s) *}

Prove (will return to this later) :
{* 0n *}
i := 0 ; r := true ;
while i<n do { r := r /\ (a[i]=0) ; i++ }
{* r = (k : 0k<n : a[k]=0) *}
33
Proving termination

{* P *} while g do S {* Q *}

Idea: come up with an integer expression m, satisfying :

1.
At the start of every iteration m > 0
2.
Each iteration decreases m
These imply that the loop will terminates.
34
Capturing the termination conditions


At the start of every iteration m > 0 :

g  m0

If you have an invariant: I /\ g  m > 0
Each iteration decreases m :
{* I /\ g *}
C:=m; S {* m<C *}
35
To Summarize


PI
{* g /\ I *} S {* I *}
I /\ g  Q
{* I /\ g *} C:=m; S {* m<C *}
I /\ g  m > 0
---------------------------------------{* P *} while g do S {* Q *}
// setting up I
// invariance
// exit cond
// m decreasing
// m bounded below
Since we also have this pre-cond strengthening rule:
P  I , {* I *} while g do S {*Q*}
-----------------------------------------------------{* P *} while g do S {* Q *}
36
Lec notes often refer to this rule

{* g /\ I *} S {* I *}
I /\ g  Q
{* I /\ g *} C:=m; S {* m<C *}
I /\ g  m > 0
---------------------------------------{* I *} while g do S {* Q *}
// invariance
// exit cond
// m decreasing
// m bounded below
37
Examples

{* in *} while i<n do i++
{* true *}

{* true *} while in do i++
{* true *}

{* reds=100 /\ blues=100 }
while reds>0 \/ blues>0 do {
if reds>0 then { reds-- ; blues += 2 }
else { blues-- }
}
{* true *}
38
Structuring the proof(s)

Recall this example:
{* 0n *}
i := 0 ; r := true ;
while i<n do { r := r /\ (a[i]=0) ; i++ }
{* r = (k : 0k<n : a[k]=0) *}

Chosen invariant & termination metric:
I : (r = (k : 0k<i : a[k]=0)) /\ 0in
m: n-i
I1
I2
39
It comes down to proving these


(Exit Condition) I /\ g  Q
(Initialization Condition)
{* given-pre-cond *} i := 0 ; r := true {* I *},
Equivalently : given-pre-cond  wp (i := 0 ; r := true) I



(Invariance) I /\ g  wp body I
(Termination Condition) I /\ g  wp (C:=m ; body) (m<C)
(Termination Condition) I /\ g  m>0
40
Top level structure of the proofs 1

PROOF PEC
[A1] r = (k : 0k<i : a[k]=0)
[A2] 0in
[A3] in
[G] r = (k : 0k<n : a[k]=0)
.... (the proof itself)

PROOF PInit
[A1] 0n
[ G ] (true = (k : 0k<0 : a[k]=0)) /\ 00n // calculated wp
41
Proof of init

PROOF PInit
[A1] 0n
[ G ] (true = (k : 0k<0 : a[k]=0)) /\ 00n // calculated wp
----------1. { follows from A1 } 00n
2. { see subproof } true = (k : 0k<0 : a[k]=0)
Equational proof
(k : 0k<0 : a[k]=0)
= { the domain is false }
(k : false : a[k]=0))
= { over empty domain }
true
end
3. { conjunction of 1 and 2 } G
end
42
Top level structure of the proofs 2

PROOF PIC
[A1] r = (k : 0k<i : a[k]=0)
[A2] 0in
[A3] i<n
[G1] r/\(a[i]=0) = (k : 0k<i+1 : a[k]=0)
[G2] 0i+1n
---------------------------------------------------------------------1. { see eq. subproof below } G1
EQUATIONAL PROOF ---------------------------(k : 0k<i+1 : a[k]=0)
= { dom. merge , PIC.A2 }
(k : 0k<i \/ k=i : a[k]=0)
= {  domain-split }
(k : 0k<i: a[k]=0) /\ (k : k=i : a[k]=0)
= { PIC.A1 }
r /\ (k : k=i : a[k]=0)
= { quant. over singleton }
r /\ (a[i]=0)
END ------------------------------------------------------2. ...
....
END
43
Top level structure of the proofs 3

PROOF TC1
[A1] r = (k : 0k<i : a[k]=0)
[A2] 0in
[A3] in
[G] n - (i+1) < n-1
// calculated wp

PROOF TC2
[A1] r = (k : 0k<i : a[k]=0)
[A2] 0in
[A3] in
[G] n - i > 0
44
Reducing a program spec to a statement spec

{* x > 0 *}

Things to take into account :
 How are parameters passed? in uPL  by-value and bycopy-restore
 only return at the end is allowed in uPL  its value is
represented by an assignment to a dummy variable
“return”
The above reduces to:

P(x:int) { x++ ; return ... } {* return = x+1 *}
{* x > 0 *} X:= x ; x++ ; return := ... {* return = X+1 *}
We introduce an auxiliary variable X to represent x’s original
value.
45
Reducing a program to its body

How about this :
{* y-1 *}
P(x, OUT y) { y++ ; x++ ; return x/y } {* return = (x+1)/y *}


It makes sense to agree that y in the post-cond refers to its
new value, whereas x to its old value.
Reduce to:
{* y-1 *} X:=x; y++; x++ ; return :=x/y {* return = (X+1)/y *}
46
Unstructured programs

“Structured” program: the control flow follows the program’s
syntax.

Unstructured program:
red:


if y=0 then goto red ;
x := x/y ;
S2
The “standard” Hoare logic rule for sequential composition is
broken!
Exceptions and “return” in the middle create a similar issue.
47
Adjusting Hoare Logic for Unstructured Programs
Program S : represented by a graph of guarded assignments
x<n  x++
0xn
2
0
x=n  skip
3
1. Node represents “control location”
2. Edge is an assignment that moves
the control of S, from one location to
another.
3. An assignment can only execute if its
guard is true.
x=n
48
Floyd Assertion Network
x<n  x++
0xn
0xn
2
0
0xn
x=n  skip
3
x= n
x=n
1. Decorate nodes with assertions.
2. Prove enter and exit
3. Prove for each edge, the
corresponding Hoare triple.
49
Handling exception and return-in-the-middle

With Flyod we can handle those.

if g then { S ; return }
T ;
return ;

try S catch T ;
g
S
T
g
T
S
50