Deductive Program Verification with Why3 Jean-Christophe Filli atre - - PowerPoint PPT Presentation

deductive program verification with why3
SMART_READER_LITE
LIVE PREVIEW

Deductive Program Verification with Why3 Jean-Christophe Filli atre - - PowerPoint PPT Presentation

Deductive Program Verification with Why3 Jean-Christophe Filli atre CNRS EJCP June 25, 2015 http://why3.lri.fr/ejcp-2015/ 1 / 130 team VALS http://vals.lri.fr/ Universit e Paris Sud 2 / 130 team VALS http://vals.lri.fr/


slide-1
SLIDE 1

Deductive Program Verification with Why3

Jean-Christophe Filliˆ atre CNRS EJCP June 25, 2015 http://why3.lri.fr/ejcp-2015/

1 / 130

slide-2
SLIDE 2

team VALS — http://vals.lri.fr/

Universit´ e Paris Sud

2 / 130

slide-3
SLIDE 3

team VALS — http://vals.lri.fr/

Universit´ e Paris Sud LRI

3 / 130

slide-4
SLIDE 4

team VALS — http://vals.lri.fr/

Universit´ e Paris Sud CNRS LRI

4 / 130

slide-5
SLIDE 5

team VALS — http://vals.lri.fr/

Universit´ e Paris Sud CNRS LRI Inria Saclay-ˆ Ile-de-France

5 / 130

slide-6
SLIDE 6

team VALS — http://vals.lri.fr/

Universit´ e Paris Sud CNRS LRI Inria Saclay-ˆ Ile-de-France VALS

6 / 130

slide-7
SLIDE 7

Software is hard. – Don Knuth

why?

  • wrong interpretation of specifications
  • coding in a hurry
  • incompatible changes
  • software = complex artifact
  • etc.

7 / 130

slide-8
SLIDE 8

a famous example: binary search

first publication in 1946 first publication without bug in 1962 Jon Bentley. Programming Pearls. 1986. Writing correct programs the challenge of binary search and yet...

8 / 130

slide-9
SLIDE 9

and yet

in 2006, a bug was found in Java standard library’s binary search Joshua Bloch, Google Research Blog “Nearly All Binary Searches and Mergesorts are Broken” it had been there for 9 years

9 / 130

slide-10
SLIDE 10

the bug

... int mid = (low + high) / 2; int midVal = a[mid]; ... may exceed the capacity of type int then provokes an access out of array bounds a possible fix int mid = low + (high - low) / 2;

10 / 130

slide-11
SLIDE 11

what can we do?

better programming languages

  • better syntax

(e.g. avoid considering DO 17 I = 1. 10 as an assignment)

  • more typing

(e.g. avoid confusion between meters and yards)

  • more warnings from the compiler

(e.g. do not forget some cases)

  • etc.

11 / 130

slide-12
SLIDE 12

test

systematic and rigorous test is another, complementary answer but test is

  • costly
  • sometimes difficult to perform
  • and incomplete (except in some rare cases)

12 / 130

slide-13
SLIDE 13

formal methods

formal methods propose a mathematical approach to software correctness

13 / 130

slide-14
SLIDE 14

what is a program?

there are several aspects

  • what we compute
  • how we compute it
  • why it is correct to compute it this way

14 / 130

slide-15
SLIDE 15

what is a program?

the code is only one aspect (“how”) and nothing else “what” and “why” are not part of the code there are informal requirements, comments, web pages, drawings, research articles, etc.

15 / 130

slide-16
SLIDE 16

an example

  • how: 2 lines of C

a[52514],b,c=52514,d,e,f=1e4,g,h;main(){for(;b=c-=14;h=printf("%04d", e+d/f))for(e=d%=f;g=--b*2;d/=g)d=d*b+f*(h?a[b]:f/5),a[b]=d%--g;}

16 / 130

slide-17
SLIDE 17

an example

  • how: 2 lines of C

a[52514],b,c=52514,d,e,f=1e4,g,h;main(){for(;b=c-=14;h=printf("%04d", e+d/f))for(e=d%=f;g=--b*2;d/=g)d=d*b+f*(h?a[b]:f/5),a[b]=d%--g;}

  • what: 15,000 decimals of π
  • why: lot of maths, including

