in collaboration with University of Kent Clara Benac Earle SICS Mads Dam Lars-Åke Fredlund Dilian Gurov Gena Chugunov CWI Izak van Langevelde Wan Fokkink Jaco van de Pol Stefan Blom Inria Rhône-alpes Hubert Garavel Radu Mateescu RWTH Aachen Thomas Noll Martin Leucker Jürgen Giesl LFCIA Juanjo Sánchez Penas in collaboration with University of Kent SICS CWI Inria Rhône-alpes RWTH Aachen LFCIA Formal verification of software Thomas Arts Ericsson Stockholm, Sweden
Why formal verification ? cut down on testing/maintenance cost Common Criteria (ISO 15408) prescribes it allow more complex products to be built For what purpose ? find bugs in design/software prove correctness
Where to apply ? Verify design Verify software Verify software implements design Verify design equivalence Verify program equivalence case-studies WAP Database lookup Resource locking leader election TCAP
Formal Verification of Design Make a formal design (specification language) Formalize properties Find a technique to verify that the formal property holds for the formal design
Formal Verification of Design (example) Design of resource locker locker B A C D client
locker client ok request A, B release idle critical idle request resources ok release idle request resources release available ? ok idle pending available? add pending idle ok rm pending Formal Verification of Design (example)
Client specification idle critical idle request resources ok release client_idle = send(request_recources). receive(ok). client_critical client_critical = send(release). client_idle init locker_idle || client_idle
idle request resources release available ? ok idle pending available? add pending idle ok rm pending locker_idle = receive(request_resources). (available. send(ok). locker_idle + not_available. add_pending. locker_idle ) + receive(release). locker_intermediate locker_intermediate = pending_available. send(ok). rm_pending. locker_intermediate + not_pending_available. locker_idle Locker specification
State space generation Clearly there are deadlock states in the state space They correspond to the situation where: - resource not available - resource available but no client waiting for it Both situations cannot occur in reality
2 clients and a locker Non-determinism gives possibility that both clients are waiting for the resource Note that both clients can get access to the same resource
Formal Verification of Design have to make formal design not enough detail for interesting properties no feeling for implementation (memory consumption, speed) implementation may divert from design
Formal Verification of Design More and more details are added to the design in order to check interesting properties. CLAIM: For Interesting properties one need to add details until the specification is executable, i.e. is a program.
Formal verification of a Program IDEA: Use code as formal design document + Libraries can be used as-is + Programming languages support debug facilities, testing possibilities, etc. + Designers do not need to learn a new language
Formal verification of a Program (example) idle critical idle request resources ok release -module(client). start(Locker) -> spawn(fun() -> loop(Locker) end). loop(Locker) -> Tag = ref(), Locker!{request,[a],self(),Tag}, receive {ok,Tag} -> critical(), Locker!{release,self()}, loop() end.
Formal verification of a Program (example) -module(locker). loop(Locks) -> receive {request,Rs,Client,Tag} -> case available(Locks,Rs) of true -> Client ! {ok,Tag}, loop(update(Locks)); false -> loop(add(Locks,Rs, {Client,Tag})) end; {release,Client} ->... idle request resources release available ? ok idle pending available? add pending idle ok rm pending
Formal verification of a Program IDEA: Use code as formal design document + Libraries can be used as-is + Programming languages support debug facilities, testing possibilities, etc. + Designers do not need to learn a new language design patterns - Too much detail no feeling for design - Risk of infinite state space theorem prover or abstraction
Formal verification of a Program (example) use the generic server design pattern to implement the locker init(Resources) -> {ok, built_locks(Resources)}. handle_call({request,Rs},Client,Locks) -> case available(Rs,Locks) of true -> {reply,ok,update(Locks)}; false -> {noreply,add(Locks,Rs,Client)} end;....
Formal verification of a Program Techniques: Theorem Proving expressive logic allows (co-)induction large manual effort Model Checking limited logic (e.g. CTL) little manual effort Ad Hoc solutions
State Space Generation Erlang modules our Tool We developed a tool to generate all possible traces for simple client- server applications
State Space Generation In Erlang we use a supervisor design pattern to handle fault-tolerance. If a process dies, it’s supervisor restarts it. supervisor locker gen_server client
State Space Generation We use supervisor design pattern to obtain initialization information for transition diagram supervisor locker gen_server client start the locker with 2 clients supervisor:start(locker_sup,init,[[[a,b],[b,c]]]). start verification with 2 clients etomcrl:start(locker_sup,init,[[[a,b],[b,c]]]). We build one state space per configuration
our Tool locker.erl client.erl locker_sup.erl client_sup.erl our Tool our Tool our Tool our Tool EtoE rest tool locker.erl client.erl init.erl instantiation State Space Generation
locker.erl client.erl locker_sup.erl client_sup.erl locker.erl client.erl init.erl instantiation tomcrl.erl instantiation EtoE rest tool EtoPmcrl
State Space Generation locker.erl client.erl locker_sup.erl client_sup.erl instantiation tomcrl.erl instantiation EtoE rest tool EtoPmcrl rest tool CWI tool instantiator locker.mCRL toMCRL locker.erl client.erl init.erl
State Space Generation locker.erl client.erl locker_sup.erl client_sup.erl instantiation tomcrl.erl instantiation EtoE rest tool EtoPmcrl CWI tool instantiator locker.mCRL toMCRL locker.erl client.erl init.erl CADP locker.aut
2 clients and a locker
Model Checking Software macro UNTIL (Act1,Act2) = [true*.(Act1).(not(Act2))*.(Act1)]false end_macro UNTIL('action_use(.*a.*)','action_free(.*a.*)') and UNTIL('action_use(.*b.*)','action_free(.*b.*)') and UNTIL('action_use(.*c.*)','action_free(.*c.*)')
Model Checking Software Non Starvation: [-*. request(1,[A])] mu X.( true /\ [not reply(1,ok)]X) /\ [-*. request(2,[A])] mu X.( true /\ [not reply(2,ok)]X) /\... [-*. request(1,[B])] mu X.( true /\ [not reply(1,ok)]X) /\.... request(2,[B]) request(1,[A]) reply(1,ok) reply(2,ok)
Model Checking Software Non Starvation: [-*. request(1,[A])] mu X.( true /\ [not reply(1,ok)]X) /\ [-*. request(2,[A])] mu X.( true /\ [not reply(2,ok)]X) /\... tau request(1,[A]) reply(1,ok) Non-starvation per group of resources. Hide all actions of which resources are not in closure of resources you are intersted in Safety reduction (trace equivalence)
Model Checking Software Abstraction may be needed to keep state space finite: Generic abstraction mechanism by providing abstraction functions over messages and server state This allows to obtain a design from a program! not yet implemented in combination with CWI tool
Results Automatic state space generation from rich class of Erlang programs Several case studies evaluated: model checking for software is possible more work to do: more design patterns property patterns in Erlang style add generic abstraction mechanism without design patterns or comments largest case-study is about 300 lines of code
Future how many configurations do we need to check? fault tolerance: - restarting processes compositionality: - N lockers exchanging information - lockers on top of leader election