SPIN Carla Ferreira http://www.spinroot.com/spin/man/intro.html Introduction PROMELA (PROcess MEta LAnguage) is the input language of SPIN (Simple Promela Interpreter) Inspired by: C and CSP Describes the model and part of the specification (other part: correctness claim as LTL formula) Generic verification system Used to verify distributed systems Correctness of process interactions 2 1
PROMELA Basic Elements Process Types and instances Local scope Variables Data types Arrays Statements/Conditions Channels FIFO queue (array) 3 Macro definitions #define name value ex: #define red 2 x = x+red 4 2
Processes (1) Process type proctype myprocess(parameters) {... } Process instantiation run myprocess(param_values) 5 Processes (2) Data arrays or process types are the only types that cannot be passed as parameters Process state defined by the values of its variables Special process: init 6 3
Data types Name bit / bool byte short int Range 0.. 1 0.. CHAR_BIT SHRT_MIN.. SHRT_MAX INT_MIN.. INT_MAX Typically false.. true 0.. 255-2 15-1.. 2 15-1 -2 32-1.. -2 15-1 7 Symbolic values Message types mtype = {value_names} ex: mtype = {red, green, blue} Special 0 is false Any non-0 value is true 8 4
Records C struct Typedef name { fieldtype1 fieldname1; fieldtype2 fieldname2;} Ex: Typedef picture{ int numcolors; int vert_resolution; int horz_resolution:} 9 Variables Declaration datatype variable_name ex: int counter Assignment variable_name = value ex: counter = 1 Test variable_name == value ex: counter == 0 10 5
Arrays Declaration elem_type array_name[size] ex: int vector[10] Element value array_name[index] ex: vector[0] 11 Statements (1) Statements and conditions are not differentiated: both are either executable or blocked Conditions are executablewhen true blocked when false Statements are executablewhen eligible for execution blocked when waiting for synchronization 12 6
Statements (2) Always executable Variable declarations, Assignments, printf Assertions true / non-0 values skip, goto, break Always blocked false and 0 values 13 Statements (3) Special case run is executable if a process of the specified type can be instantiated (memory limit, too many processes) Statement separators (where interleaving may occur) ; or -> 14 7
Atomic sequences Indivisible unit (no interleaving) atomic { statements } 15 Process communication (1) Via (buffered) channels Declaration chan channame = [size] of {msgtype} ex: chan com1 = [16] of {byte,int} Global or local 16 8
First example byte state = 1; proctype A() {byte tmp; (state==1) -> tmp=state; tmp=tmp+1; state=tmp} proctype B() {byte tmp; (state==1)->tmp=state; tmp=tmp-1; state=tmp} init { run A(); run B() } 17 Process communication (2) Sending a value on a channel channame!value Receiving a value on a channel channame?varname 18 9
Process communication (3) More than one value channame?value1,value2,... Convention: first value is message type (mtype) channame!mtype(value2,...) Test a receive statement channame?[values] 19 Process communication (4) Size of the channel buffer len(channame) Rendez-vous communication (synchronous): channel of buffer size 0 20 10
Second example proctype A(chan q1) { chan q2; q1?q2; q2!123 } proctype B(chan qforb) { int x; qforb?x; printf( x= %d\n,x) } init { chan qname = [1] of {chan}; chan qforb = [1] of {int}; run A(qname); run B(qforb); qname!qforb} 21 Control flow (1) Case selection if :: statement1 :: statement2 fi ex: if :: (a==b) -> option1 :: (a!=b) -> option2 fi 22 11
Control flow (2) Repetition do :: statement1 :: statement2 od Terminating the repetition: break 23 Control flow (3) Unconditional jump Declare a label mylabel:... Jump to that label goto mylabel Three special kinds of labels end, progress, accept 24 12
Pseudo-statements Timeout do :: statement1 :: timeout -> statement2 Od Else if :: statement1 :: else -> statement2 fi 25 Examples Three Values Channel Communication Factorial Mutual Exclusion Lift Needham-Schroeder (presented later) 26 13
Three Values byte state = 1; proctype A() {byte tmp; (state==1) -> tmp=state; tmp=tmp+1; state=tmp} proctype B() {byte tmp; (state==1) -> tmp=state; tmp=tmp-1; state=tmp} init {run A(); run B() } 27 Channel Communication proctype A(chan q1) { chan q2; q1?q2; q2!123 } proctype B(chan qforb) { int x; qforb?x; printf("x= %d\n",x) } init { chan qname = [1] of {chan}; chan qforb = [1] of {int}; run A(qname); run B(qforb); qname!qforb} 28 14
Factorial proctype fact(int n; chan p) { chan child = [1] of {int}; int result; } if :: (n <= 1) -> p!1; :: (n >= 2) -> run fact(n-1,child); child?result; p!n*result fi init { chan child = [1] of {int}; int result; } run fact(7,child); child?result; printf("result: %d\n", result) 29 Factorial $ spin -s -r factorial.spin 9: proc 4 (fact) line 6 "factorial.spin" Send 1 -> queue 4 (p) Process 4 has terminated 12: proc 3 (fact) line 8 "factorial.spin" Recv 1 <- queue 4 (child) 13: proc 3 (fact) line 8 "factorial.spin" Send 2 -> queue 3 (p) Process 3 has terminated 16: proc 2 (fact) line 8 "factorial.spin" Recv 2 <- queue 3 (child) 17: proc 2 (fact) line 8 "factorial.spin" Send 6 -> queue 2 (p) 18: proc 1 (fact) line 8 "factorial.spin" Recv 6 <- queue 2 (child) 19: proc 1 (fact) line 8 "factorial.spin" Send 24 -> queue 1 (p) 20: proc 0 (:init:) line 18 "factorial.spin" Recv 24 <- queue 1 (child) result: 24 Process 1 has terminated Process 2 has terminated 5 processes created 15
Mutual Exclusion #define Aturn false #define Bturn true bool x, y, t; proctype A() { x = true; t = Bturn; (y == false t == Aturn); /* critical section */ x = false} proctype B() { y = true; t = Aturn; (x == false t == Bturn); /* critical section */ y = false} init { run A(); run B() } 31 Semaphore #define p 0 #define v 1 chan sema = [0] of {bit}; proctype dijkstra() { byte count = 1; do :: (count == 1) -> sema!p; count = 0 :: (count == 0) -> sema?v; count = 1 od} proctype user() { do ::sema?p; /* critical section */ sema!v od} init { run dijstra(); run user(); run user(); run user() } 32 16
Lift bit doorisopen[3]; chan openclosedoor = [0] of {byte, bit}; proctype door(byte i) { do :: openclosedoor?eval(i),1; doorisopen[i-1] = 1; doorisopen[i-1] = 0; openclosedoor!i,0 od} proctype lift() { show byte floor = 1; do :: (floor!= 3) -> floor++ :: (floor!= 1) -> floor-- :: openclosedoor!floor,1; openclosedoor?eval(floor),0 od} init { atomic { run door(1); run door(2); run door(3); run lift() }} 33 Analysing of Promela Models Simulation 3 modes: Interactive Random Guided Provides immediate (and intuitive) feedback Can be viewed as executing the model Automated Proof By automatically exploring the state space corresponding to the model in a particular search mode to check some correctness properties Output: model is correct or counter-example 34 17
General structure Xspin LTL parser PROMELA parser and translator Simulation Verifier (analyzer) generator C Pre-processor/Compilation Counter-example Execution 35 Safety and liveness Safety: nothing bad ever happens ex.: deadlock freedom Find a trace leading to the bad thing; if there is not such a trace, the property is satisfied Liveness: something good will eventually happen ex.: termination Find a loop in which the good thing does not happen; if there is not such a loop, the property is satisfied 36 18
Correctness properties Safety or liveness Specified in the model: Assertions (local, global) Labels: progress (starvation), acceptance (deadlock), end (livelock) Specified outside the model: LTL Formula 37 Local properties Assertions assert(p) assert(true) assert(false) 38 19
Global property Invariant proctype monitor() { assert(p); } Variant forms atomic{!p -> assert(p);} do :: assert(p) od 39 Correctness properties Label progress:... accept:... end:... 40 20
End-state label How to differentiate between idle and normal end of a process? Specify valid end-state Statement where the blocking of the process is normal (acceptable) Useful when looking for deadlocks 41 Progress-state label The statement indicates that the system makes progress Non-progress may indicate starvation or badly designed system 42 21
Acceptance-state label When checking for acceptance cycles, the verifier will complain if there is an execution that visits infinitely often an acceptance state. 43 Example: Semaphore #define p 0 #define v 1 chan sema = [0] of {bit}; proctype dijkstra() { byte count = 1; end: do :: (count == 1) -> progress: sema!p; count = 0 :: (count == 0) -> sema?v; count = 1 od} proctype user() { do ::sema?p -> accept: sema!v od} init { run dijstra(); run user(); run user(); run user() } 44 22
LTL syntax True, false Unary operators: [], <>,! Binary operators: U, &&,, ->, <-> 45 Typical LTL formulas [] p <> p p U q p -> (<> q) p -> (q U r) [] <> p <> [] p <> p -> <> q always p eventually p p until q p implies eventually q p implies q until r always eventually p eventually, always p eventually p implies eventually q 46 23
LTL formulas: Lift Example #define open1 doorisopen[0] #define open2 doorisopen[1] #define open3 doorisopen[2] eventually one of the three doors will open <> (open1 open2 open3) 47 State-space explosion Generally in the model because usual correctness properties are short Must be dealt with: Rewriting the model Simplifying the correctness properties Verifying each property independently 48 24
Protocolo Needham-Schroeder A B 1 {Na.A} PK(B) 2 {Na.Nb} PK(A) 3 {Nb} PK(B) 49 Data mtype = {A, B, I, Na, Nb, gd, R}; chan ca = [0] of {mtype, mtype, mtype, mtype}; chan cb = [0] of {mtype, mtype, mtype}; /* A sends message {Na,A} PK(B) to B */ ca! A, Na, A, B 50 25
Initiator Process proctype PIni (mtype self; mtype party; mtype nonce) { mtype g1; atomic { IniRunning(self,party); ca! self, nonce, self, party; } atomic { ca? _ eval (nonce), eval (self); IniCommit(self,party); cb! self, g1, party; } } 51 LTL Properties bit IniRunningAB = 0; /* initiator A takes part in a session with B */ bit IniCommitAB = 0; /* initiator A commits to a session with B */ bit ResRunningAB = 0; /* responder B takes part in a session with A */ bit ResCommitAB = 0; /* responder B commits to a session with A */ ResRunningAB must become true before IniCommitAB [] ( ([]!IniCommitAB) (!IniCommitAB U ResRunningAB) ) IniRunningAB becomes true before ResCommitAB [] ( ([]!ResCommitAB) (!ResCommitAB U IniRunningAB) ) #define IniRunning(x,y) if :: ((x==a)&&(y==b))-> IniRunningAB=1 :: else skip fi 52 26
Responder Process proctype PRes (mtype self; mtype nonce) { mtype g2, g3; } atomic { ca? _, g2, g3, eval (self); ResRunning(g3,self); ca! self, g2, nonce, g3; } atomic { cb? eval (g3), eval (nonce), eval (self); ResCommit(g3,self); } 53 Protocol Instanciation Init { atomic { if :: run PIni (A, I, Na) :: run PIni (A, B, Na) fi; run PRes (B, Nb); } } run PI (); 54 27
Intruder Process proctype PI () { /* The intruder always knows A, B, I, PK(A), PK(B), PK(I), SK(I) and gd */ bit kna = 0; /* Intruder knows Na */ bit knb = 0; /* Intruder knows Nb */ bit k_na_nb A = 0; /* Intruder knows {Na, Nb}{PK(A)} */ bit k_na_a B = 0; /* " " {Na, A}{PK(B)} */ bit k_nb B = 0; /* " " {Nb}{PK(B)} */ mtype x1 = 0, x2 = 0, x3 = 0; do :: ca! B, gd, A, B :: ca! B, gd, B, B :: ca! B, gd, I, B :: ca! B, A, A, B :: ca! B, A, B, B :: ca! B, A, I, B :: ca! B, B, A, B :: ca! B, B, B, B :: ca! B, B, I, B :: ca! B, I, A, B :: ca! B, I, B, B :: ca! B, I, I, B :: ca! (kna -> A : R), Na, Na, A :: ca! (((kna && knb) k_na_nb A) -> A : R),Na, Nb, A :: ca! (kna -> A : R), Na, gd, A :: ca! (kna -> A : R), Na, A, A :: ca! (kna -> A : R), Na, B, A :: ca! (kna -> A : R), Na, I, A :: ca! ((kna kna_a B) -> B : R), Na, A, B :: ca! (kna -> B : R), Na, B, B :: ca! (kna -> B : R), Na, I, B :: ca! (knb -> B : R), Nb, A, B :: ca! (knb -> B : R), Nb, B, B :: ca! (knb -> B : R), Nb, I, B :: cb! ((k_nb B k_nb) -> B : R), Nb, B 55 Intruder Process (2/2) } :: d_step { ca? _, x1, x2, x3; if :: (x3 == I)-> k(x1); k(x2) :: else k3(x1,x2,x3) fi; x1 = 0; x2 = 0; x3 = 0; } :: d_step { cb? _, x1, x2; if :: (x2 == I)-> k(x1) :: else k2(x1,x2) fi; x1 = 0; x2 = 0; } od #define k(x1) if :: (x1 == Na)-> kna = 1 :: (x1 == Nb)-> knb = 1 :: else skip fi; 56 28
Protocolo Needham-Schroeder Simulation & Verification with SPIN 57 29