π =

  • i=0

(i!)2 2i+1 (2i + 1)!

17 / 130

slide-18
SLIDE 18

formal methods

formal methods propose a rigorous approach to programming, where we manipulate

  • a specification written in some mathematical language
  • a proof that the program satisfies this specification

18 / 130

slide-19
SLIDE 19

specification

what do we intend to prove?

  • safety: the program does not crash
  • no illegal access to memory
  • no illegal operation, such as division by zero
  • termination
  • functional correctness
  • the program does what it is supposed to do

19 / 130

slide-20
SLIDE 20

several approaches

model checking, abstract interpretation, etc. this lecture introduces deductive verification program + specification verification conditions proof

20 / 130

slide-21
SLIDE 21

this is not new

  • A. M. Turing. Checking a large routine. 1949.

STOP r′ = 1 u′ = 1 v′ = u TEST r − n s′ = 1 u′ = u + v s′ = s + 1 r′ = r + 1 TEST s − r 21 / 130

slide-22
SLIDE 22

this is not new

Tony Hoare. Proof of a program: FIND.

  • Commun. ACM, 1971.

k ≤ v v ≥ v

22 / 130

slide-23
SLIDE 23

checking a large routine (Turing, 1949)

STOP r′ = 1 u′ = 1 v′ = u TEST r − n s′ = 1 u′ = u + v s′ = s + 1 r′ = r + 1 TEST s − r

23 / 130

slide-24
SLIDE 24

checking a large routine (Turing, 1949)

STOP r′ = 1 u′ = 1 v′ = u TEST r − n s′ = 1 u′ = u + v s′ = s + 1 r′ = r + 1 TEST s − r

u ← 1 for r = 0 to n − 1 do v ← u for s = 1 to r do u ← u + v

24 / 130

slide-25
SLIDE 25

checking a large routine (Turing, 1949)

STOP r′ = 1 u′ = 1 v′ = u TEST r − n s′ = 1 u′ = u + v s′ = s + 1 r′ = r + 1 TEST s − r

precondition {n ≥ 0} u ← 1 for r = 0 to n − 1 do v ← u for s = 1 to r do u ← u + v postcondition {u = fact(n)}

25 / 130

slide-26
SLIDE 26

checking a large routine (Turing, 1949)

STOP r′ = 1 u′ = 1 v′ = u TEST r − n s′ = 1 u′ = u + v s′ = s + 1 r′ = r + 1 TEST s − r

precondition {n ≥ 0} u ← 1 for r = 0 to n − 1 do invariant {u = fact(r)} v ← u for s = 1 to r do invariant {u = s × fact(r)} u ← u + v postcondition {u = fact(n)}

26 / 130

slide-27
SLIDE 27

verification condition

function fact(int) : int axiom fact0: fact(0) = 1 axiom factn: ∀ n:int. n ≥ 1 → fact(n) = n * fact(n-1) goal vc: ∀ n:int. n ≥ 0 → (0 > n - 1 → 1 = fact(n)) ∧ (0 ≤ n - 1 → 1 = fact(0) ∧ (∀ u:int. (∀ r:int. 0 ≤ r ∧ r ≤ n - 1 → u = fact(r) → (1 > r → u = fact(r + 1)) ∧ (1 ≤ r → u = 1 * fact(r) ∧ (∀ u1:int. (∀ s:int. 1 ≤ s ∧ s ≤ r → u1 = s * fact(r) → (∀ u2:int. u2 = u1 + u → u2 = (s + 1) * fact(r))) ∧ (u1 = (r + 1) * fact(r) → u1 = fact(r + 1))))) ∧ (u = fact((n - 1) + 1) → u = fact(n))))

27 / 130

slide-28
SLIDE 28

verification condition

function fact(int) : int axiom fact0: fact(0) = 1 goal vc: ∀ n:int. n ≥ 0 → (0 > n - 1 → 1 = fact(n)) ∧

28 / 130

slide-29
SLIDE 29

