predicate abstraction for software verification shaz qadeer compaq systems research center (joint...
Post on 21-Dec-2015
218 views
TRANSCRIPT
Predicate Abstraction for Software Verification
Shaz QadeerCompaq 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);
sp(P,true) x a x b
Program P
Check for validity:
Statement S
x := e
A ; B
assume e
A B�
while {I} e do B end
Guarded command language (Dijkstra 76)
Variables have arbitrary values in program’s initial state
if e then A else B end
(assume e ; A)
(� assume e ; 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 Bdesugar(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}
Translationassume 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
• 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
C;
{I} while e do B end
Divergence
State Space
I0 I1 I2 In... ...
Predicate abstraction
• Invariant is boolean combination of predicates
• Split problem into two parts
1 Find a suitable set of predicates
2 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
ab
ab
ab
ab
cd
cd
cd
cd
F
(F)
State Space
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 d satisfiable? No!
• Can compute (F) by asking 2n such queries
ab
ab
ab
ab
cd
cd
cd
cd
F
(F)X X X X
X
X
XX
X
X X
Method 2 (Das-Dill-Park 99)Order the variables: a < b < c < d a
b
c
Fa
Fab
Fabc
Fab
(F) ab
ab
ab
ab
cd
cd
cd
cd
F
X X X X
X
X
XX
X
X X
•O(2n+1) queries•Sensitive to ordering
Method 3 (Shankar-Saidi 99)
•O(3n) queries•No ordering required
Queries of size 1: Fa, F a, Fb, etc.Number = nC121
Queries of size 2: Fab, Fab, etc.Number = nC222
Queries of size n: . . .Number = nCn2n
.
.
New method• F a b c d ? No!
ab
ab
ab
ab
cd
cd
cd
cd
F
(F)X X X X
X
X
XX
X
X X
• F a c d ? No!• F c d ? No! • Removed 1/4 of state space in 3 queries!
= (c d) (a c) (a b) ( c d)
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;
Inferring -quantified loop invariants
I0 = (sp(i := 0))In+1 = In (sp(i := 0; havoc i, a[*]; assume In i < a.length; a[i] := 0; i := i+1))
/*@ loop_predicate 0 <= i, 0 <= j, j < i, a[j] = 0 */
TFTF TFTT TTFF TTFT I0
TTTT
I1
Inferring -quantified loop invariants
/*@ loop_predicate 0 <= i, 0 <= j, j < i, a[j] = 0 */
TFTF TFTT TTFF TTFT
TTTT
I0
I1
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 */
TFTF TFTT TTFF TTFT
TTTT
I0
I1
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
. . .
. . .
. . .
. . .ibusy
idisk
bdisk
bbusy
F T T
T T
F F F
FFFF
Inode { int inum; int addr; int mode; int length;}
Block { int addr; Entry[] entries;}
Entry { String name; int inum;}
addr addr
Data structures on disk:
Data structures in memory:
. . .
. . .
vArray
vbusy T T FFTF
• vArray contains inodes• distinct entries must contain distinct inodes
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; }}
Performance
Algorithm # queries(worst case)
# queries(on benchmark)
Naive 2n 32768
Das-Dill-Park 2n+1 4026
Saidi-Shankar 3n 2252
New n.2n 473
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