310 likes | 451 Vues
This work explores the integration of game-based strategies into predicate abstraction for compositional model checking. By reformulating traditional models into a tree of automata and utilizing on-demand transitions, the method enhances efficiency in verifying real code. We demonstrate how our framework, GBPA v1, addresses critical challenges in safety verification through innovative techniques such as lazy evaluation and state compression. The proposed approach allows for the verification of complex C programs, achieving competitive performance with minimal resource use.
E N D
Towards Game-BasedPredicate Abstraction experiments in compositional model checking Adam Bakewell, University of Birmingham
MAGE • Compositional (game-based) MC’ing works, has benefits, and can be efficient. proof: • Take the regular language game model of IA • Reformulate model as tree of automata • Make model branches be function/model application • Be lazy: make transitions when demanded by checker • Be symbolic: don’t build the automata • Do CEGAR: approximate, certify, refine
COMO • Compositional MC’ing can verify real code with competitive efficiency proof: • Take a C program and process with CIL • Transform to ALGOL-like compositional CFG language • Do MAGE better: pre-composed interaction transitions; bdd; cbv; state compression+random access; etc
compositional CFGs Program ::= (τ f(xi:τi){new yi:τ’i.M})+ M ::= expr | M;M | lval:=expr | goto φ | case expr of {ki->Mi} φ = path to target from enclosing function root [[goto φ1]] = jump from state φ0·φ2 to φ0·φ1 where φ2 is the local path to this goto
still ill • Still far too slow • Data approximation is easily outwitted • Other model checkers do much better with a different kind of approximation….
e.g. xx1.c e.g. cc1.c #include<assert.h> extern int y; extern int main () { int x=y; assert(x!=x+1); return(0); } #include<assert.h> extern char d; extern int main () { char c=d; assert(c!=c+1); return(0); }
e.g. evenloop.c #include <assert.h> extern int y; int main() { int x = 0; int i = y; while(i-- >= 0) x += 2; assert(x%2==0); return(0); }
evenloop.c CFG model 2y,0 x:=0;i=y i--;x+=2 (i--;x+=2)(y-1) 0,y 2,y-1 ┴ i < 0 i < 0 i < 0 0,y 2,y-1 2y,0 assert state: x,i UNSAFE SAFE
predicate abstraction (PA) • P is a seq of predicates over program variables • PA model is a CFG where • States are #P bit-vectors (P-valuations) • Transitions exist where some basic block can change state sat. source P-valuation to state sat. target P-valuation • If #P <#(program state) then checking PA model is much quicker than checking program model
e.g. CFG PA model i--;x+=2 x:=0;i=y i--;x+=2 T F ┴ i<0 i<0 T F assert assert SAFE UNSAFE P=[x%2==0]
PA issues • P • must be sufficient to prove (un)safety • must be compact for feasibility (minimization) • derive from program/annotations • infer (e.g. by interpolation) • modelling and checking • which p’s to update/test (scope) • when to update/test (laziness)
PA needs games? • CFG PA Is not compositional! • Issues may be addressed elegantly with a compositional, semantic-direct, formulation • Let’s try it…
GBPA v1 • Take COMO architecture • Do a simple p. extraction (assert invariants!) • Locate each p. in model tree • Replace state model with p. model: (:=) sets p. state (case) tests p. state
evenloop.c GBPA model T,T x+=2 tmp>0 tmp:=i;i-- x:=0;i=y SAFE T,T T,T ┴ assert tmp≤0 T,F T,F T,F P=[x%2==0,tmp>0] UNSAFE
PA model formalization • move: arena + value + p.valuation • [[CELL(x,P)]] • Binds vars x, abstracted as preds P • states are P-valuation vectors • write move: (x := v|p) goes to state2 if SAT(state2[x’/x] & x’=v & p & state) • read move: (x.x|state) preserves state; composition with [[case]] does SAT, e.g. SAT(x=v & p)
laziness is free • COMO architecture is built to make model transitions on-demand: • (:=) SAT calls can be suspended when a valid next state is found • (case) SAT calls suspend while an alternative is explored
lo PREDICATE LOCATION
mind gym: verify your answer #include <assert.h> int main() { int x = 19; assert(x==19); x=((((((((x+x)*2)+5)/3)+3)/2)+6)/7)+2; assert(x==??); return(0); }
e.g. multi assignment int main() { int x = 19; x=x+x; x=x*2; x=x+5; x=x/3; x=x+3; x=x/2; x=x+6; x=x/7; x=x+2; assert(x==5); return(0); } x!=5 here x=5 or x!=5 here assert has spurious failure
e.g. manual interpolation x=x+3; assert(x==30); x=x/2; assert(x==15); x=x+6; assert(x==21); x=x/7; assert(x==3); x=x+2; assert(x==5); return(0); } int main() { int x = 19; assert(x==19); x=x+x; assert(x==38); x=x*2; assert(x==76); x=x+5; assert(x==81); x=x/3; assert(x==27);
e.g. manual interpolation • 10 assignments • 10 predicates • 10 assertions • how many SAT calls to prove safety?
p location • GBPA CELL automatically restricts p scope • Program transform with tighter CELLs will need less SAT calls
e.g. manual relocation int gym3(z) { int y=z+6; assert(y==21); y=y/7; assert(y==3); y=y+2; assert(y==5); return(y); } int main() { int x = 19; assert(x==19); x=gym1(x); assert(x==81); x=gym2(x); assert(x==15); x=gym3(x); assert(x==5); return(0); } #include <assert.h> int gym1(z) { int y=2*z; assert(y==38); y=y*2; assert(y==76); y=y+5; assert(y==81); return(y); } int gym2(z) { int y=z/3; assert(y==27); y=y+3; assert(y==30); y=y/2; assert(y==15); return(y); }
e.g. manual relocation • in main: • 4 p’s to update: 16 SAT per (:=) • 4 asserts test on state size 4 p’s • in gymi functions: • 3 p’s to update: 8 SAT per (:=) • local state size 3 p’s mostly sufficient • global state size 7 p’s sometimes used • total SATs: 68 + 3 * 27 = 149
delayed SAT • An alternative to interpolation: • Allow state updates to be delayed until the kth • And delay state tests • Generalise CELL strategy s.t. states also record seq of delayed changes • Refine by increasing k
the position • Game based model checking needs PA • PA fits the games framework • We get lazy PA for free • Tight CELL location offers minimization • delayed/incremental solving needs to be accommodated