and then

what do we do with this mathematical statement? we could perform a manual proof (as Turing and Hoare did) but it is long, tedious, and error-prone so we turn to tools that mechanize mathematical reasoning

29 / 130

slide-30
SLIDE 30

automated theorem proving

mathematical statement automated prover true false

30 / 130

slide-31
SLIDE 31

no hope

it is not possible to implement such a program

(Turing/Church, 1936, from G¨

  • del)

full employment theorem for mathematicians Kurt G¨

  • del

31 / 130

slide-32
SLIDE 32

automated theorem proving

mathematical statement automated prover true false I don’t know loops forever examples: Z3, CVC4, Alt-Ergo, Vampire, SPASS, etc.

32 / 130

slide-33
SLIDE 33

interactive theorem proving

if we only intend to check a proof, this is decidable mathematical statement proof proof assistant true false examples: Coq, Isabelle, PVS, HOL Light, etc.

33 / 130

slide-34
SLIDE 34

Why3, a tool for deductive verification

main idea: use as many theorem provers as possible (both automated and interactive) program + property mathematical statement prover 1 prover 2 prover 3 . . .

34 / 130

slide-35
SLIDE 35

Why3 in a nutshell

  • a programming language, WhyML
  • polymorphism
  • pattern-matching
  • exceptions
  • mutable data structures,

with controlled aliasing

  • a polymorphic logic
  • algebraic data types
  • recursive definitions
  • (co)inductive predicates

http://why3.lri.fr/

file.why file.mlw WhyML VCgen Why transform/translate print/run Coq Alt-Ergo CVC3 Z3 etc.

35 / 130

slide-36
SLIDE 36

applications

three different ways of using Why3

  • as a logical language

(a convenient front-end to many theorem provers)

  • as a programming language to prove algorithms

(many examples in our gallery)

  • as an intermediate language,

to verify programs written in C, Java, Ada, etc.

36 / 130

slide-37
SLIDE 37

some systems using Why3

GNATprove Krakatoa Frama-C Jessie WP Easycrypt Why3 WhyML logic proof assistants SMT solvers ATP systems

  • ther provers

Ada Java C prob. pgms

37 / 130

slide-38
SLIDE 38

Why3, bottom up

file.why file.mlw WhyML VCgen Why transform/translate print/run Coq Alt-Ergo CVC3 Z3 etc.

38 / 130

slide-39
SLIDE 39

Part I

  • ne logic to use them all

39 / 130

slide-40
SLIDE 40

demo 1: the logic of Why3

40 / 130

slide-41
SLIDE 41

summary

logic of Why3 = polymorphic logic, with

  • (mutually) recursive algebraic data types
  • (mutually) recursive function/predicate symboles
  • (mutually) (co)inductive predicates
  • let-in, match-with, if-then-else

formal definition in

One Logic To Use Them All (CADE 2013)

41 / 130

slide-42
SLIDE 42

declarations

  • types
  • abstract: type t
  • alias: type t = list int
  • algebraic: type list α = Nil | Cons α (list α)
  • function / predicate
  • uninterpreted: function f int : int
  • defined: predicate non empty (l: list α) = l = Nil
  • inductive predicate
  • inductive trans t t = ...
  • axiom / lemma / goal
  • goal G: ∀ x: int. x ≥ 0 → x*x ≥ 0

42 / 130

slide-43
SLIDE 43

theories

logic declarations organized in theories a theory T1 can be

  • used (use) in a theory T2
  • cloned (clone) in another theory T2

theory end theory end theory end

43 / 130

slide-44
SLIDE 44

theories

logic declarations organized in theories a theory T1 can be

  • used (use) in a theory T2
  • symbols of T1 are shared
  • axioms of T1 remain axioms
  • lemmas of T1 become axioms
  • goals of T1 are ignored
  • cloned (clone) in another theory T2

theory end theory end theory end

44 / 130

slide-45
SLIDE 45

theories

logic declarations organized in theories a theory T1 can be

  • used (use) in a theory T2
  • cloned (clone) in another theory T2
  • declarations of T1 are copied or substituted
  • axioms of T1 remain axioms or become

