game-based model checking key game model properties: syntax-direct: behaviour of any component specified in isolation truly compositional: behaviour of component built from sub-models any component can be verified in isolation huge programs could be verified by isolating difficult parts
game-based p.a. game models cannot avoid state explosion - would benefit from predicate abstraction! predicate abstraction most naturally treated as a model mutation - would benefit from syntax-direct and compositional explanation
contributions a game semantics of p.a. a verification tool for a large subset of C a predicate annotation approach that exploits the syntax-direct property a CEGAR verification algorithm that exploits the syntax-direct property
programming language: IA L C-like control structure if | ; | break | continue | goto | assert block structure state {nat x := M; N} | x := M expressions x | k | M(N 1,..,N n ) | let f(x 1,..,x n ) = M in N
battle of numbers bool goer := 0; nat pile := 10 * you()%10 + me()%10; f (if pile > 0 then {goer := 1; pile -= you()%9%pile}; if pile > 0 then {goer := 0; pile -= me()%9%pile}); if pile = 0 then winner := goer
wrestle of numbers bool goer := 0; nat pile := {nat n = you()%10; n* – n}; assert(pile%9 = 0); f (if pile > 0 then {nat n := you()%9; goer := 1; pile -= n; assert(pile%9 = 9 - n); if pile > 0 then {pile -= 9 – n; goer := 0}}); assert(goer = 0)
stateful game model – no p.a. model: M v = set of traces returning val v distinguish ab/normally terminating traces trace: start state, moves, end state state: map ids in environment to vals move: val position (in typing) | ) ( |
you()%9 d = m%9=d q you,m you stateful game models you() m = q you,m you (for all m) ( | ( | | ) | ) n := you()%9 () = d m%9=d q you,m you ( | | ) ndnd
p.a. game model model: M e = set of traces returning expr e distinguish ab/normally terminating traces trace: start P-state, moves, end P-state P-state: set of (negated) P members move: expr position (in typing) {||}
if sat ( & ) p.a. game models you()%9 y%9 = q you,y you you() y = q you,y you n := you()%9 () = q you,y you y%9/n {| {| {| |} |} |}
wrestle game model goer=0 goer=1 q you,0 you,q f q f1,q you,n you () f1 () f q f1,() f1 q you,1 you,q f … () f1
wrestle p.a. model pile%9=0 pile%9!=9-ngoer=0 pile>0 pile%9!=0 pile%9=9-n pile>0 goer=1 q you,y you,q f q f1,q you,n you pile%9=0 pile%9!=9-n pile=0 goer=0 () f1 () f pile%9=0 pile%9!=9-n pile=0 goer=0 q f1,() f1 () f1
about this formulation + simple + stateful and p.a. variants similar + control semantics orthogonal to state semantics (control ignored in this talk!) – less compositional in spirit than some game semantics - environments are important - p.a. semantics difficult to handle otherwise
p.a. properties the p.a. model is decidable (finite-state) if p.a. model of M has no aborting traces then M is safe
syntactic predicate annotation we can trivially move predicate annotations from the model to the program this can be used to minimize the predicate state size
annotated wrestle letp goer = 0 in bool goer := 0; letp pile%9 = 0 in nat pile := {nat n = you()%10; n* – n}; assert(pile%9 = 0); f (if pile > 0 then {nat n := you()%9; goer := 1; letp pile%9 = 9 – n in pile -= n; assert(pile%9 = 9 - n); if pile > 0 then {pile -= 9 – n; goer := 0}}); assert(goer = 0)
annotated p.a. model pile%9=0goer=0 pile%9!=0 pile%9=9-ngoer=1 q you,y you,q f q f1,q you,n you () f q f1,() f1 () f1 goer=0
game p.a. c.e.g.a.r. make each conditional a predicate –as tightly scoped as possible model check –safe => safe –unsafe => check trace feasibility widen scope of some letp if infeasible
c.e.g.a.r. wrestle bool goer := 0; nat pile := {nat n = you()%10; n* – n}; letp pile%9 = 0 in assert(pile%9 = 0); f (if letp pile > 0 in pile > 0 then {nat n := you()%9; goer := 1; pile -= n; letp pile%9 = 9 – n in assert(pile%9 = 9 - n); if letp pile > 0 in pile > 0 then {pile -= 9 – n; goer := 0}}); letp goer = 0 in assert(goer = 0)
c.e.g.a.r. wrestle bool goer := 0; letp pile%9 = 0 in nat pile := {nat n = you()%10; n* – n}; assert(pile%9 = 0); f (if letp pile > 0 in pile > 0 then {nat n := you()%9; goer := 1; pile -= n; letp pile%9 = 9 – n in assert(pile%9 = 9 - n); if letp pile > 0 in pile > 0 then {pile -= 9 – n; goer := 0}}); letp goer = 0 in assert(goer = 0)
c.e.g.a.r. wrestle bool goer := 0; letp pile%9 = 0 in nat pile := {nat n = you()%10; n* – n}; assert(pile%9 = 0); letp goer = 0 in f (if letp pile > 0 in pile > 0 then {nat n := you()%9; goer := 1; pile -= n; letp pile%9 = 9 – n in assert(pile%9 = 9 - n); if letp pile > 0 in pile > 0 then {pile -= 9 – n; goer := 0}}); assert(goer = 0)
c.e.g.a.r. wrestle bool goer := 0; letp goer = 0 in letp pile%9 = 0 in nat pile := {nat n = you()%10; n* – n}; assert(pile%9 = 0); f (if letp pile > 0 in pile > 0 then {nat n := you()%9; goer := 1; pile -= n; letp pile%9 = 9 – n in assert(pile%9 = 9 - n); if letp pile > 0 in pile > 0 then {pile -= 9 – n; goer := 0}}); assert(goer = 0)
c.e.g.a.r. wrestle letp goer = 0 in bool goer := 0; letp pile%9 = 0 in nat pile := {nat n = you()%10; n* – n}; assert(pile%9 = 0); f (if letp pile > 0 in pile > 0 then {nat n := you()%9; goer := 1; pile -= n; letp pile%9 = 9 – n in assert(pile%9 = 9 - n); if letp pile > 0 in pile > 0 then {pile -= 9 – n; goer := 0}}); assert(goer = 0)
c.e.g.a.r. wrestle letp goer = 0 in bool goer := 0; letp pile%9 = 0 in nat pile := {nat n = you()%10; n* – n}; assert(pile%9 = 0); f (if letp pile > 0 in pile > 0 then {nat n := you()%9; goer := 1; pile -= n; letp pile%9 = 9 – n in assert(pile%9 = 9 - n); if letp pile > 0 in pile > 0 then {pile -= 9 – n; goer := 0}}); assert(goer = 0)
c.e.g.a.r. wrestle letp goer = 0 in bool goer := 0; letp pile%9 = 0 in nat pile := {nat n = you()%10; n* – n}; assert(pile%9 = 0); f (if letp pile > 0 in pile > 0 then {nat n := you()%9; goer := 1; letp pile%9 = 9 – n in pile -= n; assert(pile%9 = 9 - n); if letp pile > 0 in pile > 0 then {pile -= 9 – n; goer := 0}}); assert(goer = 0)
c.e.g.a.r. wrestle letp goer = 0 in bool goer := 0; letp pile%9 = 0 in nat pile := {nat n = you()%10; n* – n}; assert(pile%9 = 0); f (if letp pile > 0 in pile > 0 then {nat n := you()%9; goer := 1; letp pile%9 = 9 – n in pile -= n; assert(pile%9 = 9 - n); if letp pile > 0 in pile > 0 then {pile -= 9 – n; goer := 0}}); assert(goer = 0)