50.530: Software Engineering Sun Jun SUTD. Week 9: Hoare Logic.

Slides:



Advertisements
Similar presentations
Automated Theorem Proving Lecture 1. Program verification is undecidable! Given program P and specification S, does P satisfy S?
Advertisements

NP-Hard Nattee Niparnan.
Semantics Static semantics Dynamic semantics attribute grammars
Copyright , Doron Peled and Cesare Tinelli. These notes are based on a set of lecture notes originally developed by Doron Peled at the University.
50.530: Software Engineering Sun Jun SUTD. Week 10: Invariant Generation.
Reasoning About Code; Hoare Logic, continued
Hoare’s Correctness Triplets Dijkstra’s Predicate Transformers
Rigorous Software Development CSCI-GA Instructor: Thomas Wies Spring 2012 Lecture 11.
David Evans CS655: Programming Languages University of Virginia Computer Science Lecture 19: Minding Ps & Qs: Axiomatic.
Axiomatic Verification I Prepared by Stephen M. Thebaut, Ph.D. University of Florida Software Testing and Verification Lecture 17.
Partial correctness © Marcelo d’Amorim 2010.
Axiomatic Semantics The meaning of a program is defined by a formal system that allows one to deduce true properties of that program. No specific meaning.
Copyright © 2006 Addison-Wesley. All rights reserved.1-1 ICS 410: Programming Languages Chapter 3 : Describing Syntax and Semantics Axiomatic Semantics.
ISBN Chapter 3 Describing Syntax and Semantics.
1 Discrete Structures Lecture 29 Predicates and Programming Read Ch
1 Design by Contract Building Reliable Software. 2 Software Correctness Correctness is a relative notion  A program is correct with respect to its specification.
Predicate Transformers
Program Proving Notes Ellen L. Walker.
CSE 331 Software Design & Implementation Dan Grossman Winter 2014 Lecture 2 – Reasoning About Code With Logic 1CSE 331 Winter 2014.
1/22 Programs : Semantics and Verification Charngki PSWLAB Programs: Semantics and Verification Mordechai Ben-Ari Mathematical Logic for Computer.
ESC Java. Static Analysis Spectrum Power Cost Type checking Data-flow analysis Model checking Program verification AutomatedManual ESC.
Hoare-style program verification K. Rustan M. Leino Guest lecturer Rob DeLine’s CSE 503, Software Engineering University of Washington 26 Apr 2004.
Copyright © 2006 The McGraw-Hill Companies, Inc. Programming Languages 2nd edition Tucker and Noonan Chapter 18 Program Correctness To treat programming.
ESC Java. Static Analysis Spectrum Power Cost Type checking Data-flow analysis Model checking Program verification AutomatedManual ESC.
CS 330 Programming Languages 09 / 16 / 2008 Instructor: Michael Eckmann.
Software Verification Bertrand Meyer Chair of Software Engineering Lecture 2: Axiomatic semantics.
Describing Syntax and Semantics
Floyd Hoare Logic. Semantics A programming language specification consists of a syntactic description and a semantic description. Syntactic description:symbols.
Cs3102: Theory of Computation Class 18: Proving Undecidability Spring 2010 University of Virginia David Evans.
Nattee Niparnan. Easy & Hard Problem What is “difficulty” of problem? Difficult for computer scientist to derive algorithm for the problem? Difficult.
Reading and Writing Mathematical Proofs
1 Inference Rules and Proofs (Z); Program Specification and Verification Inference Rules and Proofs (Z); Program Specification and Verification.
CSI 3125, Axiomatic Semantics, page 1 Axiomatic semantics The assignment statement Statement composition The "if-then-else" statement The "while" statement.
1 Formal Semantics of Programming Languages “Program testing can be used to show the presence of bugs, but never to show their absence!” --Dijkstra.
Chapter 25 Formal Methods Formal methods Specify program using math Develop program using math Prove program matches specification using.
CS 363 Comparative Programming Languages Semantics.
Mr. Dave Clausen1 La Cañada High School Chapter 6: Repetition Statements.
Reading and Writing Mathematical Proofs Spring 2015 Lecture 4: Beyond Basic Induction.
Reasoning about programs March CSE 403, Winter 2011, Brun.
Program Analysis and Verification Spring 2014 Program Analysis and Verification Lecture 4: Axiomatic Semantics I Roman Manevich Ben-Gurion University.
COP4020 Programming Languages Introduction to Axiomatic Semantics Prof. Robert van Engelen.
CSE 311 Foundations of Computing I Lecture 28 Computability: Other Undecidable Problems Autumn 2011 CSE 3111.
This Week Lecture on relational semantics Exercises on logic and relations Labs on using Isabelle to do proofs.
Dr. Naveed Riaz Design and Analysis of Algorithms 1 1 Formal Methods in Software Engineering Lecture # 26.
Cs7100(Prasad)L18-9WP1 Axiomatic Semantics Predicate Transformers.
CSC3315 (Spring 2009)1 CSC 3315 Languages & Compilers Hamid Harroud School of Science and Engineering, Akhawayn University
CORRECTNESS ISSUES AND LOOP INVARIANTS Lecture 8 CS2110 – Fall 2014.
Reasoning About Code.
Reasoning about code CSE 331 University of Washington.
CSE 331 Software Design & Implementation
Formal Methods in Software Engineering 1
Hoare-style program verification
Reasoning About Code; Hoare Logic
Lecture 5 Floyd-Hoare Style Verification
Axiomatic semantics Points to discuss: The assignment statement
Reasoning about Loops, Conclusion
Programming Languages and Compilers (CS 421)
Programming Languages 2nd edition Tucker and Noonan
Semantics In Text: Chapter 3.
Formal Methods in software development
Predicate Transformers
Formal Methods in software development
Axiomatic Semantics Will consider axiomatic semantics (A.S.) of IMP:
The Zoo of Software Security Techniques
Program correctness Axiomatic semantics
Program Verification with Hoare Logic
Programming Languages and Compilers (CS 421)
50.530: Software Engineering
Programming Languages 2nd edition Tucker and Noonan
COP4020 Programming Languages
Presentation transcript:

50.530: Software Engineering Sun Jun SUTD

Week 9: Hoare Logic

Delta Debugging is based on testing. The bug localization methods are based on testing. The specification mining methods are based on testing. The dynamic race detection methods are based on testing. Testing is Limited

the behaviors we wanted A C the behaviors we have the initial state a test which shows a bug “Testing shows the presence, not the absence of bugs.” ---Dijkstra “Testing shows the presence, not the absence of bugs.” ---Dijkstra

Example Initially: x = y = 0 thread 1 { x = random.nextInt(0, ); count++; } thread 2 { if (x==3771) { ERROR; } Testing would unlikely spot the error. But humans do, why?

Program verification is to show the absence of bugs based on logic.

Verification. Why? There are systems which simply cannot afford any error, e.g., control software for nuclear plants, space missions, high-speed trains, cars (e.g., for cruise control), etc. Some systems are much more secure if they are verified, e.g., many security problems are really software bugs. All systems are better off if they could be verified. Plus, the problem is really challenging and interesting.

Verification is Hard Example: the Halting problem The problem is to determine, given a program* and an input to the program, whether the program will eventually halt when run with that input. Turing proved no algorithm can exist which will always correctly decide whether, for a given arbitrary program and its input, the program halts when run with that input. *in a programming language which is equivalent to a Turing machine.

Example float sumUp (float[] array, int length) { float result = 0.0; int i = 0 ; while (i < length) { result += array[i]; i++; } return result; } How do we prove this program is correct? What exactly do I want this program to do?

Function Specification We do need a specification in order to verify. Assume the specification is based predicate logic. A predicate is a Boolean function over program state, i.e., an expression that returns a Boolean value. – e.g., x = 3, y > x, x>0 => y+z = w, s = sum(x1, x2, x3), for all I in 0..n-1, a[i] > a[i-1]

Function Specification A function specification for a method usually consists of a pre-condition and a post- condition. Example: – given a semi-prime, your program outputs its prime factors What if the pre-condition is not satisfied, e.g., you are given a number which is not a semi-prime?

Correctness Partial Correctness: If the pre-condition is satisfied and the method terminates, it is guaranteed to satisfy the post-condition. Total Correctness: If the pre-condition is satisfied, it is guaranteed that the method terminates and satisfies the post-condition.

Function Specification: Example float sumUp (float[] array, int length) { float result = 0.0; int i = 0 ; while (i < length) { result += array[i]; i++; } return result; } {length >= 0 && array.length = length} {result = sum(array[j] for all j in 0..length)} pre-condition post-condition

AN AXIOMATIC BASIS FOR COMPUTER PROGRAMMING C. A. R. Hoare, Communications of the ACM 1969

Hoare Triples If we start in a state where Pre is true and execute Program, then Program will terminate in a state where Post is true. Examples: {true} x:=5 {x=5} {x=y} x := x+3 {x=y+3} {x=a} if(x<0)then x:=-x {x=|a|} {Pre}Program{Post}

Questions Are the following Hoare triples? {x<0} while (x != 0) { x:= x-1; } {true} {false} x := 3 {x = 8} {x>-1} x:=x*2+3 {x>1}

Strongest Post-conditions The following are all valid Hoare triples. {x = 5} x := x * 2 {true} {x = 5} x := x * 2 {x > 0} {x = 5} x := x * 2 {x = 10} All are true, but which one is the most useful, if we know x = 5 is satisfied before the program executes. Definition: If {Pre} Program {Post} and Post ⇒ Post’ for all Post’ such that {Pre} Program {Post’}, then Post is the strongest post-condition sp(Program, Pre) of Program with respect to Pre.

Question What is sp(x:=x*2+3, {x>-1})?

Weakest Pre-conditions The following are all valid Hoare triples. {x = 5 && y = 10} z := x / y {z < 1} {x 0} z := x / y {z < 1} {y ≠ 0 && x / y < 1} z := x / y {z < 1} All are true, but which one is the most useful (so that it allows us to invoke the program in the most general condition) if we know z < 1 is satisfied after the program? Definition: If {Pre} Program {Post} and Pre’ ⇒ Pre for all Pre’ such that {Pre’} program {Post}, then Pre is the weakest precondition wp(Program,Post) of Program with respect to Post.

Question What is wp(if(x<0)then x:=-x, x=|a|)?

Forward Analysis Theorem: {Pre} Program {Post} holds if and only if sp(Program,Pre) ⇒ Post. In other words, a Hoare Triple is valid if the post- condition is weaker than necessary, but not if it is too strong. Example: Since sp(x := x * 2, x=5) ≡ x=10, {x = 5} x := x * 2 {x > 0} holds.

Backward Analysis Theorem: {Pre} Program {Post} holds if and only if Pre ⇒ wp(Program,Post). In other words, a Hoare Triple is valid if the precondition is stronger than necessary, but not if it is too weak. Example: Since wp(z := x / y, z 0} z := x / y {z < 1} is valid.

Example float sumUp (float[] array, int length) { float result = 0.0; int i = 0 ; while (i < length) { result += array[i]; i++; } return result; } How do we prove the following? {length >= 0 && array.length = length} sumUp(array, length) {result = sum(array[j] for all j in 0..length-1)} We need systematic ways to deal with assignments, loops, conditionals, etc.

Axiomatic Approach Hoare logic rules: – For each kind of program (like assignment, loop, etc.), define a rule so that we can compute the weakest pre-condition for a given post-condition or the strongest post-condition for a given pre- condition.

Hoare Logic Rules: Assignment Example: wp(x := 3, x + y > 0) ≡ (x+y)[3/x] > 0 ≡ 3 + y > 0 ≡ y > -3 wp(x := E, post) ≡ post[E/x] where post [E/x] is the predicate obtained by replacing x with E in post. Another way to under this rule: for any pre, it must satisfy pre && x = 3 implies x+y>0 Another way to under this rule: for any pre, it must satisfy pre && x = 3 implies x+y>0

Exercise 1 What is wp(x := 3*y + z, x * y - z > 0)?

Hoare Logic Rules: Assignment Example: sp(x := 3, x + y > 0) ≡ oldx+y > 0 && x = 3 sp(x := 3x, x + y > 0) ≡ oldx+y > 0 && x = 3oldx ≡ x + 3y > 0 sp(x := E, pre) ≡ x = E[oldx/x] && pre [oldx/x] where oldx is a fresh variable representing the old value of x; pre[oldx/x] first renames x to oldx to avoid conflict.

Hoare Logic Rules: Sequence Example: wp(x := x + 1; y := x + y, y > 5) ≡ wp(x := x+1, wp(y:=x+y, y>5)) ≡ wp(x := x+1, x+y>5) ≡ x+y > 4 wp(program1; program2, post) ≡ wp(program1, wp(program2, post)) We first get the weakest pre-condition of program2 with respect to post and then use that the post-condition for program1.

Exercise 2 What is wp(x := 3*y + z; x = 5, x * y - z > 0)?

Hoare Logic Rules: Sequence Example: sp(x := x + 1; y := x + y, y > 5) ≡ ??? sp(program1; program2, pre) ≡ sp(program2, sp(program1, pre)) We first get the strongest post-condition of program1 and then use that the pre-condition for program2.

Hoare Logic Rules: Conditional Example: wp(if x > 0 then y := z else y := -z, y > 5) ≡ x > 0 => wp(y:=z, y>5) && x wp(y:=-z, y>5) ≡ x > 0 => z > 5 && x -z>5 wp(if B then Program1 else Program2; post) ≡ B => wp(program1, post) && !B => wp(program2, post)

Hoare Logic Rules: Conditional Example: sp(if x > 0 then y := z else y := -z, y > 5) ≡ ??? sp(if B then Program1 else Program2; pre) ≡ sp(program1, B && pre) || sp(program2, !B && pre)

Hoare Logic Rules: Loops How do we find the following? wp( while (i < x) { f = f*i; i := i+1; }, f = x! ) This is the ONE step that can’t be automated. Similarly for calculating the strongest post-condition. This is the ONE step that can’t be automated. Similarly for calculating the strongest post-condition.

Hoare Logic Rules: Loops *** (1): the pre-condition satisfies the invariant (2): the invariant remains valid after executing the loop body when B is satisfied (3): the invariant and !B is strong enough to imply the post- condition {pre}while B do program{post} if there exists an invariant inv such that the following are satisfied: (1) pre => inv (2) {inv && B} program {inv} (3) inv && !B => post and the loop terminates Are we missing something?

inv Big View B!B post {inv && B}program{inv} pre one iteration

Example {length >= 0 && array.length = length} float result = 0.0; int i = 0 ; while (i < length) { result += array[i]; i++; } {result = sum(array[j] for all j in 0..length-1)} Apply strongest post-condition calculation {length >= 0 && array.length = length && i = 0 && result = 0.0} while (i < length) { result += array[i]; i++; } {result = sum(array[j] for all j in 0..length-1)}

Example: Finding Invariants Proof: Let inv be 0 <= i <= length and result = sum(array[j] for all j in 0..i-1); 1.inv is true right before the loop starts, i.e., length >= 0 && array.length = length && i = 0 && result = 0.0 => 0 <= i <= length and result = sum(array[j] for all j in 0..i-1) {length >= 0 && array.length = length && i = 0 && result = 0.0} while (i < length) { result += array[i]; i++; } {result = sum(array[j] for all j in 0..length-1)} How do we find a loop invariant?

Example: Finding Invariants Proof: 2.inv is a loop invariant, i.e., {0 <= i <= length and result = sum(array[j] for all j in 0..i-1) && i < length} result += array[i]; i++; {0 <= i <= length and result = sum(array[j] for all j in 0..i-1)} {length >= 0 && array.length = length && i = 0 && result = 0.0} while (i < length) { result += array[i]; i++; } {result = sum(array[j] for all j in 0..length-1)}

Example: Finding Invariants Proof: 3.inv and i >= length implies the post-condition, i.e., 0 = length => i = length and result = sum(array[j] for all j in 0..i-1) Hence, the program is partially correct. {length >= 0 && array.length = length && i = 0 && result = 0.0} while (i < length) { result += array[i]; i++; } {result = sum(array[j] for all j in 0..length-1)} We will prove termination later

Invariant Intuition For code without loops, we apply the Hoare triple rules to get the weakest pre-condition or the strongest post-condition. For code with loops, we are doing one proof of correctness for multiple loop iterations – Don’t know how many iterations there will be – Need our proof to cover all of them – The invariant expresses a general condition that is true for every execution, but is still strong enough to give us the post-condition we need.

Termination Find a variant function v such that: – v is an upper bound on the number of loops remaining – (inv && B) ⇒ v > 0: the variant function evaluates to a finite integer value greater than zero at the beginning of the loop – {inv && B && oldv=v} program {v < oldv}: the value of the variant function decreases each time the loop body executes.

Example: Finding Variant Variant function: length-i length-i is an upper bound on the number of iterations. It is positive initially. It decrease every time. Hence the algorithm is terminating and the Hoare triple holds. Finally! {length >= 0 && array.length = length && i = 0 && result = 0.0} while (i < length) { result += array[i]; i++; } {result = sum(array[j] for all j in 0..length-1)}

Exercise 3 Prove the following in 20 minutes static int gcd(int K, int M) { int k= K; int m = M; while(k!=m) { if (k > m) {k = k-m;} else {m = m-k;} } return k; } {K > 0 and M > 0} {the returned value is GCD of the inputs}

Proving Real Programs How do we apply this kind of proving to real- world programs with classes, inheritance, higher-order functions, etc.? – On source code level, transform a program to a form which has only simple constructs like above. – Or work on the assembly code.

PROVING PROGRAM TERMINATION Byron Cook et al. Communications of the ACM 2011

Undeciability the halting problem “The problem is undecidable.” (1936) “Forget about it then.” “But that’s like the termination problem.” “Proving termination is not always impossible.”

The problem The Halting Problem: “using only a finite amount of time, determine whether a given program will always finish running or could execute forever.” The Halting Problem: “using only a finite amount of time, determine whether a given program will always finish running or could execute forever or answer unknown otherwise – the less unknown the better.”

Turing’s Method Find a ranking function f, which maps a program state to the domain of a well- founded relation – By definition, there is no infinite descending chain in a well-founded relation. Show that the “rank” decrease according to the relation along every possible step of the algorithm.

Example Assume that x and y are integers in math. How do we prove that this loop terminates? x = input(); y = input(); while (x > 0 and y > 0) do { if (input() == 1) { x = x-1; y = y+1; } else { y = y-1; }

Example Proof: Let f be 2x+y. The set of (bounded) integer forms a well-founded relation. At the beginning of the loop: 2x+y is some positive integer (since x >0 and y > 0). The following Hoare triple holds. {2x+y = V} if (input() = 1) { x = x-1; y = y+1; } else { y = y-1; } {2x+y = V’ && V’ < V} Finish the proof by showing the Hoare triple holds.

Good News: Every loop that terminates has a ranking function. Bad News: We don’t know if a loop is terminating or not. For some loops, the ranking function could be complicated. How do we go about searching for this ranking function?

Exercise 4: Terminating? while (x > 0) { x--; } while (x > 0 || y > 0) { x--; y--; } while (x > 0) { x := x-y; y++; } while (x > 0) { y=x; while (y>0) { y--; } while (x > 0) { y=x; while (y>0) { y--; } x--; } while (x > 0) { y=x; x--; while (y>0) { y--; } if (x == 300) { y=20; } Assume x >= 0 and y >= 0

Example Intuitively, is this loop always terminating? And Why? What is the ranking function? No function into the natural numbers exists that suffices to prove termination. x = input(); y = input(); while (x > 0 and y > 0) do { if (input() = 1) { x = x-1; y = input(); } else { y = y-1; }

Disjunctive Termination Arguments One Ranking Function For 60+ years, people have been devoted to methods on finding one ranking function automatically. Hard to find in many cases. Once found, it is easy to check the validity of the argument. Multiple Ranking Functions Recent trend Easier to find, because it can be expressed in small, mutually independent pieces. It is much more difficult to validate.

Example Disjunctive Termination Argument (two ranking functions: x and y): “x goes down by at least 1 and is larger than 0 OR y goes down by at least 1 and is larger than 0” x = input(); y = input(); while (x > 0 and y > 0) do { if (input() = 1) { x = x-1; y = input(); } else { y = y-1; } How do we use this argument to prove termination?

Disjunctive Termination Argument It is not sufficient to prove that the rank decreases through one iteration. Theorem: If every ranking function maps a program state to the domain of a well-founded relation and the rank decreases through all possible unrolling of the loop, then the loop terminates.

Example The following is true for every one iteration: “x goes down by at least 1 and is larger than 0 OR y goes down by at least 1 and is larger than 0” x = input(); y = input(); while (x > 0 and y > 0) do { if (input() = 1) { x = x-1; y = y+1; } else { x = x+1 y = y-1; } Is this terminating?

Intuition One ranking function: Since the “rank” decreasing every time, eventually it will stop and the loop terminates. Multiple ranking functions: Every iteration at least one “rank” decrease, but some other rank might increase and the decrement might be un-done later. If we show that at least one “rank” decrease for any arbitrary number of iterations, then the loop terminates. How do we show that something holds through arbitrary number of iterations?

Termination as Assertion Checking copied := false x = input(); y = input(); while (x > 0 and y > 0) do { if (copied) { assert((x 0 ) || (y 0 )) } else if (input() == 1) { copied = true; oldx = x; oldy = y; } if (input() = 1) { x = x-1; y = y+1; } else { x = x+1; y = y-1; } } Would you agree that if the assertion is always true, we proved that x or y is decreasing through arbitrary number of iterations?

Example: Assertion Checking If we know that the assertion is true, then it is implied that y >= 1 is true (at that location) through arbitrary number if iterations. if (y >= 1) { while (x >0) { assert(y >= 1); x = x – y; } Bad News: Assertion checking in general is undecidable. Good News: There are tools for assertion checking. ***We will discuss how to do assertion checking in later classes.

State-Of-the-Art Many tools for proving termination – Terminator, T2, ARMC, Aprove, etc. Latest empirical study: – 449 benchmarks (including Windows device drivers, the Apache web server, the PostgreSQL server, etc.) ranging from hundreds LOC to 30K LOC. – 260 are known to be terminating; 181 non- terminating;

Empirical Study

Summary Termination checking is not always impossible. It is not always possible either (see examples soon). So far we are able to handle programs of size 30K LOC or less. Serious researchers are needed in this area.

Further Challenges c = head while (c != null) { if (c.next != null && c.next.data == 5) { c.next = c.next.next; } c = c.next } How would you argue that the program is terminating or not? How would you know whether the linked list is acyclic or not? There are no integers here. How would you find the function automatically?

Integers are NOT “Integers” x := 10 while x > 9 do { x := x – 2^32; } x := 10 while x > 0 do { x := x + 2; } Are these terminating?

Source Code vs Binary Code Java Programs Bytecode JVM Physical Machine Is it terminating here?

Concurrency + Termination while x > 0 { x := x-1; lock(mu); b := x; unlock(mu); } Thread 1 while y > 0 { lock(mu); y := b; unlock(mu); } Thread 2 How do we prove both threads are terminating?

Collatz Program while (x > 1) { if (x is divisible by 2) { x := x / 2; } else { x := 3x+1; } Is this program always terminating or not? If you solve it, you will be well-known. Is this program always terminating or not? If you solve it, you will be well-known.

Exercise 5 x = input(); while (x >= 0) { y := 1; while (y < x) { y = 2*y; } x = x – 1; } Show the above is terminating.