model-checkingpromelaspin

Why an infinite loop doesn't result in an error in model checking with Promela and Spin?


If I write the following code in Promela and run it in Spin in verifier mode it ends with 0 errors. It does report that toogle and init had unreached states, but those seem to be only warnings.

byte x = 0; byte y = 0;
active proctype toggle() {
    do
    :: x == 1 -> x = 0
    :: x == 0 -> x = 1
    od
}
init {
    (y == 1);
}

I was confused by this because I thought this would give me a 'invalid end state' error. If I change the body of the toogle proctype with a simple skip statement it does error out as I expected.

Why is this? Is there a way to force the simulator to report the infinite loop as an error?

Regarding the 'unreached in proctype' messages, adding an end label to the do loop doesn't seem to do anything.

I am running spin 6.5.0 and ran the following commands:

spin.exe -a test.pml
gcc -o pan pan.c
pan.exe

These are the outputs, for reference.

With do loop:

pan.exe

(Spin Version 6.5.0 -- 1 July 2019)
        + Partial Order Reduction

Full statespace search for:
        never claim             - (none specified)
        assertion violations    +
        acceptance   cycles     - (not selected)
        invalid end states      +

State-vector 20 byte, depth reached 3, errors: 0
        4 states, stored
        1 states, matched
        5 transitions (= stored+matched)
        0 atomic steps
hash conflicts:         0 (resolved)

Stats on memory usage (in Megabytes):
    0.000       equivalent memory usage for states (stored*(State-vector + overhead))
    0.292       actual memory usage for states
   64.000       memory used for hash table (-w24)
    0.343       memory used for DFS stack (-m10000)
   64.539       total actual memory usage


unreached in proctype toggle
        ..\test2.pml:7, state 8, "-end-"
        (1 of 8 states)
unreached in init
        ..\test2.pml:10, state 2, "-end-"
        (1 of 2 states)

pan: elapsed time 0.013 seconds
pan: rate 307.69231 states/second

With skip:

pan.exe
pan:1: invalid end state (at depth 0)
pan: wrote ..\test2.pml.trail

(Spin Version 6.5.0 -- 1 July 2019)
Warning: Search not completed
        + Partial Order Reduction

Full statespace search for:
        never claim             - (none specified)
        assertion violations    +
        acceptance   cycles     - (not selected)
        invalid end states      +

State-vector 20 byte, depth reached 1, errors: 1
        2 states, stored
        0 states, matched
        2 transitions (= stored+matched)
        0 atomic steps
hash conflicts:         0 (resolved)

Stats on memory usage (in Megabytes):
    0.000       equivalent memory usage for states (stored*(State-vector + overhead))
    0.293       actual memory usage for states
   64.000       memory used for hash table (-w24)
    0.343       memory used for DFS stack (-m10000)
   64.539       total actual memory usage



pan: elapsed time 0.015 seconds
pan: rate 133.33333 states/second