lemmas/goals

  • lemmas of T1 become axioms
  • goals of T1 are ignored

theory end theory end theory end

45 / 130

slide-46
SLIDE 46

using theorem provers

there are many theorem provers

  • SMT solvers: Alt-Ergo, Z3, CVC3, Yices, etc.
  • TPTP provers: Vampire, Eprover, SPASS, etc.
  • proof assistants: Coq, PVS, Isabelle, etc.
  • dedicated provers, e.g. Gappa

we want to use all of them if possible

46 / 130

slide-47
SLIDE 47

under the hood

a technology to talk to provers central concept: task

  • a context (a list of declarations)
  • a goal (a formula)

goal

47 / 130

slide-48
SLIDE 48

workflow

theory end theory end theory end

Alt-Ergo Z3 Vampire

48 / 130

slide-49
SLIDE 49

workflow

theory end theory end theory end goal

Alt-Ergo Z3 Vampire

49 / 130

slide-50
SLIDE 50

workflow

theory end theory end theory end goal goal

Alt-Ergo Z3 Vampire T1

50 / 130

slide-51
SLIDE 51

workflow

theory end theory end theory end goal goal goal

Alt-Ergo Z3 Vampire T1 T2

51 / 130

slide-52
SLIDE 52

workflow

theory end theory end theory end goal goal goal

Alt-Ergo Z3 Vampire T1 T2 P

52 / 130

slide-53
SLIDE 53

transformations

  • eliminate algebraic data types and match-with
  • eliminate inductive predicates
  • eliminate if-then-else, let-in
  • encode polymorphism, encode types
  • etc.

efficient: results of transformations are memoized

53 / 130

slide-54
SLIDE 54

driver

a task journey is driven by a file

  • transformations to apply
  • prover’s input format
  • syntax
  • predefined symbols / axioms
  • prover’s diagnostic messages

more details:

Expressing Polymorphic Types in a Many-Sorted Language (FroCos 2011) Why3: Shepherd your herd of provers (Boogie 2011)

54 / 130

slide-55
SLIDE 55

example: Z3 driver (excerpt)

printer "smtv2" valid "^unsat" invalid "^sat" transformation "inline trivial" transformation "eliminate builtin" transformation "eliminate definition" transformation "eliminate inductive" transformation "eliminate algebraic" transformation "simplify formula" transformation "discriminate" transformation "encoding smt" prelude "(set-logic AUFNIRA)" theory BuiltIn syntax type int "Int" syntax type real "Real" syntax predicate (=) "(= %1 %2)" meta "encoding : kept" type int end

55 / 130

slide-56
SLIDE 56

API

Why3 has an OCaml API

  • to build terms, declarations, theories, tasks
  • to call provers

defensive API

  • well-typed terms
  • well-formed declarations, theories, and tasks

56 / 130

slide-57
SLIDE 57

plug-ins

Why3 can be extended via three kinds of plug-ins

  • parsers (new input formats)
  • transformations (to be used in drivers)
  • printers (to add support for new provers)

57 / 130

slide-58
SLIDE 58

API and plug-ins

Your code Why3 API WhyML TPTP etc. eliminate algebraic encode polymorphism etc. Simplify Alt-Ergo SMT-lib etc.

58 / 130

slide-59
SLIDE 59

summary

  • numerous theorem provers are supported
  • SMT, TPTP, proof assistants, etc.
  • user-extensible system
  • input languages
  • transformations
  • output syntax
  • proofs
  • are preserved
  • can be replayed

more details: Preserving User Proofs Across Specification Changes (VSTTE 2013)

59 / 130

slide-60
SLIDE 60

Part II program verification

60 / 130

slide-61
SLIDE 61

demo 2: an historical example

  • A. M. Turing. Checking a Large Routine. 1949.

STOP r′ = 1 u′ = 1 v′ = u TEST r − n s′ = 1 u′ = u + v s′ = s + 1 r′ = r + 1 TEST s − r

61 / 130

slide-62
SLIDE 62

demo 2: an historical example

  • A. M. Turing. Checking a Large Routine. 1949.

