Automatic Discovery of Automatic Discovery of Software ContractSoftware Contract
Work in progressWork in progress
Yishai Feldman, Leon Gendler
Design By ContractDesign By Contract
• Precondition• An obligation of the consumer (client)
• A set of assertions to be ensured before executing a program part
• Postcondition• An obligation of the provider (server)
• A set of conditions to be fulfilled after executing a program part
• Invariant• A set of conditions concerning the state of the class
• Ensured to hold before and after each class operation.
Design By ContractDesign By Contract
• Reliability• Avoid unexpected inputs and results
• Expresses and validates correctness arguments• Make sure you know what your code does
• Testing• Contract violation means a bug
• Documentation• Express the input and outcome of each class method
ContractContract
• A set of pre- and post conditions for each operation provided by a component and an invariant for the whole class.
• Ensuring the precondition when requesting an operation, guarantees the provider will make the postcondition true at its end.
Research PurposeResearch Purpose
• To find a proper contract for a given class by performing static program analysis.• The contract may not be complete, there may be pre/post
conditions that will not be found
• The contract will (?) be correct.
• Discover from the code itself the conditions for its correctness (preconditions)• No intention to verify the correctness of a given piece of code.
• Describe the code’s outcome and the claims about the object’s state at end of execution (postconditions).
ExampleExample
public synchronized boolean addAll(int index, Collection c) { if (index < 0 || < 0 || index > > elementCount) throw new
ArrayIndexOutOfBoundsException(index); int numNew = c.size();
ensureCapacityHelper(elementCount + numNew); int numMoved = elementCount - index; if (numMoved > 0)
System.arraycopy(elementData, index, elementData,
index + numNew, numMoved); Iterator e = c.iterator(); for (int i = 0; i < numNew; i++) elementData[index++] = e.next(); elementCount += numNew; return numNew != 0;}
// @inv: elementData != null // @inv: elementData != null // @inv: elementCount <= elementData.length()// @inv: elementCount <= elementData.length()
// @post: elementData.length() <= $pre(elementCount) + c.size()// @post: elementData.length() <= $pre(elementCount) + c.size()// @post: forall i, 0 <= i < c.size():// @post: forall i, 0 <= i < c.size():// $prev(elementData[index + i] =// $prev(elementData[index + i] =//// elementData[index + i + c.size()]elementData[index + i + c.size()]
// @pre: index >= 0// @pre: index >= 0// @pre: index <= elementCount// @pre: index <= elementCount// @pre: c != null// @pre: c != null
Verification ConditionVerification Condition
• A logical expression representing a program part.
• Proving the VC to be correct, proves the program part to be correct.
• Program verification:• Annotate program with assertions and loop invariants (manually).
• Build all VCs by traversing the annotated program.
• If the VC is correct then the program stands correct according to the given assertions.
Mechanizing Program VerificationMechanizing Program Verification
Specification to be proved
+ Loop Invariants Human expert
Annotated specificationVC Generator
Set of logic statements (VC’s)Theorem Prover
Simplified set of verif. cond.
End of proof
Human Expert
Hoare TripletsHoare Triplets
• {P} S {Q} - Starting at state {P} and performing S will bring us to state {Q}
• Weakest Precondition:• wp(S, Q): The weakest initial state {P} in which after performing
S will bring us to state {Q}
• WLP(S, Q): Doesn’t ensure termination
• Strongest Postcondition• SP(S, P): The strongest state {Q} which can be achieved starting
at {P} and performing S
Weakest? Strongest?Weakest? Strongest?
• A is stronger than B: A B
• A is weaker than B: B A
• The weakest condition: True
• The strongest condition: False
Precondition ComputationPrecondition Computation
• Use Weakest Precondition method to calculate preconditions.
• Start with the weakest condition : True
• Traverse the code bottom-up and push the condition through all program instructions.
• Each command can add to the precondition.
Weakest PreconditionWeakest Precondition
• wp(x := e, Post) = Post(x := e)• Every free occurrence of x in postcondition P is replaced by e.
• wp(S1;S2, Post) = wp(S1, wp(S2, Post))• Calculate the WP bottom up
• wp(if B then S1 else S2, Post) = B wp(S1, Post) B wp(S2, Post)
• wp(while B do S1, Post) = B Post B wp(while B do S1, wp(S1, Post))
• L(i) = B Post B wp(L(i-1))
Automatic Discovery of Automatic Discovery of Software ContractsSoftware Contracts
• Translate the source code into a structural representation (the Plan Calculus).
• Use a library of structural elements and their contract (assertions) to assign to each structural element its pre/post conditions.
• Propagate the assertions upwards and downwards through the plan to produce the preconditions and the postconditions. (V.C. generation)
• Add knowledge by applying heuristics of frequent code
• Simplify expressions
Looking for a contractLooking for a contract
• Practical• Something the programmer can work with
• A starting point for the programmer to check for possible bugs.
• Must contain relevant data• No local method variables
• Class members and input variables
• Must be correct, but ‘almost’ also counts
• Completeness is impossible
ImplementationImplementation
• Input: a Java class source code
• Translate the Java class into a plan representation• Code written in Java using Barat • Barat: A front-end for Java to support static analysis of Java
programs. Builds a complete abstract syntax tree from Java source code files, enriched with name and type analysis information.
• Generate a Lisp representation of a plan.
• Traverse the plan generating the contract.• Use predefined spec’s assertion and known methods’ library• Use ACL2 Theorem Prover to simplify generated logical
expressions and to check possible contract assumptions.
ACL2ACL2
• An automated reasoning system• Developed (for the first 9 years) at Computational Logic, Inc. and
(from January, 1997) at the University of Texas at Austin.
• The successor to the Nqthm (or Boyer-Moore) logic and proof system
• A Computational Logic for Applicative Common Lisp
• An automated theorem prover or proof checker• A competent user can utilize the ACL2 system to discover proofs
of theorems stated in the ACL2 logic or to check previously discovered proofs
WP ExampleWP Example
{(and (>= i 0) (< i 5))}
int j = i * 2;{(and (not (eq a nil)) (>= i 0) (< i (len a)))}
a[i] = j;{t}
int [] a = new int [5];{(let ((j (* 2 i))) (and (not (eq a nil)) (>= i 0) (< i (len a)))}
{(and (not (eq a nil)) (>= i 0) (< i (len a))), (not (eq a nil)) (eq (len a) 5))}
{(and (>= 5 0) (>= i 0) (< i 5))}
int foo (int i) { int [] a = new int[5]; int j = i*2; a[i] = j; ... }
Plan CalculusPlan Calculus
• A structural, high level representation
• Manipulation of local variables is represented as Data Flow
• Representation of Control Flow which can be parallel.
• Spec Types:• IO Spec
• Test Spec
• Join Spec
Condition StructureCondition Structure
Outer
Failure Success
Outer-wp
Join
Test
Success-wp
Failure-wp
Test Failure-wp Test Success-wp
negate-if-negativenegate-if-negative
<
JoinFS
TestFS
int abs (int x) { If (x < 0) { x = -x; } return x;}
-
x
LoopsLoops
• Traverse the loop several times until:• Either a fixed-point in the contract is reached
• Or a fixed number of iteration
• Usually, the first traversal contributes most of the information.
• Try to identify special behavior: well-known loop structures such as array or range iteration.
Loop structureLoop structure
feedback
body
outer
Body-wp
Feedback-wp
Outer-wp
Join
Test
For exampleFor example
TestFS
JoinFS
<
aset
++
i
i
i
i
i = initlimit
for (i=init; i<limit; i++) { ... a[i] = x ...} a
a
a
ax
Vector.addAll(int, collection)Vector.addAll(int, collection)
public synchronized boolean addAll(int index, Collection c) { if (index < 0 || index > elementCount) throw new ArrayIndexOutOfBoundsException(index); int numNew = c.size();
ensureCapacityHelper(elementCount + numNew); int numMoved = elementCount - index; if (numMoved > 0)
System.arraycopy(elementData, index, elementData,index + numNew, numMoved);
Iterator e = c.iterator();int limit = index + numNew;
for (int i = index; i < limit; i++) elementData[i] = e.next(); elementCount += numNew; return numNew != 0;}
Precondition of Vector.addAll(int, collection)Precondition of Vector.addAll(int, collection)
(AND (NOT (OR (< INDEX 0) (< ELEMENTCOUNT INDEX)))
C
(OR (NOT (< 0 (+ ELEMENTCOUNT (- INDEX))))
(AND (NOT (< (+ INDEX NUMNEW) 0))
ELEMENTDATA
(<= (+ INDEX ELEMENTCOUNT (- INDEX))
(LEN ELEMENTDATA))
(<= (+ INDEX NUMNEW ELEMENTCOUNT (- INDEX))
(LEN ELEMENTDATA))))) ...
(AND (>= INDEX 0) (>= ELEMENTCOUNT INDEX) ;; If (…) Throw {…}
C ;; c.size()
(IMPLIES (< 0 (- ELEMENTCOUNT INDEX)) ;; If (numMoved > 0)…
(AND (>= (+ INDEX NUMNEW) 0) ;; System.arrayCopy
ELEMENTDATA
(<= ELEMENTCOUNT (LEN ELEMENTDATA))
(<= (+ NUMNEW ELEMENTCOUNT) (LEN ELEMENTDATA)))))
Precondition of Vector.addAll(int, collection)Precondition of Vector.addAll(int, collection)
(AND (>= INDEX 0) (>= ELEMENTCOUNT INDEX)
C
(IMPLIES (< 0 (- ELEMENTCOUNT INDEX))
(AND (>= (+ INDEX NUMNEW) 0)
ELEMENTDATAELEMENTDATA
(<= ELEMENTCOUNT (LEN ELEMENTDATA))(<= ELEMENTCOUNT (LEN ELEMENTDATA))
(<= (+ NUMNEW ELEMENTCOUNT) (LEN ELEMENTDATA)))(<= (+ NUMNEW ELEMENTCOUNT) (LEN ELEMENTDATA)))))Class InvariantClass Invariant
Postcondition of ensureCapacityHelper(int capacity):Postcondition of ensureCapacityHelper(int capacity):
(LEN ELEMENTDATA) >= capacity(LEN ELEMENTDATA) >= capacity
Full PreconditionFull Precondition(AND
(NOT (OR (< INDEX 0) (< ELEMENTCOUNT INDEX)))
C
(OR (NOT (< 0 (+ ELEMENTCOUNT (- INDEX))))
(AND (NOT (< (+ INDEX NUMNEW) 0))
ELEMENTDATA
(<= (+ INDEX ELEMENTCOUNT (- INDEX))
(LEN ELEMENTDATA))
(<= (+ INDEX NUMNEW ELEMENTCOUNT (- INDEX))
(LEN ELEMENTDATA))
(OR (NOT (< INDEX LIMIT))(OR (NOT (< INDEX LIMIT))
(AND E (< 0 INDEX)(AND E (< 0 INDEX)
(< INDEX (LEN ELEMENTDATA))(< INDEX (LEN ELEMENTDATA))
(OR (NOT (< (+ 1 INDEX) LIMIT))(OR (NOT (< (+ 1 INDEX) LIMIT))
(< (+ 1 INDEX) (LEN ELEMENTDATA)))(< (+ 1 INDEX) (LEN ELEMENTDATA))))))))))) (COND ((< 0 (+ ELEMENTCOUNT (- INDEX))) T)
((< INDEX LIMIT)
(AND E ELEMENTDATA (< 0 INDEX)
(< INDEX (LEN ELEMENTDATA))
(OR (NOT (< (+ 1 INDEX) LIMIT))
(< (+ 1 INDEX) (LEN ELEMENTDATA)))))
(T T)))
Cleaning the resultCleaning the result
• The precondition may contain irrelevant data• internal implementation
• The precondition should contain only global and input variables.
Adding more knowledgeAdding more knowledge
• Serial traversal:• Most commonly used loop to traverse an array (any
serial traversal)
• Adding the calculation of the last loop traversal
• Add special information:For (i = init; i < limit; i++) {... NO BREAK ...}:
Assert init < limit "First execution" i=limit #=limit-init
Assert (init < limit) "No execution" i = init # = 0
Serial TraversalSerial Traversal
for(i = init; i < limit; i++) {
… a[i] = …
}
• Easy to find: 0 init < a.len• Better: a.length() limit
• Method:• Add i = limit (the last value of i) as a postcondition of the
loop.
• Compute the loop precondition based on:• L(k) - the postcondition computed so far• i = limit - the value of i on the last iteration
for(i=0; i<n; i++) {… a[i] …}for(i=0; i<n; i++) {… a[i] …}
• L(0) = outer, L(i) = p L(0) p wp(S,L(i-1))
• L(1) = in outer i<n wp(…a[i]…, wp(i++, L(0)))• L(1) = i n outer i < n 0 i < a.len
• L(2) = in outer i<n wp(…a[i]…;i++, L(1))• L(2) = in outer i<n wp(…a[i]…;i++, i n outer i < n
0 i < a.len)• L(2) = i n outer i = n-1 wp(outer) i < n 0 i <
a.len i+1 < n 0 i+1 < a.len
• L(k) = i n outer [… i=n-k wpk-1(outer) …] [i < n 0 i < a.len) … i+k < n 0 i+k-1 < a.len)]
• Wp(loop, i=0)• 0 < n 0 0 < a.len• 1 < n 0 1 < a.len• …
• k < n 0 k < a.len
• wp(…a[i];i++…, L(k) i = n)) (after loop exit) • wp(…a[i];i++…, wp(i++, L(k) i = n))
• wp(…a[i], L(k)i = i+1 i+1 = n
• 0 i < a.len L(k)i = i+1 i+1 = n
• 0 n-1 < a.len L(k)i = i+1
Computing PostconditionsComputing Postconditions
• Similar computation as in Preconditions.
• Possible improvement:• use the already computed preconditions as an initial
value
Class InvariantClass Invariant
• Finding candidates:• Assertions regarding class members
• Assertions appearing in more that one method’s preconditions.
• Assertions appearing in pre and post conditions
• Checking candidate assertions:• The assertion doesn’t effect contract of methods it is
not part of their contract.
Related ResearchRelated Research
• ESC/Java• Statically detects common errors such as null pointer references
• Programmer adds annotations (assertion statements)
• ESC/Java issues warnings about annotations which cannot be verified.
• Translates the program into V.C.s and tries to prove them.
• Houdini• Guesses a candidate set of annotations and uses ESC/Java to prove
them correct
Block diagramBlock diagram
ESC exampleESC example
class Bag {
int[] a;
int n;
Bag(int[] input) {
n = input.length;
a = new int[n];
System.arraycopy(input, 0, a, 0, n);
}
int extractMin() {
int m = Integer.MAX_VALUE;
int mindex = 0;
for (int i = 0; i < n; i++) {
if (a[i] < m) {
mindex = i;
m = a[i];
}
}
n--;
a[mindex] = a[n];
return m;
}
}
escjava Bag.javaescjava Bag.java
ESC exampleESC example
class Bag {
/*@ non_null */ int[] a; int n;
//@ invariant 0 <= n && n <= a.length;
//@ requires input != null; Bag(int[] input) {
n = input.length;
a = new int[n];
System.arraycopy(input, 0, a, 0, n);
}
//@ requires n >= 1; int extractMin() {
int m = Integer.MAX_VALUE;
int mindex = 0;
for (int i = 0; i < n; i++) {
if (a[i] < m) {
mindex = i;
m = a[i];
}
}
n--;
a[mindex] = a[n];
return m;
}
}
Blow-up from assignment ruleBlow-up from assignment rule
wp(x := e,Q) = Q(x e)
• Q(x e) may contain many copies of e
• Sequential composition of assignment statements
may yield exponentially large VC, e.g.
• wp( b=a+a ; c=b+b ; ... ; z=y+y, z>0)
ReferencesReferences
• Bertrand Meyer, Object-Oriented Software Construction, 2nd edition, Prentice-Hall, 1997.
• Charles Rich, Richard C. Waters, The Programmer's Apprentice, Addison-Wesley, November 1990.
• Matt Kaufmann, Panagiotis Manolios, and J Strother Moore, Computer-Aided Reasoning: An Approach, Kluwer Academic Publishers, June, 2000
• Edsger W. Dijkstra, Carel S. Scholten Predicate Calculus and Program Semantics, Springer-Verlag, July 1989.
• Richard C. Waters, "A Method for Analyzing Loop Programs", IEEE Transactions on Software Engineering, vol. 5 pp. 237-247, May 1979.
• Guy L. Steele Jr., Common Lisp: The Language, 2nd edition, Butterworth-Heinemann, December 1990.
• David L. Detlefs, K. Rustan M. Leino, Greg Nelson, and James B. Saxe. "Extended Static Checking". Research Report 159, Compaq Systems Research Center, December, 1998.