Search code examples
model-checkingspinpromela

Spin Model Checker does not find a bug when using remote varrefs


I am trying to formally verify mutual exclusion on the following Promela Model:

/* Mutex with (bugged) Peterson algorithm */

bool flag[2];
int turn = -1;

active [2] proctype agent() {
    bool in_cs;
    int other = 1 - _pid;

    do
        :: true ->

            /* trying section */
            turn = other;
            flag[_pid] = true;
            !( flag[other] && (turn == other));

            /* critical section */
            in_cs = true;
            printf("Agent %d in CS\n", _pid);
            in_cs = false;

            /* exit section */
            flag[_pid] = false;
    od;
}

ltl mutex { [] ! (agent[0]:in_cs && agent[1]:in_cs) }

To verify it, I use the following chain of commands:

~$ spin -a test.pml ; gcc -o run pan.c -DNOREDUCE ; ./run -a -N p1

which prints the following output:

...
Full statespace search for:
    never claim             + (mutex)
    assertion violations    + (if within scope of claim)
    acceptance   cycles     + (fairness disabled)
    invalid end states      - (disabled by never claim)

State-vector 40 byte, depth reached 53, errors: 0
...

Q: i know for a fact that the Promela Model does not satisfy mutual exclusion, but Spin claims that it does. Why is it?


NOTE: I added option -DNOREDUCE when compiling with gcc because Spin 6.4.8 prints a warning message requesting me to do so if I don't:

~$ spin -a test.pml ; gcc -o run pan.c ; ./run -a -N p1
...
pan.c: In function ‘main’:
pan.c:9478:3: warning: #warning p.o. reduction not compatible with remote varrefs (use -DNOREDUCE) [-Wcpp]
  #warning p.o. reduction not compatible with remote varrefs (use -DNOREDUCE)
   ^~~~~~~
...

Solution

  • Here is the solution:

    The problem here is that to disable partial order reduction you not only have to compile with -DNOREDUCE but also generate the model while disabling statement merging (merging of subsequent local actions) -- which is also a partial order reduction strategy. So, for the second example you also get the counter-example if you do:

    spin -o3 -a test.pml; gcc -DNOREDUCE -o pan pan.c; ./pan -a
    

    [G. H., private communication]

    The output of the suggested solution matches our expectation:

    ~$ spin -o3 -a test.pml ; gcc -o run pan.c -DNOREDUCE ; ./run -a -N p1
    ...
    pan:1: assertion violated  !( !( !((agent[0].in_cs&&agent[1].in_cs)))) (at depth 55)
    pan: wrote test.pml.trail
    
    ...
    
    Full statespace search for:
        never claim             + (mutex)
        assertion violations    + (if within scope of claim)
        acceptance   cycles     + (fairness disabled)
        invalid end states  - (disabled by never claim)
    
    State-vector 40 byte, depth reached 69, errors: 1
    ...
    

    Alternatively, one can circumvent the problem by using label remote references instead of variable remote references, e.g.:

    /* Mutex with (bugged) Peterson algorithm */
    
    bool flag[2];
    int turn = -1;
    
    active [2] proctype agent() {
        int other = 1 - _pid;
    
        do
            :: true ->
    
                /* trying section */
                turn = other;
                flag[_pid] = true;
                !( flag[other] && (turn == other));
    
                /* critical section */
    cs:
                printf("Agent %d in CS\n", _pid);
    
                /* exit section */
                flag[_pid] = false;
        od;
    }
    
    ltl mutex { [] ! (agent[0]@cs && agent[1]@cs) }
    

    With this Model, we find a counter-example, as expected:

    ~$ spin -search test.pml
    ...
    pan:1: assertion violated  !( !( !(((agent[0]._p==cs)&&(agent[1]._p==cs))))) (at depth 28)
    pan: wrote test.pml.trail
    ...