STOP r′ = 1 u′ = 1 v′ = u TEST r − n s′ = 1 u′ = u + v s′ = s + 1 r′ = r + 1 TEST s − r

u ← 1 for r = 0 to n − 1 do v ← u for s = 1 to r do u ← u + v

demo (access code)

62 / 130

slide-63
SLIDE 63

demo 3: another historical example

f (n) = n − 10 si n > 100, f (f (n + 11)) sinon.

demo (access code)

63 / 130

slide-64
SLIDE 64

demo 3: another historical example

f (n) = n − 10 si n > 100, f (f (n + 11)) sinon.

demo (access code)

e ← 1 while e > 0 do if n > 100 then n ← n − 10 e ← e − 1 else n ← n + 11 e ← e + 1 return n

demo (access code)

64 / 130

slide-65
SLIDE 65

Recapitulation

  • pre/postcondition

let foo x y z requires { P } ensures { Q } = ...

  • loop invariant

while ... do invariant { I } ... done for i = ... do invariant { I(i) } ... done

65 / 130

slide-66
SLIDE 66

Recapitulation

termination of a loop (resp. a recursive function) is ensured by a variant variant {t} with R

  • R is a well-founded order relation
  • t decreases for R at each step

(resp. each recursive call) by default, t is of type int and R is the relation y ≺ x def = y < x ∧ 0 ≤ x

66 / 130

slide-67
SLIDE 67

remark

as shown with function 91, proving termination may require to establish functional properties as well another example:

  • Floyd’s cycle detection (tortoise and hare algorithm)

67 / 130

slide-68
SLIDE 68

now, it’s up to you suggested exercises

  • Euclidean division (exo_eucl_div.mlw)
  • Factorial (exo_fact.mlw)
  • Fast exponentiation (exo_power.mlw)

68 / 130

slide-69
SLIDE 69

Part III arrays

69 / 130

slide-70
SLIDE 70

mutable data

  • nly one kind of mutable data structure:

records with mutable fields for instance, references are defined this way type ref α = { mutable contents : α } and ref, !, and := are regular functions

70 / 130

slide-71
SLIDE 71

arrays

the library introduces arrays as follows:

type array α model { length: int; mutable elts: map int α }

where

  • map is the logical type of purely applicative maps
  • keyword model means type array α is an abstract data type

in programs

71 / 130

slide-72
SLIDE 72
  • perations on arrays

we cannot define operations over type array α (it is abstract) but we can declare them examples:

val ([]) (a: array α) (i: int) : α requires { 0 ≤ i < length a } ensures { result = Map.get a.elts i } val ([]←) (a: array α) (i: int) (v: α) : unit requires { 0 ≤ i < length a } writes { a.elts } ensures { a.elts = Map.set (old a.elts) i v } and other operations such as create, append, sub, copy, etc.

72 / 130

slide-73
SLIDE 73

arrays in the logic

when we write a[i] in the logic

  • it is mere syntax for Map.get a.elts i
  • we do not prove that i is within array bounds

(a.elts is a map over all integers)

73 / 130

slide-74
SLIDE 74

demo 4: Boyer-Moore’s majority

given a multiset of N votes A A A C C B B C C C B C C determine the majority, if any

74 / 130

slide-75
SLIDE 75

an elegant solution

due to Boyer & Moore (1980) linear time uses only three variables

75 / 130

slide-76
SLIDE 76

principle

A A A C C B B C C C B C C cand = A k = 1

76 / 130

slide-77
SLIDE 77

principle

A A A C C B B C C C B C C cand = A k = 2

77 / 130

slide-78
SLIDE 78

principle

A A A C C B B C C C B C C cand = A k = 3

78 / 130

slide-79
SLIDE 79

principle

A A A C C B B C C C B C C cand = A k = 2

79 / 130

slide-80
SLIDE 80

principle

A A A C C B B C C C B C C cand = A k = 1

80 / 130

slide-81
SLIDE 81

principle

A A A C C B B C C C B C C cand = A k = 0

81 / 130

slide-82
SLIDE 82

principle

