automatic discovery of software contract work in progress yishai feldman, leon gendler
TRANSCRIPT
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.