Technologies for finding errors in object-oriented software K. Rustan M. Leino Microsoft Research, Redmond, WA Lecture 0 Summer school on Formal Models of Software 1 Sep 2003, Tunis, Tunisia Motivation Software development problem • Software construction and maintenance are expensive • Reliability is costly and difficult to achieve Vision • Increased programmer productivity and program reliability through increased rigor Record design decisions + Utilize automatic checking = Detect errors and improve maintainability User's view Program with specifications Error messages public class Bag { private /*@non_null*/ int[] a; private int n; //@ invariant 0 <= n && n <= a.length; public Bag(/*@non_null*/ int[] initialElements) { n = initialElements.length; a = new int[n]; System.arraycopy(initialElements, 0, a, 0, n); } public void add(int x) { if (n == a.length) { int[] b = new int[2*(a.length+1)]; System.arraycopy(a, 0, b, 0, n); a = b; } a[n] = x; n++; } Bag.java:18: Array index possibly too large Extended Static Checker for Java (ESC/Java) • Built at Compaq SRC • Input: Java + user-supplied annotations • Annotation language captures programmer design decisions • Powered by program semantics and automatic theorem proving • Performs modular checking ESC/Java demo Program checker design tradeoffs • • • • Missed errors Spurious warnings Annotation overhead Performance Tool architecture Annotated Java program Translator Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Valid Resource exhausted Tool architecture, detail Translator Annotated Java program Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Tool architecture, detail Annotated Java program Annotated Java program Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Translator Translator Sugared command Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Annotation language • Simple – non_null • Method annotations – – – – requires E; modifies w; ensures E; exsures (T x) E; • Object invariants – invariant E; Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Annotation language • Simple – non_null • Method annotations – – – – requires E; modifies w; ensures E; exsures (T x) E; • Object invariants – invariant E; Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Annotation language • Specification expressions – side-effect free Java expressions • no ++, no method calls – result, old(E) • ensures result == old(x); – ==> – (forall T x; E), (exists T x; E) • (forall int j; 0 <= j && j < n ==> a[j] > 0); – typeof(E), type(T), <: • requires typeof(x) == typeof(this); Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Annotation language • Specification expressions – side-effect free Java expressions • no ++, no method calls – result, old(E) • ensures result == old(x); – ==> – (forall T x; E), (exists T x; E) • (forall int j; 0 <= j && j < n ==> a[j] > 0); – typeof(E), type(T), <: • requires typeof(x) == typeof(this); Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Annotation language • Concurrency – monitored_by lock • /*@ monitored_by this */ long x; – lockset[lock] • requires lockset[this]; – lock0 < lock1 – max(lockset) • requires max(lockset) < this; Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Annotation language • Concurrency – monitored_by lock • /*@ monitored_by this */ long x; – lockset[lock] • requires lockset[this]; – lock0 < lock1 – max(lockset) • requires max(lockset) < this; Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Annotation language • Ghost variables – ghost public T x; • ghost public int objectState; • ghost public TYPE elementType; – set x = E; • set objectState = Open; • set elementType = type(T); Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Annotation language • Ghost variables – ghost public T x; • ghost public int objectState; • ghost public TYPE elementType; – set x = E; • set objectState = Open; • set elementType = type(T); Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Annotation language • Miscellaneous – assert E; – assume E; • assume x >= 0; // because x == y*y – nowarn • x = a[j]; //@ nowarn – axiom E; • axiom (forall int x; x ≫ 2 >= 0); Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Sugared commands • S,T ::= | | | | | | | | | assert E assume E x= E raise S;T S!T S [] T loop {inv E} S T end call x = t.m(E) … Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Sugared commands • x = t.f.g; assert t ≠ null; tmp = select(f, t); assert tmp ≠ null; x = select(g, tmp) • if (x < 0) { x = -x; } /*@ assert x >= 0; */ ( assume x < 0; x = -x [] assume ¬(x < 0) ); assert x >= 0 Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Sugared commands • x = t.f.g; assert lblneg(“Null@58.9”, t ≠ null); tmp = select(f, t); assert lblneg(“Null@58.11”, tmp ≠ null); x = select(g, tmp) • if (x < 0) { x = -x; } /*@ assert x >= 0; */ ( assume x < 0; assume lblpos(“Then^280:7”, true); x = -x [] assume ¬ (x < 0); assume lblpos(“Else^280:7”, true) ); assert x >= 0 Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Primitive commands • S,T ::= | | | | | | | | | assert E assume E x= E raise S;T S!T S [] T loop {inv E} S T end call x = t.m(E) … Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Primitive commands • //@ requires Pre; modifies w; ensures Post; X m(U u); • call x = t.m(E) var u in u = E; assert Pre; var w0 in w0 = w; havoc w; assume Post; x = result end end Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Passive commands • S,T ::= | | | | | | assert E assume E x= E raise S;T S!T S [] T Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Passive commands • if (x < 0) { x= -x; } /*@ assert x >= 0; */ ( assume x0 < 0; x1 = -x0; x2 = x1 [] assume ¬(x0 < 0); x2 = x0 ); assert x2 >= 0 Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Passive commands • if (x < 0) { x= -x; } /*@ assert x >= 0; */ ( assume x0 < 0; assume x1 == -x0; assume x2 == x1 [] assume ¬(x0 < 0); assume x2 == x0 ); assert x2 >= 0 Annotated Java program Translator Sugared command Primitive command Passive command Weakest preconditions • A Hoare triple {P} S {Q} says that if command S is started in a state satisfying P, then S terminates without error in a state satisfying Q Verification condition Automatic theorem prover Counterexample context Post processor Warning messages • The weakest precondition of a command S with respect to a postcondition Q, written wp(S, Q), is the weakest P such that {P} S {Q} Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Weakest preconditions • • • • wp(assert E, Q) = E && Q wp(assume E, Q) = E ==> Q wp(S;T, Q) = wp(S, wp(T,Q)) wp(S [] T, Q) = wp(S, Q) && wp(T, Q) • wp(S, Q) = wp(S, true) && wlp(S, Q) • wlp(S, Q) = wlp(S, false) || Q Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Verification condition • Universal background predicate – (∀t ・ t <: t) • Type-specific background predicate – Bag <: java.lang.Object Counterexample context Post processor Warning messages • Verification condition: BPUniv && BPT ==> VCmethod Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages (AND (<: T_T |T_java.lang.Object|) (EQ T_T (asChild T_T |T_java.lang.Object|)) (DISTINCT arrayType |T_boolean| |T_char| |T_byte| |T_short| |T_int| |T_long| |T_float| |T_double| |T_.TYPE| T_T |T_java.lang.Object|))) (EXPLIES (LBLNEG |vc.T.abs.2.2| (IMPLIES (AND (EQ |elems@pre| elems) (EQ elems (asElems elems)) (< (eClosedTime elems) alloc) (EQ LS (asLockSet LS)) (EQ |alloc@pre| alloc)) (NOT (AND (EQ |@true| (is |x:2.21| T_int)) (OR (AND (OR (AND (< |x:2.21| 0) (LBLPOS |trace.Then^0,3.15| (EQ |@true| |@true|)) (EQ |x:3.17| (- 0 |x:2.21|)) Verification condition • class T { static int abs(int x) { if (x < 0) { x = -x; } //@ assert x >= 0; } } Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Theorem prover: “Simplify” • Nelson-Oppen cooperating decision procedures – – – – conguence closure linear arithmetic partial orders quantifiers • Key features: – automatic: no user interaction – refutation based: searches for counterexamples – heuristics tuned for program checking – labels – time limit Annotated Java program Translator Sugared command Primitive command Passive command Verification condition Automatic theorem prover Counterexample context Post processor Warning messages Counterexamples and warnings • Counterexample: labels: (|IndexTooBig@26.5| |vc.Bag.add.20.2| |trace.Then^0,21.23|) context: (AND (NEQ |tmp1!a:23.23| null) (NEQ this null) (EQ |alloc@pre| alloc) (EQ |tmp4!n:26.6| 0) … (<= alloc (vAllocTime |tmp3!a:26.4|)) ) • Bag: add(int) ... -------------------------------------------------Bag.java:26: Warning: Array index possibly too large (IndexTooBig) a[n] = x; ^ Execution trace information: Executed then branch in "Bag.java", line 21, col 23. -------------------------------------------------- Experience: annotations • • • • • Capture common design decisions Suggested immediately by warnings Overhead: 4-10% of source code ~1 annotation per field or parameter Most common annotations: – non_null – container element types Experience: performance • 50% of all methods: • 80% of all methods: • time limit: < 0.5 s <1s 300 s • total time for Javafe (~40kloc): 65 min. Related work • ESC/Modula-3 • Full functional specification and verification – JML, LOOP, B, Penelope, ... • Languages and language features – Euclid, Eiffel, Escher, Guava, Vault, Cqual, ... – LCLint, refinement types, Types against races, ... • Other checking techniques – Abstract interpretation, PREfix, SLAM, Bandera, Java PathFinder 2, Canvas, ESP, AST Toolkit, Metal, … Conclusions • Using program semantics and automatic decision procedures for program analysis works! • Cost effective? What's needed? • Semantics of programming language • Specification language • Programming methodology and disciplines • Decision procedures • Property inference Download ESC/Java (tool, documentation, sources): http://research.compaq.com/SRC/esc http://research.compaq.com