Previous Up Next

7  Aliasing

Pointer aliasing is a major source of complexity in the implementation of Blast. Blast comes with a flow insensitive and field insensitive Andersen's analysis for answering pointer aliasing questions internally. The implementation of the pointer analysis uses BDDs. Additionally, Blast allows the user to input alias information (generated from some other alias analysis) from a file. The syntax of an alias file is a list of C equalities between C memory expressions (variables, dereferences, field accesses) separated by commas.

Considering possibly aliased lvalues is essential for soundness of the analysis. Consider for example the following code:
int main() {
  int *a, *b;
  int i;
  i = 0;
  a = &i;
  b = &i;
  *b = 1 ;
  assert(*a == 1);
} 
If the analysis proceeds without considering the alias relationship between *a and *b, the assertion passes. However, updating *b also updates *a. The analysis is expensive if the alias information is not precise, since all (exponentially many) alias scenarios between the variables must be considered. In order to improve precision, Blast makes the (possibly unsound) assumption that the program is type-safe, so that only variables of the same type may be aliased. Moreover, the current implementation does not handle function pointers. Code with function pointer calls therefore cause Blast to fail with an exception, unless the flag -nofp is used, in which case all function pointer calls are ignored.

The option -alias is used to provide aliasing information to Blast. The option takes a string argument. If the argument is bdd then the BDD based Andersen's analysis is run. If it is some other string, then Blast assumes that the string indicates a filename where aliasing relationships are given. If the alias option is omitted, Blast makes the unsound assumption that there are no aliases in the program.
Exercise 1   Consider the program

#include <assert.h>

int __BLAST_NONDET;


void swap1(int *a, int *b) {
  int tmp = *a;
  *a = *b;
  *b = tmp;
}

void* malloc(int k);

void main () {
 
  int *i, *j;

  int v1, v2;

  i = malloc(4);
  j = malloc(4);

  *i = v1;
  *j = v2;

  swap1 (i, j);
  swap1 (i, j);
  assert (  *i == v1 &&   *j == v2 ); 
}
  1. Run Blast with
    
    pblast.opt foo.i -craig 1 -predH 7
    
    There is an error trace because Blast does not consider the aliasing among the variables. Now run Blastwith
    
    pblast.opt -alias bdd foo.i -craig 1 -predH 7
    
    Blast says that the system is safe.
  2. Now comment out the second call to swap1 in main. Check that Blast produces an error trace.
  3. Now add a second swap routine
    
    void swap2(int *a, int *b) {
      *a = *a + *b;
      *b = *a - *b;
      *a = *a - *b;
    } 
    
    Replace one of the calls to swap1 with swap2. Verify that Blast still proves the program correct.
  4. Consider the following variant of main.
    
    void main () {
     
      int *i, *j;
    
      int v1, v2;
    
      i = malloc(4);
      j = malloc(4);
    
      *i = v1;
    
      swap1 (i, i);
      assert (  *i == v1 ); 
    }
    
    Does the assertion hold? What happens if you replace swap1 with swap2? Run Blast and verify in each case.


Previous Up Next