An Introduction to Separation Logic
Total Page:16
File Type:pdf, Size:1020Kb
An introduction to separation logic James Brotherston Programming Principles, Logic and Verification Group Dept. of Computer Science University College London, UK [email protected] Oracle Labs, Brisbane, 4 December 2015 1/ 19 Introduction Verification of imperative programs is classically based on Hoare triples: fP g C fQg where C is a program and P; Q are assertions in some logical language. These are read, roughly speaking, as for any state σ satisfying P , if C transforms state σ to σ0, then σ0 satisfies Q. (with some wriggle room allowing us to deal with faulting or non-termination in various ways.) 2/ 19 Hoare-style verification A Hoare-style program logic therefore relies on three main components: 1. a language of programs, and an operational semantics explaining how they transform states; 2. a language of logical assertions, and a semantics explaining how to read them as true or false in a particular state; 3. a formal interpretation of Hoare triples, together with (sound) proof rules for manipulating them. We'll look at these informally first, then introduce a little more formal detail. 3/ 19 Programs, informally We consider a standard while language with pointers, memory (de)allocation and recursive procedures. E.g.: deltree(*x) f if x=nil then return; else f l,r := x.left,x.right; deltree(l); deltree(r); free(x); g g 4/ 19 Assertions, informally Our assertion language lets us describe heap data structures such as linked lists and trees. E.g., binary trees with root pointer x can be defined by: x = nil : emp ) tree(x) x 6= nil : x 7! (y; z) ∗ tree(y) ∗ tree(z) ) tree(x) where • emp denotes the empty heap; • x 7! (y; z) denotes a single pointer to a pair of data cells; • ∗ means \and separately in memory". 5/ 19 ftree(x)g fempg fx 7! (y; z) ∗ tree(y) ∗ tree(z)g fx 7! (l; r) ∗ tree(l) ∗ tree(r)g fx 7! (l; r) ∗ emp ∗ tree(r)g fx 7! (l; r) ∗ emp ∗ empg femp ∗ emp ∗ empg fempg fempg An example proof deltree(*x) f if x=nil then return; else f l,r := x.left,x.right; deltree(l); deltree(r); free(x); g g 6/ 19 fempg fx 7! (y; z) ∗ tree(y) ∗ tree(z)g fx 7! (l; r) ∗ tree(l) ∗ tree(r)g fx 7! (l; r) ∗ emp ∗ tree(r)g fx 7! (l; r) ∗ emp ∗ empg femp ∗ emp ∗ empg fempg An example proof ftree(x)g deltree(*x) f if x=nil then return; else f l,r := x.left,x.right; deltree(l); deltree(r); free(x); g g fempg 6/ 19 fx 7! (y; z) ∗ tree(y) ∗ tree(z)g fx 7! (l; r) ∗ tree(l) ∗ tree(r)g fx 7! (l; r) ∗ emp ∗ tree(r)g fx 7! (l; r) ∗ emp ∗ empg femp ∗ emp ∗ empg fempg An example proof ftree(x)g deltree(*x) f if x=nil then return; fempg else f l,r := x.left,x.right; deltree(l); deltree(r); free(x); g g fempg 6/ 19 fx 7! (l; r) ∗ tree(l) ∗ tree(r)g fx 7! (l; r) ∗ emp ∗ tree(r)g fx 7! (l; r) ∗ emp ∗ empg femp ∗ emp ∗ empg fempg An example proof ftree(x)g deltree(*x) f if x=nil then return; fempg else f fx 7! (y; z) ∗ tree(y) ∗ tree(z)g l,r := x.left,x.right; deltree(l); deltree(r); free(x); g g fempg 6/ 19 fx 7! (l; r) ∗ emp ∗ tree(r)g fx 7! (l; r) ∗ emp ∗ empg femp ∗ emp ∗ empg fempg An example proof ftree(x)g deltree(*x) f if x=nil then return; fempg else f fx 7! (y; z) ∗ tree(y) ∗ tree(z)g l,r := x.left,x.right; fx 7! (l; r) ∗ tree(l) ∗ tree(r)g deltree(l); deltree(r); free(x); g g fempg 6/ 19 fx 7! (l; r) ∗ emp ∗ empg femp ∗ emp ∗ empg fempg An example proof ftree(x)g deltree(*x) f if x=nil then return; fempg else f fx 7! (y; z) ∗ tree(y) ∗ tree(z)g l,r := x.left,x.right; fx 7! (l; r) ∗ tree(l) ∗ tree(r)g deltree(l); fx 7! (l; r) ∗ emp ∗ tree(r)g deltree(r); free(x); g g fempg 6/ 19 femp ∗ emp ∗ empg fempg An example proof ftree(x)g deltree(*x) f if x=nil then return; fempg else f fx 7! (y; z) ∗ tree(y) ∗ tree(z)g l,r := x.left,x.right; fx 7! (l; r) ∗ tree(l) ∗ tree(r)g deltree(l); fx 7! (l; r) ∗ emp ∗ tree(r)g deltree(r); fx 7! (l; r) ∗ emp ∗ empg free(x); g g fempg 6/ 19 fempg An example proof ftree(x)g deltree(*x) f if x=nil then return; fempg else f fx 7! (y; z) ∗ tree(y) ∗ tree(z)g l,r := x.left,x.right; fx 7! (l; r) ∗ tree(l) ∗ tree(r)g deltree(l); fx 7! (l; r) ∗ emp ∗ tree(r)g deltree(r); fx 7! (l; r) ∗ emp ∗ empg free(x); femp ∗ emp ∗ empg g g fempg 6/ 19 An example proof ftree(x)g deltree(*x) f if x=nil then return; fempg else f fx 7! (y; z) ∗ tree(y) ∗ tree(z)g l,r := x.left,x.right; fx 7! (l; r) ∗ tree(l) ∗ tree(r)g deltree(l); fx 7! (l; r) ∗ emp ∗ tree(r)g deltree(r); fx 7! (l; r) ∗ emp ∗ empg free(x); femp ∗ emp ∗ empg g fempg g fempg 6/ 19 Frame property Consider the following step in the previous example: fx 7! (l; r) ∗ tree(l) ∗ tree(r)g deltree(l) fx 7! (l; r) ∗ emp ∗ tree(r)g Implicitly, this relies on a framing property, namely: ftree(l)g deltree(l) fempg fx 7! (l; r) ∗ tree(l) ∗ tree(r)g deltree(l) fx 7! (l; r) ∗ emp ∗ tree(r)g 7/ 19 Classical failure of frame rule The so-called frame rule, fP g C fQg fF ^ P g C fF ^ Qg is well known to fail in standard Hoare logic. E.g., fx = 0g x := 2 fx = 2g fy = 0 ^ x = 0g x := 2 fy = 0 ^ x = 2g is not valid (because y could alias x). As we'll see, using the \separating conjunction" ∗ instead of ^ will however give us a valid frame rule. 8/ 19 Heap memory model • We assume an infinite set Val of values of which an infinite subset Loc ⊂ Val are allocable locations; nil is a non-allocable value. • Stacks map variables to values, s : Var ! Val. • Heaps map finitely many locations to values, h : Loc *fin Val. We write e for the empty heap (undefined on all locations). • Heap composition h1 ◦ h2 is defined to be h1 [ h2 if their domains are non-overlapping, and undefined otherwise. • A state is simply a stack paired with a heap, (s; h). 9/ 19 Program semantics • A configuration is given by (C; s; h), where C is a program, and (s; h) a (stack-heap) state. • C could be empty, in which case we call (C; s; h) final (and usually just write hs; hi). • fault is a special configuration used to catch memory errors. • The small-step semantics of programs is then given by a relation between configurations: 0 0 0 (C; s; h) (C ; s ; h ) 10/ 19 Semantics of assignment and (de)allocation (x := E; s; h) (s[x 7! [[E]]s]; h) [[E]]s 2 dom(h) (x := E:f; s; h) (s[x 7! h([[E]]s):f]; h) [[E]]s 2 dom(h) 0 0 (E:f := E ; s; h) (s; h[[[E]]s:f 7! [[E ]]s]) ` 2 Loc n dom(h) v 2 Val (E := new(); s; h) (s[x 7! `]; h[` 7! v]) [[E]]s = ` 2 dom(h) (free(E); s; h) (s; (h (dom(h) n f`g)) C ≡ x := E:f j E:f := E0 j free(E) [[E]]s2 = dom(h) (C; s; h) fault 11/ 19 Symbolic-heap assertions • Terms t are either variables x; y; z : : : or the constant nil. • Pure formulas π and spatial formulas F are given by: π ::= t = t j t 6= t F ::= emp j x 7! t j P t j F ∗ F (where P a predicate symbol, t a tuple of terms). • A symbolic heap is 9x: Π: F , for Π a set of pure formulas. • The predicate symbols might come from a hard-coded set, or might be user-defined. 12/ 19 Semantics of assertions We define the forcing relation s; h j= A: s; h j=Φ t1 = (6=)t2 , s(t1) = (6=)s(t2) s; h j=Φ emp , h = e s; h j=Φ x 7! t , dom(h) = fs(x)g and h(s(x)) = s(t) s; h j=Φ P t , (s(t); h) 2 [[P ]] s; h j=Φ F1 ∗ F2 , 9h1; h2:h = h1 ◦ h2 and s; h1 j=Φ F1 and s; h2 j=Φ F2 jzj s; h j=Φ 9z: Π: F , 9v 2 Val : s[z 7! v]; h j=Φ π for all π 2 Π and s[z 7! v]; h j=Φ F The semantics [[P ]] of inductive predicate P has a standard construction (but outside the scope of this talk). 13/ 19 Interpretation of Hoare triples Our interpretation of Hoare triples is almost standard, except we take a fault-avoiding interpretation: Definition fP g C fQg is valid if, whenever s; h j= P , ∗ 1.