The KeY Platform for Verification and Analysis of Java Programs
Reiner H¨ ahnle
Technische Universit¨ at Darmstadt Department of Computer Science, Software Engineering Group
25 April 2016
The KeY Platform for Verification and Analysis of Java Programs - - PowerPoint PPT Presentation
The KeY Platform for Verification and Analysis of Java Programs Reiner H ahnle Technische Universit at Darmstadt Department of Computer Science, Software Engineering Group 25 April 2016 Joint Work with. . . Wolfgang Ahrendt, Bernhard
Reiner H¨ ahnle
Technische Universit¨ at Darmstadt Department of Computer Science, Software Engineering Group
25 April 2016
Wolfgang Ahrendt, Bernhard Beckert, Richard Bubel, Christoph Gladisch, Daniel Grahl, Sarah Grebing, Martin Hentschel, Mihai Herda, Vladimir Klebanov, Wojciech Mostowski, Christoph Scheben, Peter H. Schmitt, Mattias Ulbrich, Nathan Wasser and many others over the last 12 years!
Target Languages
◮ sequential Java ◮ without floats, reflexion, lambdas
Properties
◮ functional correctness ◮ framing ◮ information flow ◮ resource consumption
Symbolic Execution Engine for Dynamic Logic
Threorem Proving Debugging Visualization Counter Examples Test Cases
The Ke Y Approach
Theorem Prover Proof Obligation Generator DL Formula
Specification Program File.java
The Ke Y Approach
Theorem Prover Proof Obligation Generator DL Formula
Specification Program File.java
Program Specification
Follows design-by-contract methodology
◮ Behavior of programs specified on level of classes and methods ◮ Contracts are used to specify methods
◮ precondition must be established by caller ◮ postcondition guaranteed by callee if precondition holds at
invocation time
◮ Invariants attached to classes to specify
◮ global system properties ◮ data consistency properties
A Specification Language for Java
Sum and Maximum (VSComp 2010)
◮ Description: Given an N-element array of natural numbers,
write a program to compute the sum and the maximum of the elements in the array.
◮ Properties: Given that N ≥ 0 and a[i] ≥ 0 for 0 ≤ i < N,
prove the post-condition that sum ≤ N · max.
❝❧❛ss SumAndMax { ✐♥t sum; ✐♥t max; /*@ normal_behaviour @ r❡q✉✐r❡s (❭❢♦r❛❧❧ ✐♥t i; 0 <= i && i < a.length; 0 <= a[i]); @ ❛ss✐❣♥❛❜❧❡ sum, max; @ ❡♥s✉r❡s (❭❢♦r❛❧❧ ✐♥t i; 0 <= i && i < a.length; a[i] <= max); @ ❡♥s✉r❡s (❭❡①✐sts ✐♥t i; 0 <= i && i < a.length; max == a[i]); @ ❡♥s✉r❡s sum == (❭s✉♠ ✐♥t i; 0 <= i && i < a.length; a[i]); @ ❡♥s✉r❡s sum <= a.length * max; @*/ ✈♦✐❞ sumAndMax(✐♥t[] a) { ... } }
The Ke Y Approach
Theorem Prover Proof Obligation Generator DL Formula
Specification Program File.java
FOL ∃x.φ
Modal logic ∃x.♦φ
Multi-modal logic ∃x.♦∗♥φ
Dynamic logic ∃x.x < 0? y := −x ; x ≥ 0? y := x y ≥ 0
Java DL for (Integer i: c) y+= i*i; y ≥ 0
Java DL with updates ∃s : java.util.Set. {c := s} [for (Integer i: c) y+= i*i;] y ≥ 0
Java DL with updates ∃s : java.util.Set. {c := s} [for (Integer i: c) y+= i*i;] y ≥ 0 Relative partial correctness:
◮ Upφ: If program p is started in any state that validates U
and it terminates, then formula φ holds in the final state.
The Ke Y Approach
Theorem Prover Proof Obligation Generator DL Formula
Specification Program File.java
Analysis Technique: Sequent Calculus realizes symbolic interpreter
Analysis Technique: Sequent Calculus realizes symbolic interpreter
ifStatement
Γ, b . = true = ⇒ [p; rest]φ, ∆ Γ, b . = false = ⇒ [q; rest]φ, ∆ Γ = ⇒ [if (b) { p } else { q }; rest]φ, ∆
unwindLoop
Γ = ⇒ [ if(b) {p; while(b) p} rest]φ, ∆ Γ = ⇒ [ while(b) p rest]φ, ∆
public static int sum(int[] a) throws Exception { if (a == null) { throw new Exception(); } else { int sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i]; } return sum; } }
if (a == null) a: a0 public static int sum(int[] a) throws Exception { if (a == null) { throw new Exception(); } else { int sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i]; } return sum; } }
if (a == null) a: a0 throw new Exception(); a: a0 {a0 = null} a0 = null public static int sum(int[] a) throws Exception { if (a == null) { throw new Exception(); } else { int sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i]; } return sum; } }
if (a == null) a: a0 throw new Exception(); a: a0 {a0 = null} int sum = 0; a: a0 {a0 ≠ null} a0 = null a0 ≠ null public static int sum(int[] a) throws Exception { if (a == null) { throw new Exception(); } else { int sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i]; } return sum; } }
if (a == null) a: a0 throw new Exception(); a: a0 {a0 = null} int sum = 0; a: a0 {a0 ≠ null} int i = 0; sum: 0 a0 = null a0 ≠ null public static int sum(int[] a) throws Exception { if (a == null) { throw new Exception(); } else { int sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i]; } return sum; } }
if (a == null) a: a0 throw new Exception(); a: a0 {a0 = null} int sum = 0; a: a0 {a0 ≠ null} int i = 0; sum: 0 i < a.length; i: 0 a0 = null a0 ≠ null public static int sum(int[] a) throws Exception { if (a == null) { throw new Exception(); } else { int sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i]; } return sum; } }
if (a == null) a: a0 throw new Exception(); a: a0 {a0 = null} int sum = 0; a: a0 {a0 ≠ null} int i = 0; sum: 0 i < a.length; i: 0 return sum; a.length: 0 a0 = null a0 ≠ null a0.length = 0 public static int sum(int[] a) throws Exception { if (a == null) { throw new Exception(); } else { int sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i]; } return sum; } }
if (a == null) a: a0 throw new Exception(); a: a0 {a0 = null} int sum = 0; a: a0 {a0 ≠ null} int i = 0; sum: 0 i < a.length; i: 0 return sum; a.length: 0 a0 = null a0 ≠ null a0.length = 0 a0.length > 0 sum += a[i]; a.length: len0 {len0 > 0} public static int sum(int[] a) throws Exception { if (a == null) { throw new Exception(); } else { int sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i]; } return sum; } }
public static int sum(int[] a) throws Exception { if (a == null) { throw new Exception(); } else { int sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i]; } return sum; } } sum += a[i]; a: a0 {a0 ≠ null} a.length: len0 {len0 > 0} i: 0 sum: 0
public static int sum(int[] a) throws Exception { if (a == null) { throw new Exception(); } else { int sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i]; } return sum; } } i++; sum: a0[0] sum += a[i]; a: a0 {a0 ≠ null} a.length: len0 {len0 > 0} i: 0 sum: 0
public static int sum(int[] a) throws Exception { if (a == null) { throw new Exception(); } else { int sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i]; } return sum; } } i++; sum: a0[0] i < a.length; i: 1 sum += a[i]; a: a0 {a0 ≠ null} a.length: len0 {len0 > 0} i: 0 sum: 0
public static int sum(int[] a) throws Exception { if (a == null) { throw new Exception(); } else { int sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i]; } return sum; } } i++; sum: a0[0] i < a.length; i: 1 return sum; a.length: 1 a0.length = 1 sum += a[i]; a: a0 {a0 ≠ null} a.length: len0 {len0 > 0} i: 0 sum: 0
public static int sum(int[] a) throws Exception { if (a == null) { throw new Exception(); } else { int sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i]; } return sum; } } i++; sum: a0[0] i < a.length; i: 1 return sum; a.length: 1 sum += a[i]; a.length: len0 {len0 > 1} a0.length = 1 a0.length > 1 sum += a[i]; a: a0 {a0 ≠ null} a.length: len0 {len0 > 0} i: 0 sum: 0
public static int sum(int[] a) throws Exception { if (a == null) { throw new Exception(); } else { int sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i]; } return sum; } } i++; sum: a0[0] i < a.length; i: 1 return sum; a.length: 1 sum += a[i]; a.length: len0 {len0 > 1} a0.length = 1 a0.length > 1 sum += a[i]; a: a0 {a0 ≠ null} a.length: len0 {len0 > 0} i: 0 sum: 0
Analysis Technique: Sequent Calculus realizes symbolic interpreter
ifStatement
Γ, b . = true = ⇒ [p; rest]φ, ∆ Γ, b . = false = ⇒ [q; rest]φ, ∆ Γ = ⇒ [if (b) { p } else { q }; rest]φ, ∆
unwindLoop
Γ = ⇒ [ if(b) {p; while(b) p} rest]φ, ∆ Γ = ⇒ [ while(b) p rest]φ, ∆ How to achieve finite proof tree for unbounded loops?
Analysis Technique: Sequent Calculus realizes symbolic interpreter
ifStatement
Γ, b . = true = ⇒ [p; rest]φ, ∆ Γ, b . = false = ⇒ [q; rest]φ, ∆ Γ = ⇒ [if (b) { p } else { q }; rest]φ, ∆
unwindLoop
Γ = ⇒ [ if(b) {p; while(b) p} rest]φ, ∆ Γ = ⇒ [ while(b) p rest]φ, ∆ How to achieve finite proof tree for unbounded loops? loopInvariant Γ = ⇒ Inv, ∆ (initially valid) Inv, b . = true = ⇒ [p]Inv (preserved) Inv, b . = false = ⇒ [rest]φ (use case) Γ = ⇒ [ while(b){ p } rest]φ, ∆
Deductive verification for general behavioral properties of “algorithmic challenging” programs requires
◮ interactions, and is hence, ◮ time (and cost) expensive
st❛t✐❝ ✈♦✐❞ ✐♥t st❛t✐❝ ✐♥t ✐♥t ✐♥t
Deductive verification for general behavioral properties of “algorithmic challenging” programs requires
◮ interactions, and is hence, ◮ time (and cost) expensive
Correctness of library functions is crucial: used in millions of programs
Java standard library functions
◮ Sorting a given array a
st❛t✐❝ ✈♦✐❞ sort(✐♥t[] a)
◮ Search a value key in the array a
st❛t✐❝ ✐♥t binarySearch(✐♥t[] a, ✐♥t key)
Description
Timsort: a hybrid sorting algorithm (insertion sort + merge sort)
Description
Timsort: a hybrid sorting algorithm (insertion sort + merge sort)
Facts
◮ Designed by Tim Peters (for Python) ◮ Since Java 7 default algorithm for non-primitive arrays
Description
Timsort: a hybrid sorting algorithm (insertion sort + merge sort)
Facts
◮ Designed by Tim Peters (for Python) ◮ Since Java 7 default algorithm for non-primitive arrays
Timsort is used in
◮ Java (standard library) ◮ Python (standard library) ◮ Android (standard library) ◮ ... and many more languages / frameworks!
Description
Timsort: a hybrid sorting algorithm (insertion sort + merge sort)
Facts
◮ Designed by Tim Peters (for Python) ◮ Since Java 7 default algorithm for non-primitive arrays
Timsort is used in
◮ Java (standard library) ◮ Python (standard library) ◮ Android (standard library) ◮ ... and many more languages / frameworks! Exception in thread "main" ArrayIndexOutOfBoundsException: 40 at java.util.TimSort.pushRun(TimSort.java:386) at java.util.TimSort.sort(TimSort.java:213) at java.util.Arrays.sort(Arrays.java:659) at TestTimSort.main(TestTimSort.java:18)
◮ Add a test to one method (mergeCollapse) to ensure that
invariant holds
◮ Formally specify the invariant as class invariant ◮ Specify contracts for all methods ◮ Specify loop invariants ◮ Verify that each method
◮ satisfies its contract ◮ preserves the class invariant
◮ Adding support for bitwise operations to KeY ◮ Adding capability to merge symbolic execution nodes ◮ 460 lines of specification vs. 928 lines of code ◮ Fixed version formally verified (in contrast to previous ”fixes”) ◮ Not yet proven:
◮ sortedness & permutation
◮ Effort
◮ ca. 3 Mio. rule applications ◮ ca. 5,000 interactions
◮ Java community fixed the bug by increasing stack size
(based on pen-and-paper worst case analysis)
◮ Java community fixed the bug by increasing stack size
(based on pen-and-paper worst case analysis)
◮ Android community provided their own fix
(which we proved in the meantime as well)
◮ Java community fixed the bug by increasing stack size
(based on pen-and-paper worst case analysis)
◮ Android community provided their own fix
(which we proved in the meantime as well)
◮ Python community adopted our formally proven fix
◮ Java community fixed the bug by increasing stack size
(based on pen-and-paper worst case analysis)
◮ Android community provided their own fix
(which we proved in the meantime as well)
◮ Python community adopted our formally proven fix ◮ Bug affected
◮ Java, Android, Python ◮ Apache: Lucene, Hadoop, Spark++ ◮ Go, D, Haskell
Language
Android 65.536 (216) Java 67.108.864 (226) Python 562.949.953.421.312 (249)
◮ Java community fixed the bug by increasing stack size
(based on pen-and-paper worst case analysis)
◮ Android community provided their own fix
(which we proved in the meantime as well)
◮ Python community adopted our formally proven fix ◮ Bug affected
◮ Java, Android, Python ◮ Apache: Lucene, Hadoop, Spark++ ◮ Go, D, Haskell
Language
Android 65.536 (216) Java 67.108.864 (226) Python 562.949.953.421.312 (249)
◮ Formal verification works for ”real” languages! ◮ Hundreds of thousands page views, top news on ycombinator,
reddit, Hacker News etc.
◮ Java community fixed the bug by increasing stack size
(based on pen-and-paper worst case analysis)
◮ Android community provided their own fix
(which we proved in the meantime as well)
◮ Python community adopted our formally proven fix ◮ Bug affected
◮ Java, Android, Python ◮ Apache: Lucene, Hadoop, Spark++ ◮ Go, D, Haskell
Language
Android 65.536 (216) Java 67.108.864 (226) Python 562.949.953.421.312 (249)
◮ Formal verification works for ”real” languages! ◮ Hundreds of thousands page views, top news on ycombinator,
reddit, Hacker News etc. W e l l , w
l d y
l
a t t h a t . K e Y i s a c
u a l l y u s e d f
s
e t h i n g u s e f u l . I t h
g h t t h e y j u s t t
t u r e d u s w i t h i t f
f u n a t u n i
e r s i t y . U n k n
n s t u d e n t v i a r e d d i t
◮ Java community fixed the bug by increasing stack size
(based on pen-and-paper worst case analysis)
◮ Android community provided their own fix
(which we proved in the meantime as well)
◮ Python community adopted our formally proven fix ◮ Bug affected
◮ Java, Android, Python ◮ Apache: Lucene, Hadoop, Spark++ ◮ Go, D, Haskell
Language
Android 65.536 (216) Java 67.108.864 (226) Python 562.949.953.421.312 (249)
◮ Formal verification works for ”real” languages! ◮ Hundreds of thousands page views, top news on ycombinator,
reddit, Hacker News etc. W e l l , w
l d y
l
a t t h a t . K e Y i s a c
u a l l y u s e d f
s
e t h i n g u s e f u l . I t h
g h t t h e y j u s t t
t u r e d u s w i t h i t f
f u n a t u n i
e r s i t y . U n k n
n s t u d e n t v i a r e d d i t Some researchers found an error in the logic of merge collapse, explained here, and with corrected code shown in . . . It should be fixed anyway, and their sug- gested fix looks good to me. Tim Peters via Python-Bugtracker
◮ Java community fixed the bug by increasing stack size
(based on pen-and-paper worst case analysis)
◮ Android community provided their own fix
(which we proved in the meantime as well)
◮ Python community adopted our formally proven fix ◮ Bug affected
◮ Java, Android, Python ◮ Apache: Lucene, Hadoop, Spark++ ◮ Go, D, Haskell
Language
Android 65.536 (216) Java 67.108.864 (226) Python 562.949.953.421.312 (249)
◮ Formal verification works for ”real” languages! ◮ Hundreds of thousands page views, top news on ycombinator,
reddit, Hacker News etc. W e l l , w
l d y
l
a t t h a t . K e Y i s a c
u a l l y u s e d f
s
e t h i n g u s e f u l . I t h
g h t t h e y j u s t t
t u r e d u s w i t h i t f
f u n a t u n i
e r s i t y . U n k n
n s t u d e n t v i a r e d d i t Some researchers found an error in the logic of merge collapse, explained here, and with corrected code shown in . . . It should be fixed anyway, and their sug- gested fix looks good to me. Tim Peters via Python-Bugtracker Congratulations to Stijn de Gouw et al. for finding and fixing a bug in TimSort using formal methods! Joshua Bloch via Twitter