A A A C C B B C C C B C C cand = B k = 1

82 / 130

slide-83
SLIDE 83

principle

A A A C C B B C C C B C C cand = B k = 0

83 / 130

slide-84
SLIDE 84

principle

A A A C C B B C C C B C C cand = C k = 1

84 / 130

slide-85
SLIDE 85

principle

A A A C C B B C C C B C C cand = C k = 2

85 / 130

slide-86
SLIDE 86

principle

A A A C C B B C C C B C C cand = C k = 1

86 / 130

slide-87
SLIDE 87

principle

A A A C C B B C C C B C C cand = C k = 2

87 / 130

slide-88
SLIDE 88

principle

A A A C C B B C C C B C C cand = C k = 3

88 / 130

slide-89
SLIDE 89

principle

A A A C C B B C C C B C C cand = C k = 3 then we check if C indeed has majority, with a second pass (in that case, it has: 7 > 13/2)

89 / 130

slide-90
SLIDE 90

Fortran

90 / 130

slide-91
SLIDE 91

Why3

let mjrty (a: array candidate) = let n = length a in let cand = ref a[0] in let k = ref 0 in for i = 0 to n-1 do if !k = 0 then begin cand := a[i]; k := 1 end else if !cand = a[i] then incr k else decr k done; if !k = 0 then raise Not found; try if 2 * !k > n then raise Found; k := 0; for i = 0 to n-1 do if a[i] = !cand then begin incr k; if 2 * !k > n then raise Found end done; raise Not found with Found → !cand end

demo (access code) 91 / 130

slide-92
SLIDE 92

specification

  • precondition

let mjrty (a: array candidate) requires { 1 ≤ length a }

  • postcondition in case of success

ensures { 2 * numeq a result 0 (length a) > length a }

  • postcondition in case of failure

raises { Not found → ∀ c: candidate. 2 * numeq a c 0 (length a) ≤ length a }

92 / 130

slide-93
SLIDE 93

loop invariants

first loop for i = 0 to n-1 do invariant { 0 ≤ !k ≤ numeq a !cand 0 i } invariant { 2 * (numeq a !cand 0 i - !k) ≤ i - !k } invariant { ∀ c: candidate. c = !cand → 2 * numeq a c 0 i ≤ i - !k } ... second loop for i = 0 to n-1 do invariant { !k = numeq a !cand 0 i } invariant { 2 * !k ≤ n } ...

93 / 130

slide-94
SLIDE 94

proof

verification conditions express

  • safety
  • access within array bounds
  • termination
  • user annotations
  • loop invariants are initialized and preserved
  • postconditions are established

fully automated proof

94 / 130

slide-95
SLIDE 95

extraction to OCaml

WhyML code can be translated to OCaml code why3 extract -D ocaml64 -D mjrty -T mjrty.Mjrty -o . two drivers used here

  • a library driver for 64-bit OCaml

(maps type int to Zarith, type array to OCaml’s arrays, etc.)

  • a custom driver for this example, namely

module mjrty.Mjrty syntax type candidate "char" end

95 / 130

slide-96
SLIDE 96

extraction to OCaml

then we can link extracted code with hand-written code

  • camlopt ... zarith.cmxa why3extract.cmxa

mjrty__Mjrty.ml test_mjrty.ml

96 / 130

slide-97
SLIDE 97

exercise: two-way sort

sort an array of Boolean, using the following algorithm

let two way sort (a: array bool) = let i = ref 0 in let j = ref (length a - 1) in while !i < !j do if not a[!i] then incr i else if a[!j] then decr j else begin let tmp = a[!i] in a[!i] ← a[!j]; a[!j] ← tmp; incr i; decr j end done

False ? . . . ? True ↑ ↑ i j exercise: exo_two_way.mlw

97 / 130

slide-98
SLIDE 98

exercise: Dutch national flag

an array contains elements of the following enumerated type type color = Blue | White | Red sort it, in such a way we have the following final situation: . . . Blue . . . . . . White . . . . . . Red . . .

98 / 130

slide-99
SLIDE 99

exercise: Dutch national flag

