Meaning of \\old in ACSL post-conditions

2019-05-11 02:22发布

问题:

I am a newbie user of Frama-C and have a few questions regarding assertions over pointers.

Consider the C fragment below involving:

  • two related data structures Data and Handle, s.t. Handle has a pointer to Data;
  • a 'state' field in Data indicating whether some hypothetical operation has completed
  • three functions: init(), start_operation() and wait();
  • a main() function using the above, and containing 6 assertions (A1-A6)

Now, why is it that A5 and A6 cannot be asserted with the WP verifier ("frama-c -wp file.c") Shouldn't they hold due to the last "ensures" clause on start_operation()?

What am I doing wrong?

Best,

Eduardo

typedef enum 
{ 
  PENDING, NOT_PENDING
} DataState;

typedef struct 
{
  DataState state;
  int value;  
} Data;


typedef struct 
{
  Data* data;
  int id;
} Handle;

/*@
  @ ensures \valid(\result);
  @ ensures \result->state == NOT_PENDING;
  @*/
Data* init(void);

/*@ 
  @ requires data->state == NOT_PENDING;
  @ ensures data->state == PENDING;
  @ ensures \result->data == data;
  @*/  
 Handle* start_operation(Data* data, int to);

/*@
  @ requires handle->data->state == PENDING;
  @ ensures handle->data->state == NOT_PENDING;
  @ ensures handle->data == \old(handle)->data;
  @*/  
 void wait(Handle* handle);


 int main(int argc, char** argv)
 {
  Data* data = init();
  /*@ assert A1: data->state == NOT_PENDING; */

  Handle* h = start_operation(data,0);
  /*@ assert A2: data->state == PENDING; */
  /*@ assert A3: h->data == data; */

  wait(h);
  /*@ assert A4: h->data->state == NOT_PENDING; */
  /*@ assert A5: data->state == NOT_PENDING; */
  /*@ assert A6: h->data == data; */

  return 0; 
}

回答1:

You are verifying some tricky memory manipulations for a “newbie”.

The ACSL construct \old does not work exactly like you think it does.

First, \old(handle) in a post-condition is the same as handle, because handle is a parameter. A contract is intended to be used from the point of view of callers. Even if the function wait modified handle (which would be unusual but is possible), its contract would still be intended to relate values passed as argument by the caller and the state returned by the function to the caller.

In short, in an ACSL post-condition, parameter always means \old(parameter) (even if the function modifies parameter like a local variable).

Second, the rule above is only for parameters. For global variables and memory accesses, the post-condition is considered to apply to the post-state, as you would expect from its name. The construct \old(handle)->data that you wrote, and that is equivalent to handle->data, means “the field .data that the old value of handle points to in the new state”, so that the post-condition handle->data == \old(handle)->data is a tautology and probably not what you intended.

From the context, it appears that you intended handle->data == \old(handle->data) instead, which means "the field .data that the old value of handle points to in the new state is equal to the field .data that the old value of handle points to in the old state”. With that change, all the assertions in your program get proved for me (using Alt-Ergo 0.93).