Solution

  • In this example

    byte x = 0; byte y = 0;
    active proctype toggle() {
        do
        :: x == 1 -> x = 0
        :: x == 0 -> x = 1
        od
    }
    init {
        (y == 1);
    }
    

    the init process is blocked forever (because y == 1 is always false), but the toggle process can always execute something. Therefore, there is no invalid end state error.

    Instead, in this example

    byte x = 0; byte y = 0;
    active proctype toggle() {
        skip;
    }
    init {
        (y == 1);
    }
    

    the init process is still blocked forever, but the toggle process can execute its only instruction skip; and then terminate. At this point, none of the remaining processes (i.e. only init) has any instruction it can execute, so Spin terminates with an invalid end state error.

    ~$ spin -a -search test.pml
    pan:1: invalid end state (at depth 0)
    pan: wrote test.pml.trail
    
    (Spin Version 6.5.0 -- 17 July 2019)
    ...
    State-vector 20 byte, depth reached 1, errors: 1
    ...
    

    Is there a way to force the simulator to report the infinite loop as an error?

    Yes. There are actually multiple ways.

    The simplest approach is to use option -l of Spin:

    ~$ spin --help
    ...
    -l: search for non-progress cycles
    ...
    

    With this option, Spin reports any infinite-loop which does not contain any state with a progress label.

    This is the output on your original problem:

    ~$ spin -search -l test.pml
    pan:1: non-progress cycle (at depth 2)
    pan: wrote test.pml.trail
    
    (Spin Version 6.5.0 -- 17 July 2019)
    ...
    State-vector 28 byte, depth reached 9, errors: 1
    ...
    
    ~$ spin -t test.pml
    spin: couldn't find claim 2 (ignored)
      <<<<<START OF CYCLE>>>>>
    spin: trail ends after 10 steps
    #processes: 2
            x = 0
            y = 0
     10:    proc  1 (:init::1) test.pml:10 (state 1)
     10:    proc  0 (toggle:1) test.pml:5 (state 4)
    2 processes created
    

    An alternative approach is to use LTL model checking. For instance, you may state that at some point the number of processes (see _nr_pr) that are in execution becomes equal to 0 (or more, if you admit some infinite loops), or check that a particular process terminates correctly using remote references.

    Both cases are contained in the following example:

    byte x = 0; byte y = 0;
    active proctype toggle() {
        do
        :: x == 1 -> x = 0
        :: x == 0 -> x = 1
        od;
    end:
    }
    init {
        (y == 1);
    }
    
    // sooner or later, the process toggle
    // with _pid == 0 will reach the end
    // state
    ltl p1 { <> toggle[0]@end };
    
    // sooner or later, the number of processes
    // that are currently running becomes 0,
    // (hence, there can be no infinite loops)
    ltl p2 { <> (_nr_pr == 0) };
    

    Both the first

    ~$ spin -a -search -ltl p1 test.pml
    ~$ spin -t test.pml
    ltl p1: <> ((toggle[0]@end))
    ltl p2: <> ((_nr_pr==0))
      <<<<<START OF CYCLE>>>>>
    Never claim moves to line 4 [(!((toggle[0]._p==end)))]
    spin: trail ends after 8 steps
    #processes: 2
            x = 0
            y = 0
            end = 0
      8:    proc  1 (:init::1) test.pml:10 (state 1)
      8:    proc  0 (toggle:1) test.pml:3 (state 5)
      8:    proc  - (p1:1) _spin_nvr.tmp:3 (state 3)
    2 processes created
    

    and the second

    ~$ spin -a -search -ltl p2 test.pml
    ~$ spin -t test.pml
    ltl p1: <> ((toggle[0]@end))
    ltl p2: <> ((_nr_pr==0))
      <<<<<START OF CYCLE>>>>>
    Never claim moves to line 11    [(!((_nr_pr==0)))]
    spin: trail ends after 8 steps
    #processes: 2
            x = 0
            y = 0
            end = 0
      8:    proc  1 (:init::1) test.pml:10 (state 1)
      8:    proc  0 (toggle:1) test.pml:3 (state 5)
      8:    proc  - (p2:1) _spin_nvr.tmp:10 (state 3)
    2 processes created
    

    LTL properties are found to be false.


    Regarding the 'unreached in proctype' messages, adding an end label to the do loop doesn't seem to do anything.

    The end label(s) are used to remove the "invalid end state" error that would be otherwise be found.

    For example, modifying your previous example as follows:

    byte x = 0; byte y = 0;
    active proctype toggle() {
        skip;
    }
    
    init {
    end:
        (y == 1);
    }
    

    Makes the error go away:

    ~$ spin -a -search test.pml
    (Spin Version 6.5.0 -- 17 July 2019)
    ...
    State-vector 20 byte, depth reached 1, errors: 0
    ...
    

    One should only ever use an end label when one is willing to guarantee that a process being stuck with no executable instruction is not a symptom of an undesired deadlock situation.