let dutch flag (a:array color) (n:int) = let b = ref 0 in let i = ref 0 in let r = ref n in while !i < !r do match a[!i] with | Blue → swap a !b !i; incr b; incr i | White → incr i | Red → decr r; swap a !r !i end done

Blue White . . . Red ↑ ↑ ↑ ↑ !b !i !r n exercise: exo_flag.mlw

99 / 130

slide-100
SLIDE 100

Part IV specifying / implementing a data structure

100 / 130

slide-101
SLIDE 101

example

say we want to implement a queue with bounded capacity type queue α val create: int → queue α val push: α → queue α → unit val pop: queue α → α

101 / 130

slide-102
SLIDE 102

ring buffer

it can be implemented with an array type buffer α = { mutable first: int; mutable len : int; data : array α; } len elements are stored, starting at index first x0 x1 . . . xlen−1 ↑

first

they may wrap around the array bounds . . . xlen−1 x0 x1 ↑

first

102 / 130

slide-103
SLIDE 103

specification

to give a specification to queue operations, we would like to model the queue contents, say, as a sequence of elements

  • ne way to do it is to use ghost code

103 / 130

slide-104
SLIDE 104

ghost code

may be inserted for the purpose of specification and/or proof rules are:

  • ghost code may read regular data (but can’t modify it)
  • ghost code cannot modify the control flow of regular code
  • regular code does not see ghost data

in particular, ghost code can be removed without observable modification (and is removed during OCaml extraction)

104 / 130

slide-105
SLIDE 105

ghost field

we add two ghost fields to model the queue contents type queue α = { ... ghost capacity: int; ghost mutable sequence: Seq.seq α; }

105 / 130

slide-106
SLIDE 106

ghost field

then we use them in specifications val create (n: int) (dummy: α) : queue α requires { n > 0 } ensures { result.capacity = n } ensures { result.sequence = Seq.empty } val push (q: queue α) (x: α) : unit requires { Seq.length q.sequence < q.capacity } writes { q.sequence } ensures { q.sequence = Seq.snoc (old q.sequence) x } val pop (q: queue α) : α requires { Seq.length q.sequence > 0 } writes { q.sequence } ensures { result = (old q.sequence)[0] } ensures { q.sequence = (old q.sequence)[1 ..] }

106 / 130

slide-107
SLIDE 107

abstraction

we are already able to prove some client code using the queue let harness () = let q = create 10 0 in push q 1; push q 2; push q 3; let x = pop q in assert { x = 1 }; let x = pop q in assert { x = 2 }; let x = pop q in assert { x = 3 }; ()

107 / 130

slide-108
SLIDE 108

gluing invariant

we link the regular fields and the ghost fields with a type invariant

type buffer α = ... invariant { self.capacity = Array.length self.data ∧ 0 ≤ self.first < self.capacity ∧ 0 ≤ self.len ≤ self.capacity ∧ self.len = Seq.length self.sequence ∧ ∀ i: int. 0 ≤ i < self.len → (self.first + i < self.capacity → Seq.get self.sequence i = self.data[self.first + i]) ∧ (0 ≤ self.first + i - self.capacity → Seq.get self.sequence i = self.data[self.first + i

  • self.capacity])

}

108 / 130

slide-109
SLIDE 109

semantics

such a type invariant holds at function boundaries thus

  • it is assumed at function entry
  • it must be ensured
  • when a function is called
  • at function exit, for values returned or modified

109 / 130

slide-110
SLIDE 110

ghost code

ghost code is added to set ghost fields accordingly example: let push (b: buffer α) (x: α) : unit = ghost b.sequence ← Seq.snoc b.sequence x; let i = b.first + b.len in let n = Array.length b.data in b.data[if i ≥ n then i - n else i] ← x; b.len ← b.len + 1

110 / 130

slide-111
SLIDE 111

exercise: ring buffer

implement other operations

  • length
  • clear
  • head
  • n ring buffers and prove them correct

111 / 130

slide-112
SLIDE 112

Part V purely applicative programming

112 / 130

slide-113
SLIDE 113
  • ther data structures

a key idea of Hoare logic: any types and symbols from the logic can be used in programs note: we already used type int this way

