390 likes | 485 Views
Learn why verifying programs with pointers and heap data is crucial, explore separation logic and operational semantics. Discover frame rules, shape analysis, and techniques for pointer manipulation.
E N D
Techniques for proving programs with pointers A. Tikhomirov
Why is it important? • Every program works not only with stack but stores intermediate data in heap and use pointers to operate with heap. • Common program verification techniques couldn’t work with pointers and heap.
John C. Reynolds, Peter O’Hearn Separation Logic
Fail using Hoare Logic • Structure of Hoare Logic assignment judgment couldn't work with heap. int x = 5; int *y = &x; // *y = 5 x = 3; // *y = 3 Hoare logic doesn’t specify holding of *y FAIL
Separation Logic • Representing the Heap • Assertions empty heap singleton heap separating conjunction separating implication
Separation Logic • Asserts that x points to an adjacent pair of cells containing u, v (x stores address αand the heap maps αinto u and (α +1) into v)
Singleton heap • Reference pointer to some cell • Asserts that x points to an adjacent pair of sells containing 1 and y.
Separating conjunction • The separating conjunction constructs a heap property from two disjoint heaps
Logical conjunction • Could be ambiguous in heap, x and y could be aliases ,or disjoint.
Lists • List representation
Frame rule • Frame rule: This rule holds as long as free variables in s not mentioned in R.
Allocation • Allocation rule (local): • Allocation rule (global):
Deallocation • Dispose rule (local): • Dispose rule (global):
Heap write (mutation) • Mutation forward rule (local): • Mutation forward rule (global):
Examples of Operational Semantics Store: [x:3, y:40, z:17] Heap: empty • Allocation x := cons(y, z) • Heap lookup y := [x+1] • Mutation [x + 1] := 3 • Deallocation dispose(x+1) Store: [x:37, y:40, z:17] Heap: [37:40, 38:17] Store: [x:37, y:17, z:17] Heap: [37:40, 38:17] Store: [x:37, y:17, z:17] Heap: [37:40, 38:3] Store: [x:37, y:17, z:17] Heap: [37:40]
Example: Swap • Function in which the contents of two heap cells are swapped: t1 := [v1] t2 := [v2] [v1] := t2 [v2] := t1
Example: Swap void swap (int* v1, int* v2) { t1:= [v1] t2 := [v2] [v1] := t2 [v2] := t1 } // {v1 > v1’ * v2 > v2’} // {t1 = v1’ & v1 > v1’ * v2 > v2’} // {t1 = v1’ & t2 = v2’ & v1 > v1’ * v2 > v2’} // {t1 = v1’ & t2 = v2’ & v1 > v2’ * v2 > v2’} // {t1 = v1’ & t2 = v2’ & v1 > v2’ * v2 > v1’}
Example: Doubly Indirect References • Mutating the value of a double indirect reference (a pointer to a pointer):
Example: Doubly Indirect References void write (int** p, int v) { t:= [p] [t] := v } // {p > w * w > —} // {p > w} Frame rule – begin // {t = w & p > w} Mutation // {t = w & p > w * w > —} Frame rule — end // {t = w & w > —} Frame rule — begin // {t = w & w > v} Mutation // {t = W & p > w * w > v} Frame rule — end // {p > w * w > v} Remove extra stack assertion
Example: Doubly Indirect References • Attempting to aggressively apply Frame rule to entire block would result in a stuck proof. void write (int** p, int v) { // {p > w * w > —} // {p > w} Frame rule – begin t:= [p] // {t = w & p > w} Mutation [t] := v // {??} Stuck – no mapping for w // {t = W & p > w * w > v} Frame rule — end }
Example: DeleteTree // {tree(t)} void deleteTree (t) { local i,j; if (t != nil) { i = [t] j = [t+1] deleteTree(j) deleteTree(i) dispose(t) } } // {tree(t) ^ t != nil} // {∃x,y.t > (x,y) * tree(x) * tree(y)} // {t > (i,j) * tree(i) * tree(j)} // {t > (i,j) * tree(i)} // {t > (i,j) * nil} // {emp} // {emp}
Example: List reverse // {list(x)} list reverse (x) { y = nil; while (x != nil) { t := [x]; x := [y]; y := x; x := t; } return y; } // {list(x) * list(y)} // {x != nil ^ list(x) * list(y)} // {x > t ^ list(t) * list(y)} // {x > y ^ list(x) * list(y)} // {list(t) * list(x)} // {list(t) * list(y)} // {list(x) * list(y)} // {x = nil ^ list(x) * list(y)} // {list(y)}
Conclusion • Extension of Hoare logic • Could use verification tools, that work with Hoare logic • For automatic proving • Assertions • Pre and post-conditions
Reinhard Wilhelm, MoolySagiv, Thomas Reps Shape ANALYSIS
Shape Analysis • Somewhat constrained view of programs • Static code analysis • Program verification
Input to reverse • (a) lists of length at least 2 • (b) list of length 1
Input to reverse • List structure of size from 0 to 4
Graph interpretation • Rectangle box containing p represent pointer variable p • Ovals – abstract locations • solid – represents exactly one heap cell • dotted – represent one or more heap cells • Edge – label c between locations m and m’ • solid – c component of mwill point to m’ • dotted - c component of mmay point to m’
3-value logic • solid means “always holds” • absent means “never hold” • dotted means “don’t know”
Execution states • (a) – shape arise before executing x=x->n • (b) & – (c) after executing x=x->n on shape (a)
Insert algorithm • The same list structure
Execution States (1) y = x; while (y->n != NULL) {y = y->n}
Execution States (2) • t = malloc(); • t -> data = n
Execution States (3) • e = y->n;
Execution States (4) • t->n = NULL; • t->n = e;
Execution States (5) • y->n = NULL;
Execution States (6) • y->n = t;
Conclusion • Code analysis • NULL-pointers; • May-Alias; • Must-Alias; • Sharing; • Reachability; • Disjointness; • Cyclicity; • Shape;