380 likes | 400 Views
This work focuses on moving from low-level to high-level operations in OS components, such as file systems and buffer cache managers, to avoid tedious low-level programming details. It addresses verifying software in the presence of crashes and multiple concurrent clients. The study covers verification conditions, loop invariants, universally quantified invariants, and Frangipani, utilizing predicate abstraction for efficient software verification.
E N D
Predicate Abstraction for Software Verification Shaz Qadeer Compaq Systems Research Center (joint work with Cormac Flanagan)
Systems software • OS components • file system, buffer cache manager • Abstract from low-level to high-level operations • tedious low-level programming detail • Expected to work • with multiple concurrent clients • in the presence of crashes
Outline • Background • verification condition • strongest postcondition • loop invariants • Inferring loop invariants • predicate abstraction • universally-quantified invariants • Frangipani • Related work
Verification condition x := a; if (x < b) { x := b; } assert x = max(a,b); Program P Check for validity: sp(P,true) x a x b
Guarded command language (Dijkstra 76) Statement S x := e A ; B assume e A B while {I} e do B end Variables have arbitrary values in program’s initial state if e then A else B end (assume e ; A) (assumee ; B)
Strongest postcondition semantics sp(S,Q) y.(Q[xy] x=e[xy]) sp(B,sp(A,Q)) e Q sp(A,Q) sp(B,Q) Statement S x := e A ; B assume e A B Denote sp(S, true) by sp(S)
Checking loop invariants • Given loop • Need to check • Invariant holds on entry to loop • sp( C ) I • Invariant holds at end of every iteration • sp( C; H; assume I e; B) I where H = havoc variables modified in B C; {I} while e do B end
Desugaring loops S = “while {I} e do B end” H = havoc variables modified in B desugar(S) = assert I ; H ; assume I ; ( (assume e ; B; assert I; assume false) assume e)
Example assume 0 < a.length; i := 0; while (i < a.length) { a[i] := 0; i := i + 1; } assert a[0] = 0; { int j; 0 <= j j < i a[j] = 0, 0<=i}
Translation assume 0 < a.length; i := 0; assert int j; 0 <= j j < i a[j] = 0; assert 0 <= i; havoc a[*], i; assume int j; 0 <= j j < i a[j] = 0; assume 0 <= i; ( assume (i < a.length); a[i] := 0; i := i + 1; assert int j; 0 <= j j < i a[j] = 0; assert 0 <= i; assume false;) assume (i < a.length) assert a[0] = 0;
ESC/Java with loop invariants /*@ loop_invariant i >= 0; loop_invariant 0 <= spot; loop_invariant spot <= MAXDIRENTRY; loop_invariant (\forall int j; 0 <= j && j < i && bdisk[addr].dirEntries[j].inum != DIRENTRY_UNUSED ==> bdisk[addr].dirEntries[j].name != name); loop_invariant (\forall int j; spot == MAXDIRENTRY && 0 <= j && j < i ==> bdisk[addr].dirEntries[j].inum != DIRENTRY_UNUSED); loop_invariant spot == MAXDIRENTRY || bdisk[addr].dirEntries[spot].inum == DIRENTRY_UNUSED; loop_invariant (\forall DirEntry t; t != de ==> t.name == \old(t.name)); loop_invariant (\forall DirEntry t; t != de ==> t.inum == \old(t.inum)); loop_invariant (\forall DirEntry t; t.inum == FS.DIRENTRY_UNUSED || (0 <= t.inum && t.inum < FS.IMAX)); */ for (i = 0; i < cwd.inode.length; i++) { GetDirEntry(de, addr, i); if (de.inum != DIRENTRY_UNUSED && de.name == name) { return ERROR; } if (de.inum == DIRENTRY_UNUSED && spot == MAXDIRENTRY) { spot = i; } }
Outline • Background • verification condition • strongest postcondition • loop invariants • Inferring loop invariants • predicate abstraction • universally-quantified invariants • Frangipani • Related work
Inferring loop invariants C; {I} while e do B end • Could try • I0 = sp( C ) • In+1 = In sp( C; H; assume In e; B) • In is an invariant for first n iterations • Problem: May not converge
Divergence State Space I0 I1 I2 In ... ...
Predicate abstraction • Invariant is boolean combination of predicates • Split problem into two parts • Find a suitable set of predicates • Find the boolean combination of these predicates
Predicate abstraction (contd.) • Predicates over program variables • a = expr1, b = expr2, c = expr3, d = expr4 • : boolean expression over {a,b,c,d} formula over program variables • :formula over program variables boolean expression over {a, b, c, d} • (F) = least boolean function G over {a,b,c,d} such that F (G)
Abstract state space • Predicates {a, b, c, d} • They generate an abstract space of size 24 = 16 cd cd cd cd State Space ab (F) ab F ab ab
Inferring loop invariants • Compute • I0=(sp( C )) • In+1 =In(sp( C; H; assume E (In); B)) • Converges yielding valid loop invariant • Best loop invariant for the given predicates
Method 1 (slow!) • Is F a b c dsatisfiable? No! • Can compute (F) by asking 2n such queries cd cd cd cd ab X X X X (F) ab X X X F ab X X ab X X
a Fa cd cd cd cd b Fab Fab ab X X X X c (F) Fabc ab X X X F ab X X ab X X Method 2 (Das-Dill-Park 99) Order the variables: a < b < c < d • O(2n+1) queries • Sensitive to ordering
. . Queries of size n: . . . Number = nCn2n Method 3 (Shankar-Saidi 99) Queries of size 1: Fa, F a, Fb, etc. Number = nC121 Queries of size 2: Fab, Fab, etc. Number = nC222 • O(3n) queries • No ordering required
New method • F a c d? No! • F a b c d? No! • F c d? No! • Removed 1/4 of state space in 3 queries! cd cd cd cd ab X X X X (F) = (c d) (a c) (a b) ( c d) ab X X X F ab X X ab X X
Universally-quantified loop invariants /*@ loop_invariant i >= 0; loop_invariant 0 <= spot; loop_invariant spot <= MAXDIRENTRY; loop_invariant (\forall int j; 0 <= j && j < i && bdisk[addr].dirEntries[j].inum != DIRENTRY_UNUSED ==> bdisk[addr].dirEntries[j].name != name); loop_invariant (\forall int j; spot == MAXDIRENTRY && 0 <= j && j < i ==> bdisk[addr].dirEntries[j].inum != DIRENTRY_UNUSED); loop_invariant spot == MAXDIRENTRY || bdisk[addr].dirEntries[spot].inum == DIRENTRY_UNUSED; loop_invariant (\forall DirEntry t; t != de ==> t.name == \old(t.name)); loop_invariant (\forall DirEntry t; t != de ==> t.inum == \old(t.inum)); loop_invariant (\forall DirEntry t; t.inum == FS.DIRENTRY_UNUSED || (0 <= t.inum && t.inum < FS.IMAX)); */ for (i = 0; i < cwd.inode.length; i++) { GetDirEntry(de, addr, i); if (de.inum != DIRENTRY_UNUSED && de.name == name) { return ERROR; } if (de.inum == DIRENTRY_UNUSED && spot == MAXDIRENTRY) { spot = i; } }
Inferring -quantified loop invariants assume 0 < a.length; i := 0; /*@ loop_invariant 0 <= i, int j; 0 <= j j < i a[j] = 0 */ while (i < a.length) { a[i] := 0; i := i + 1; } assert a[0] = 0;
Inferring -quantified loop invariants assume 0 < a.length; i := 0; /*@ loop_predicate 0 <= i, int j; 0 <= j j < i a[j] = 0 */ while (i < a.length) { a[i] := 0; i := i + 1; } assert a[0] = 0;
Inferring -quantified loop invariants assume 0 < a.length; i := 0; /*@ skolem_constant int j */ /*@ loop_predicate 0 <= i, 0 <= j, j < i, a[j] = 0 */ while (i < a.length) { a[i] := 0; i := i + 1; } assert a[0] = 0;
/*@ loop_predicate 0 <= i, 0 <= j, j < i, a[j] = 0 */ I0=(sp(i := 0)) In+1 =In(sp(i := 0; havoc i, a[*]; assume In i < a.length; a[i] := 0; i := i+1)) I0 TFTF TFTT TTFF TTFT I1 TTTT Inferring -quantified loop invariants
Inferring -quantified loop invariants /*@ loop_predicate 0 <= i, 0 <= j, j < i, a[j] = 0 */ I0 TFTF TFTT TTFF TTFT I1 TTTT 0 i (0 j) (j < i) a[j] = 0 0 j j < i
Inferring -quantified loop invariants /*@ loop_predicate 0 <= i, 0 <= j, j < i, a[j] = 0 */ I0 TFTF TFTT TTFF TTFT I1 TTTT 0 i int j; 0 j j < i a[j] = 0 int j; 0 j j < i
Outline • Background • verification condition • strongest postcondition • loop invariants • Inferring loop invariants • predicate abstraction • universally-quantified invariants • Frangipani • Related work
Frangipani • Distributed file system (Thekkath, Mann, Lee 1997) • Built on top of Petal virtual disk (Lee, Thekkath 1996) • Implements the file abstraction from the virtual disk block abstraction • Exports file and directory operations - create, delete, rename etc. - to clients
Verification • Loop invariant inference built on top of ESC/Java (Detlefs, Leino, Nelson, Saxe 1998) • ESC/Java uses automatic theorem prover Simplify (Nelson 81) • Frangipani data structures and “create” modeled abstractly in Java • Assume • single thread of execution • no crashes
. . . . . . . . . . . . . . . . . . Data structures on disk: Inode { int inum; int addr; int mode; int length; } ibusy F T F T F F idisk addr addr bdisk Block { int addr; Entry[] entries; } Entry { String name; int inum; } bbusy F T F F T F Data structures in memory: • vArray contains inodes • distinct entries must contain • distinct inodes vArray vbusy T T F F T F itov: int int
/*@ invariant int i, j: vbusy[i] ((vArray[i].inum = j) (itov[j] = i)); invariant int i: ibusy[i] bbusy[idisk[i].addr]; invariant idisk bdisk; invariant int i: idisk[i] null idisk[i].inum = i; . . */ /*@ requires vbusy[num] vArray[num].mode = DIR */ /*@ ensures \result = ERROR there is an entry with name s in directory vArray[num] */ int create(int num, String s) { . . . }
Main loop in “create” /*@ loop_invariant i >= 0; loop_invariant 0 <= spot; loop_invariant spot <= MAXDIRENTRY; loop_invariant (\forall int j; 0 <= j && j < i && bdisk[addr].dirEntries[j].inum != DIRENTRY_UNUSED ==> bdisk[addr].dirEntries[j].name != name); loop_invariant (\forall int j; spot == MAXDIRENTRY && 0 <= j && j < i ==> bdisk[addr].dirEntries[j].inum != DIRENTRY_UNUSED); loop_invariant spot == MAXDIRENTRY || bdisk[addr].dirEntries[spot].inum == DIRENTRY_UNUSED; loop_invariant (\forall DirEntry t; t != de ==> t.name == \old(t.name)); loop_invariant (\forall DirEntry t; t != de ==> t.inum == \old(t.inum)); loop_invariant (\forall DirEntry t; t.inum == FS.DIRENTRY_UNUSED || (0 <= t.inum && t.inum < FS.IMAX)); */ for (i = 0; i < cwd.inode.length; i++) { GetDirEntry(de, addr, i); if (de.inum != DIRENTRY_UNUSED && de.name == name) { return ERROR; } if (de.inum == DIRENTRY_UNUSED && spot == MAXDIRENTRY) { spot = i; } }
Related work • Inferring/computing loop invariants • Predicate abstraction • Graf-Saidi 97 • Bensalem-Lakhnech-Owre 98, Colon-Uribe 98 • Saidi-Shankar 99, Das-Dill-Park 99 • Ball-Majumdar-Millstein-Rajamani 2001
Future Work • Heuristics for generating predicates • Multiple threads • locks, semaphores, ... • thread-modular reasoning • Linked lists, reachability • Target C