113 / 130

slide-114
SLIDE 114

algebraic data types

we can do so with algebraic data types in the library, we find type bool = True | False (in bool.Bool) type option α = None | Some α (in option.Option) type list α = Nil | Cons α (list α) (in list.List)

114 / 130

slide-115
SLIDE 115

trees

let us consider binary trees type elt type tree = | Empty | Node tree elt tree and the following problem

115 / 130

slide-116
SLIDE 116

same fringe

given two binary trees, do they contain the same elements when traversed in order? 8 3 1 5 4 4 1 3 8 5

116 / 130

slide-117
SLIDE 117

specification

function elements (t: tree) : list elt = match t with | Empty → Nil | Node l x r → elements l ++ Cons x (elements r) end let same fringe (t1 t2: tree) : bool ensures { result=True ↔ elements t1 = elements t2 } = ...

117 / 130

slide-118
SLIDE 118

a solution

  • ne solution: look at the left branch as

a list, from bottom up x1 x2 ... xn t1 t2 tn

118 / 130

slide-119
SLIDE 119

a solution

  • ne solution: look at the left branch as

a list, from bottom up x1 x2 ... xn t1 t2 tn 1 3 8 5 4 1 4 3 8 5

demo (access code)

119 / 130

slide-120
SLIDE 120

exercise: inorder traversal

type elt type tree = Null | Node tree elt tree inorder traversal of t, storing its elements in array a

let rec fill (t: tree) (a: array elt) (start: int) : int = match t with | Null → start | Node l x r → let res = fill l a start in if res = length a then begin a[res] ← x; fill r a (res + 1) end else res end exercise: exo_fill.mlw

120 / 130

slide-121
SLIDE 121

Part VI machine arithmetic

121 / 130

slide-122
SLIDE 122

machine arithmetic

let us model signed 32-bit arithmetic two possibilities:

  • ensure absence of arithmetic overflow
  • model machine arithmetic faithfully (i.e. with overflows)

a constraint: we do not want to loose arithmetic capabilities of SMT solvers

122 / 130

slide-123
SLIDE 123

32-bit arithmetic

we introduce a new type for 32-bit integers type int32 its integer value is given by function toint int32 : int main idea: within annotations, we only use type int (thus a program variable x : int32 always appears as toint x in annotations)

123 / 130

slide-124
SLIDE 124

32-bit arithmetic

we define the range of 32-bit integers function min int: int = - 0x8000 0000 (* -2^31 *) function max int: int = 0x7FFF FFFF (* 2^31-1 *) when we use them... axiom int32 domain: ∀ x: int32. min int ≤ toint x ≤ max int ... and when we build them val ofint (x: int) : int32 requires { min int ≤ x ≤ max int } ensures { toint result = x }

124 / 130

slide-125
SLIDE 125

32-bit arithmetic

then each program expression such as x + y is translated into

  • fint (toint x) (toint y)

this ensures the absence of arithmetic overflow (but we get a large number of additional verification conditions)

125 / 130

slide-126
SLIDE 126

binary search

let us consider searching for a value in a sorted array using binary search let us show the absence of arithmetic overflow

demo (access code)

126 / 130

slide-127
SLIDE 127

binary search

we found a bug the computation let m = (!l + !u) / 2 in may provoke an arithmetic overflow (for instance with a 2-billion elements array) a possible fix is let m = !l + (!u - !l) / 2 in

127 / 130

slide-128
SLIDE 128

conclusion

128 / 130

slide-129
SLIDE 129

conclusion

three different ways of using Why3

  • as a logical language

(a convenient front-end to many theorem provers)

  • as a programming language to prove algorithms

(currently 120 examples in our gallery)

  • as an intermediate language

(for the verification of C, Java, Ada, etc.)

129 / 130

slide-130
SLIDE 130

things not covered in this lecture

  • how aliases are controlled
  • how verification conditions are computed
  • how formulas are sent to provers
  • how pointers/heap are modeled
  • how floating-point arithmetic is modeled
  • etc.

see http://why3.lri.fr for more details

130 / 130