Automated Verification with HIP and SLEEK Asankhaya Sharma Goal • Design and build software that is correct by construction • Needed: Automatic tools for establishing software correctness • Such tools can – Search for standard problems like memory access violations or array index out of bounds – Check if a program does what it is supposed to do with respect to a specification Why a Need for Automatic Tools? Let the tool fill in the details A Tale of Two Tools • HIP – Automatically applies a given set of Hoare rules • SLEEK – Discharges the proof obligations resulting from the rule of consequence and the frame rule • Under development since 2006 – 180k lines of OCaml – Currently 6 PhD students; 3 graduated Overview Code Pre/Post code verifier (HIP) Predicates Lemmas separation logic prover (SLEEK) range of pure provers … Omega, MONA, Isabelle, Coq, SMT, Redlog, MiniSAT, Mathematica An Example – List Length struct node{ int val; struct node* next; }; int length(struct node* p) { if(p == NULL) return 0; else return 1 + length(p->next); } List Predicate Example of Acyclic List : list(x) x null list(self) self=null ∃r . self node(_,r) list(r) pointer to memory spatial conjunction Syntactic Abbreviation (ASCII) list(self) self=null ∃ r . self node(_, r) list(r) list == self=null or self::node_, r r::list implicit existential instantiation Verify with Shape Property struct node{ int val; struct node* next; }; /*@ list<> == self=null or self::node<_,q>*q::list<>; */ int length(struct node* p) /*@ requires p::list<> ensures p::list<>; */ { if(p == NULL) return 0; else return 1 + length(p->next); } Predicate Definition Method Pre and Post condition Memory Safety With Size parameter on length of linked list listn == self=null & n=0 or self::node_, r r::listn-1 inv n >= 0 predicate invariant x::ll5 x null Verify with Shape and Size int length(struct node* p) /*@ requires p::list<n> Memory Safety ensures p::list<n> & res=n; */ Length of the List { if(p == NULL) return 0; else return 1 + length(p->next); } With Size and Bag listn,B == self=null & n=0 & B={} or self::nodev, r r::listn-1,B1 & B = B1 U {v} inv n >= 0 & n=|B| Verify with Shape, Size and Bag int length(struct node* p) /*@ requires p::list<n,B> ensures p::list<n,B> & res=n; */ { if(p == NULL) return 0; else return 1 + length(p->next); } Memory Safety Length of the List Bag of Values Automated Verification int length(struct node* p) /*@ requires p::list<n> Pre condition Checking ensures p::list<n> & res=n; */ { if(p == NULL) return 0; Memory dereference Checking // p=null & n = 0 & res = 0 |- p::list<n> & res = n // p::ll<n> & p!=null |- p::node<val,nxt> // p::node<_,q> * q::ll<n-1> & p!=null & q = r |- r::ll<m> else return 1 + length(p->next); // p::node<_,q> * q::ll<n-1> & x!=null & res = 1 + n – 1 |- p::ll<n> & res = n Post condition Checking } SLEEK • Automatic Checking of Entailment • Custom decision procedure for the spatial fragment (Separation Logic) – Handles user-defined data structures and inductive predicates • Uses off-the-shelf provers to discharge: – Linear Arithmetic (using Omega) • Also available as a tactic in Coq – Bag Expressions (using MONA) • Sound but incomplete Numerical Examples for SLEEK Checking implications with integer constraints: Valid. checkentail x > 5 |- x > 0. InValid. checkentail x > 5 |- x < 0. InValid. checkentail x > 5 |- x > 6. checkentail x > 1 & y > 1 & r = x + y |- r > 3. Valid. SLEEK uses Omega for these examples List Examples for SLEEK checkentail x::node<1,null> |- x::list<n,B>. Valid. checkentail x::node<17,null> |Valid. x::list<n,B> & B = {17}. checkentail x::node<2,y> * y::list<m,{2} |Valid. x::list<n,B>. checkentail x::list<1,{2}> |- x::node<2,null>. Valid. checkentail x::list<2,{2,3}> |- x::node<2,null>. InValid. HIP • Automatic checking of pre/post for methods – Handle conditional, loops, methods – With arrays, data structures, dynamic memory allocation – Supports Multiple pre/post specifications, structured specifications, termination specifications • Sound but incomplete • Automated with the help of SLEEK Append Example with HIP • What should be the specification for the following method ? void append(node* x, node* y) requires ? ensures ? { if(x->next==NULL) x->next=y; else append(x->next,y); } Append Example with HIP • Is the following specification which aims to guarantee memory safety correct? void append(node* x, node* y) requires x::list<> * y::list<> ensures x::list<> { No, null pointer dereference possible if(x->next==NULL) x->next=y; else append(x->next,y); } Append Example with HIP • Correct specification for safety void append(node* x, node* y) requires x::list<> * y::list<> & x!=null ensures x::list<> { if(x->next==NULL) x->next=y; else append(x->next,y); } Append Example with HIP • With size and bag properties void append(node* x, node* y) requires x::list<n1,B1> * y::list<n2,B2> & x!=null ensures x::list<n1 + n2, B1 U B2> { if(x->next==NULL) x->next=y; else append(x->next,y); } Multiple Specifications • Same method may be called in different calling contexts • Verify using different specifications for each scenario • Which sorting algorithm may require an append function for two sorted lists ? – Quick Sort (and Merge Sort) With Bag and Sortedness lsortn,B == self=null & n=0 & B={} or self::nodev, r r::lsortn-1,B1 & B = B1 U {v} & ∀x ∈ B1. v<=x inv n >= 0 & n=|B| Verify with Multiple Specifications void append(node* x, node* y) requires x::list<n1,B1> * y::list<n2,B2> & x!=null ensures x::list<n1 + n2, B1 U B2> requires x::lsort<n1,B1> * y::lsort<n2,B2> & x!=null & ∀a ∈ B1. ∀b ∈ B2. a<=b ensures x::lsort<n1 + n2, B1 U B2> { if(x->next==NULL) x->next=y; else append(x->next,y); } List Segment with Size lsegp,n == self=p & n=0 or self::node_,r r::lsegp,n-1 inv n >= 0 x x::lsegy,3 y y::lsegx,2 Circular List with Size clistn == self::node_,r r::lsegself,n-1 inv n >= 1 x::clist3 x r r::lsegx,2 Use of Multiple Pre/Post void append(node* x, node* y) requires x::list<n> & x != null & x = y ensures x::clist<n> requires x::list<n> & x != null ensures x::lseg<y,n> { if(x->next==NULL) x->next=y; else append(x->next,y); } Binary Search Tree How do we express a binary search tree ? tree<> == self=null or self::node<v,l,r> * l::tree<> * r::tree<> Shape Property for Tree Binary Search Tree 5 3 1 7 Sortedness property 4 bst<B> == self=null & B = {} or self::node<v,l,r> * l::bst<B1> * r::bst<B2> & B = {v} U B1 U B2 & ∀w ∈ B1. v>=w & ∀w ∈ B2. v<=w AVL Tree How do we specify height balanced trees ? avl<h,B> == self=null & B = {} & h = 0 or self::node<v,l,r> * l::avl<h1,B1> * r::avl<h2,B2> & B={v}UB1UB2 & ∀w∈B1.v>=w & ∀w∈B2.v<=w & h = 1 + max(h1,h2) & h2<=h1+1 & h1<=h2+1 Conclusions • HIP and SLEEK Verification System – Automated • Given pre/post and loop invariants – Modular and scalable • Each method verified independently – Expressive • From shape, size, bag properties towards functional correctness • Total correctness with Termination and NonTermination proving Perspectives • Hardware community has accepted verification in their design phase • Verified software is the future for guarantying high assurance and reliability • Many challenges remain on scalability, automation, expressivity, concurrency, inference and higher order programs Questions? • We will try out some examples in Lab tomorrow using TeachHIP • TeachHIP is a Web Interface to – Try out HIP and SLEEK without installing any software – Available at http://loris-7.ddns.comp.nus.edu.sg/~project/TeachHIP/ • Contact – asankhaya@nus.edu.sg Further Reading • Chin, Wei-Ngan, Cristina David, Huu Hai Nguyen, and Shengchao Qin. "Automated verification of shape, size and bag properties via user-defined predicates in separation logic." Science of Computer Programming 77, no. 9 (2012): 1006-1036.