430 likes | 519 Views
Cooperative Reasoning for Automatic Software Verification. Andrew Ireland School of Mathematical & Computer Sciences Heriot-Watt University Edinburgh. Outline. Cooperative reasoning: Tight integration of complementary techniques Compensating for each other’s weaknesses
E N D
Cooperative Reasoning for Automatic Software Verification Andrew Ireland School of Mathematical & Computer Sciences Heriot-Watt University Edinburgh
Outline • Cooperative reasoning: • Tight integration of complementary techniques • Compensating for each other’s weaknesses • Proof planning & cooperative reasoning: • Decouples proof search & proof checking • Promotes a flexible style of proof discovery • Case studies: • Property based verification • Functional verification
Proof Planning Theory Conjectures Proof planning Methods + Critics Proof checking Tactics Proof plans promote reuse, flexibilityand cooperation
Flexibility? • Conjecture: • ∀l:list(A).rotate(length(l),l)=l • Speculative generalization: • ∀l,a:list(A). • rotate(length(l),F1(l,a))= F2(l,a) • Generalized conjecture: • ∀l,a:list(A). rotate(length(l),app(l,a))=app(a,l) • Proof planning promotes the productive use of failure, • and in particular middle-out reasoning (MOR)
The SPARK Approach • A subset of Ada that eliminates potential ambiguities and insecurities (PRAXIS) • Expressive enough for industrial applications, but restrictive enough to support rigorous analysis • SPARK toolset supports data & information flow analysis and formal verification • Partial correctness & exception freedom proof, e.g. proving code is free from buffer overflows, range violations, division by zero. • Avionics, transportation, air traffic control, security,…
The SPARK Approach SPARK code Proofs SPARK Examiner VCs SPADE Simplifier UnprovenVCs Cmds SPADE Proof Checker
A Filter Example subtype AR_T is Integer range 0..9; type A_T is array (AR_T) of Integer; ... procedure Filter(A: in A_T; R: out Integer) is begin R:=0; for I in AR_T loop if A(I)>=0 and A(I)<=100 then R:=R+A(I); end if; end loop; end Filter; R:=R+A(I); If R <= Integer’Last then potential overflow exception
Loop Invariant subtype AR_T is Integer range 0..9; type A_T is array (AR_T) of Integer; ... procedure Filter(A: in A_T; R: out Integer) is begin R:=0; for I in AR_T loop --# assert R >= 0 and R <= I*100 if A(I)>=0 and A(I)<=100 then R:=R+A(I); end if; end loop; end Filter;
The SPARK Approach SPARK code Proofs SPARK Examiner VCs SPADE Simplifier UnprovenVCs Cmds SPADE Proof Checker
NuSPADE Project SPARK code Proofs SPARK Examiner VCs SPADE Simplifier UnprovenVCs Cmds SPADEase SPADE Proof Checker Refinement • Bill J. Ellis • EPSRC Critical Systems programme (GR/R24081) • EPSRC RAIS Scheme (GR/T11289) • http://www.macs.hw.ac.uk/nuspade
SPADEase Proof Planning Program Analysis • Proof planning: • automates invariant & exception freedom proofs • supports equational reasoning • generates program property schemas (invariants) • Program analysis: • instantiates program property schemas (invariants) • generates equational reasoning goals
Loop Invariant VC H1: r >= 0 . H2: r <= loop__1__i * 100 . ... H6: element(a, [loop__1__i]) >= 0 . H7: element(a, [loop__1__i]) <= 100 . … -> C1: r + element(a,[loop__1__i]) >= 0 . C2: r + element(a,[loop__1__i]) <= (loop__1__i + 1) * 100 . …
Loop Invariant VC H1: r >= 0 . H2: r <= loop__1__i * 100 . ... H6: element(a, [loop__1__i]) >= 0 . H7: element(a, [loop__1__i]) <= 100 . … -> C1: r + element(a,[loop__1__i]) >= 0 . C2: r + element(a,[loop__1__i])<=(loop__1__i+ 1)* 100 . … Rippling
Rippling Proof Pattern Given: ∀u’. f(g(v), h(u’)) f(g(c1(v)), h(u)) Goal:
Rippling Proof Pattern Given: ∀u’. f(g(v), h(u’)) f(g(c1(v)), h(u)) Goal: c2(f(g(v),h(c3(u)))) Rippling = difference identification + reduction
Separation Logic • John Reynolds (CMU) and Peter O’Hearn (QMU) • Simplifies pointer program proofs by enriching Hoare logic with spatial operators • Builds upon the logic of bunched implications (O’Hearn & Pym), and early work by Burstall • Focuses the reasoning effort on only those parts of the heap that are relevant to a program unit, i.e. local reasoning
Modelling the Heap • Empty heap: the assertion emp holds for a heap that contains no cells • Singleton heap:the assertion X ↦ Eholds for a heap that contains a single cell (maps-to relation), e.g. i 5 (i ↦ 5)
Separating Conjunction P*Q holds for a heap if the heap can be divided into two disjoint heaplets H1 and H2, such that P holds for H1 holds forQ holds for H2, e.g. i 5 i 5 7 7 (i↦5)*(i+1↦7) (i↦5)*(i+1↦7) *true (i↦5,7) (i↪5,7)
Separating Implication * P Q asserts that, if the current heap H1 is extended with a disjoint heaplet H2 in which P holds, then Q will hold in the extended heap, e.g. i 5 i 7 * Q (i↦5,7) Q
Singly-linked Lists list([],Y,Z)↔ emp Y=Z list([W|X],Y,Z)↔(∃p.(Y ↦W,p)*list(X,p,Z)) i a2 a1 an … j list([a1, a2, …, an],i,j)
Loop Invariant Discovery R:{∃A,B.list(A,i,nil)*list(B,j,nil)∧ A0 = app(rev(B),A)} {∃A. list(A,i,nil)∧ A0 = A} j := nil; {R} while not(i = nil) loop k := [i+1]; [i+1] := j; j := i; i := k end loop {∃B.list(B,j,nil) ∧ A0 = rev(B)}
Loop Invariant Discovery R:{∃A,B.list(A,i,nil)*list(B,j,nil)∧A0 = app(rev(B),A)} {∃A. list(A,i,nil)∧ A0 = A} j := nil; {R} while not(i = nil) loop k := [i+1]; [i+1] := j; j := i; i := k end loop {∃B.list(B,j,nil) ∧ A0 = rev(B)} shape
Loop Invariant Discovery R:{∃A,B.list(A,i,nil)*list(B,j,nil)∧A0 = app(rev(B),A)} {∃A. list(A,i,nil)∧ A0 = A} j := nil; {R} while not(i = nil) loop k := [i+1]; [i+1] := j; j := i; i := k end loop {∃B.list(B,j,nil) ∧ A0 = rev(B)} structural
Loop Invariant Discovery R:{∃A,B.list(A,i,nil)*list(B,j,nil)∧A0 = app(rev(B),A)} {∃A. list(A,i,nil)∧ A0 = A} j := nil; {R} while not(i = nil) loop k := [i+1]; [i+1] := j; j := i; i := k end loop {∃B.list(B,j,nil) ∧ A0 = rev(B)} functional
Verification Condition (∃A’,B’.list(A’,i,nil)*list(B’,j,nil) ∧ A0 = app(rev(B’),A’)) ∧(i≠nil) ├ (∃X2.(∃X,Y.(i ↦ X,Y)*((i ↦ X,j) (∃A,B.list(A,X2,nil)*list(B,i,nil) ∧ A0 = app(rev(B),A)))) ∧(∃X1.(i ↪ X1,X2))) Case-splitting Mutating Rippling *
Given: Loop Invariant xn+1 xn+2 xw i … nil xn xn-1 x1 j … nil (∃A’,B’.list(A’,i,nil)*list(B’,j,nil) ∧ A0 = app(rev(B’),A’))∧(i≠nil)
Goal: Weakest Precondition x2 xn+1 xn+2 xw i … nil xn xn-1 x1 j … nil (∃X2.(∃X,Y.(i ↦ X,Y)*((i ↦ X,j) (∃A,B.list(A,X2,nil)*list(B,i,nil) ∧ A0 = app(rev(B),A))∧(∃X1.(i ↪ X1,X2))) *
Case-splitting & Mutating xn+1 xn+2 xw i … nil xn xn-1 x1 j … nil (∃A,Btl.list([xn+1|A],i,nil)*list(Btl,j,nil) ∧ A0 = app(rev([xn+1|Btl]),A))
Mutating Proof Pattern Goal: … (U ↦ V,W) (( … ) (U’ ↦ V’,W’) ( … )) * * *
Mutating Proof Pattern Goal: …(U ↦ V,W)(( … )(U’ ↦ V’,W’) ( … )) * * * …(U ↦ V,W)((U’ ↦ V’,W’) (( … ) (…))) * * * X (X Y) Y * * (rewrite rule) ((…) ( …)) * Mutating = reconcile complementary heaplets
Rippling (∃A’,B’.list(A’,i,nil)*list(B’,j,nil) ∧ A0 = app(rev(B’),A’)) ∧(i≠nil) ├ (∃A,Btl.list([xn+1|A],i,nil)*list(Btl,j,nil) ∧ A0 = app(rev([xn+1|Btl]),A))
Rippling (∃A’,B’.list(A’,i,nil)*list(B’,j,nil) ∧ A0 = app(rev(B’),A’)) ∧(i≠nil) ├ (∃A,Btl.list([xn+1|A],i,nil)*list(Btl,j,nil) ∧ A0 = app(rev([xn+1|Btl]),A))
Rippling (∃A’,B’.list(A’,i,nil)*list(B’,j,nil) ∧ A0 = app(rev(B’),A’)) ∧(i≠nil) ├ (∃A,Btl.list([xn+1|A],i,nil)*list(Btl,j,nil) ∧ A0 = app(rev([xn+1|Btl]),A)) (∃A,Btl.list([xn+1|A],i,nil)*list(Btl,j,nil) ∧ A0 = app(app(rev(Btl),[xn+1]),A))
Rippling (∃A’,B’.list(A’,i,nil)*list(B’,j,nil) ∧ A0 = app(rev(B’),A’)) ∧(i≠nil) ├ (∃A,Btl.list([xn+1|A],i,nil)*list(Btl,j,nil) ∧ A0 = app(rev([xn+1|Btl]),A)) (∃A,Btl.list([xn+1|A],i,nil)*list(Btl,j,nil) ∧ A0 = app(app(rev(Btl),[xn+1]),A)) (∃A,Btl.list([xn+1|A],i,nil)*list(Btl,j,nil) ∧ A0 = app(rev(Btl),app([xn+1],A)))
Rippling (∃A’,B’.list(A’,i,nil)*list(B’,j,nil) ∧ A0 = app(rev(B’),A’)) ∧(i≠nil) ├ (∃A,Btl.list([xn+1|A],i,nil)*list(Btl,j,nil) ∧ A0 = app(rev([xn+1|Btl]),A)) (∃A,Btl.list([xn+1|A],i,nil)*list(Btl,j,nil) ∧ A0 = app(app(rev(Btl),[xn+1]),A)) (∃A,Btl.list([xn+1|A],i,nil)*list(Btl,j,nil) ∧ A0 = app(rev(Btl),app([xn+1],A))) (∃A,Btl.list([xn+1|A],i,nil)*list(Btl,j,nil) ∧ A0 = app(rev(Btl),[xn+1|,A]))
CORE Project Spec & code CORE system animation Smallfoot (family) IsaPlanner Proofs Isabelle • Ewen Maclean, Gudmund Grov, Richard Addison • EPSRC (EP/F037597)http://www.macs.hw.ac.uk/core • Smallfoot (O’Hearn’s Theory Group) • IsaPlanner (Bundy’s Mathematical Reasoning Group)
CORE Project Shape Analysis Proof Planning Term Synthesis • Shape analysis automates: • checking of shape properties • shape invariant generation • Proof planning automates proof search: • structural properties • functional properties • Term synthesis automates invariant discovery – • ongoing work (alternative to MOR)
Invariant Discovery (Ongoing) ∃A,B. list(V1,i,nil)* list(V2,j,nil)∧A0 =F1(rev(B),A,B)
Invariant Discovery (Ongoing) ∃A,B. list(V1,i,nil)*list(V2,j,nil)∧A0 =F1(rev(B),A,B) Shape Shape invariants currently hand-crafted, but aiming for automation via Smallfoot family.
Invariant Discovery (Ongoing) ∃A,B. list(V1,i,nil)*list(V2,j,nil)∧A0 =F1(rev(B),A,B) Structural Structural invariants generated via MOR (proof planning within CORE System)
Invariant Discovery (Ongoing) ∃A,B. list(V1,i,nil)* list(V2,j,nil)∧A0 =F1(rev(B),A,B) Functional Functional invariants generated via term synthesis (CORE System) and proved via proof planning (IsaPlanner)
Conclusion • Successful software verification approaches are typically an integration of techniques • Argued for cooperative reasoning, i.e. a tight integration of complementary techniques • Focused on proof planning as a flexible approach to proof automation that promotes cooperative reasoning