Dynamic array with Frama-C and Eva

情到浓时终转凉″ 提交于 2019-12-11 06:59:07

问题


In https://stackoverflow.com/a/57116260/946226 I learned how to verify that a function foo that operates on a buffer (given by a begin and end pointer) really only reads form it, but creating a representative main function that calls it:

#include <stddef.h>

#define N 100
char test[N];

extern char *foo(char *, char *);

int main() {
  char* beg, *end;
  beg = &test[0];
  end = &test[0] + N;
  foo(beg, end);
}

but this does not catch bugs that only appear when the buffer is very short.

I tried the following:

#include <stddef.h>
#include <stdlib.h>
#include "__fc_builtin.h"

extern char *foo(char *, char *);

int main() {
  int n = Frama_C_interval(0, 255);
  uint8_t *test = malloc(n);
  if (test != NULL) {
    for (int i=0; i<n; i++) test[i]=Frama_C_interval(0, 255);

    char* beg, *end;
    beg = &test[0];
    end = &test[0] + n;
    foo(beg, end);
  }
}

But that does not work:

[eva:alarm] frama-main.c:14: Warning: 
  out of bounds write. assert \valid(test + i);

Can I make it work?


回答1:


As mentioned in anol's comment, none of the abstract domains available within Eva is capable of keeping track of the relation between n and the length of the memory block returned by malloc. Hence, for all practical purposes, it will not be possible to get rid of the warning in such circumstances for a real-life analysis. Generally speaking, it is important to prepare an initial state which leads to precise bounds for the buffer that are manipulated throughout the program (while the content can stay much more abstract).

That said, for smaller experiments, and if you don't mind wasting (quite a lot of) CPU cycles, it is possible to cheat a little bit, by basically instructing Eva to consider each possible length separately. This is done by a few annotations and command-line options (Frama-C 19.0 Potassium only)

#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include "__fc_builtin.h"

extern char *foo(char *, char *);

int main() {
  int n = Frama_C_interval(0, 255);
  //@ split n;
  uint8_t *test = malloc(n);
  if (test != NULL) {
    //@ loop unroll n;
    for (int i=0; i<n; i++) {
      Frama_C_show_each_test(n, i, test);
      test[i]=Frama_C_interval(0, 255);
    }
    char* beg, *end;
    beg = &test[0];
    end = &test[0] + n;
    foo(beg, end);
  }
}

Launch Frama-C with

  frama-c -eva file.c \
          -eva-precision 7 \
          -eva-split-limit 256 \
          -eva-builtin malloc:Frama_C_malloc_fresh

In the code, //@ split n indicates that Eva should consider separately each possible value of n at this point. It goes along with the -eva-split-limit 256 (by default, Eva won't split if the expression can have more than 100 values). //@ loop unroll n ask to unroll the loop n times instead of merging results for all steps.

For the other command-line options, -eva-precision 7 sets the various parameters controlling Eva precision to sensible values. It goes from 0 (less precise than default) up to 11 (maximal precision - don't try it on anything more than a dozen of lines). -eva-builtin malloc:Frama_C_malloc_fresh instructs Eva to create a fresh base address for any call to malloc it encounters. Otherwise, you'd get a single base for all lengths, defeating the purpose of splitting on n in the first place.



来源:https://stackoverflow.com/questions/57168439/dynamic-array-with-frama-c-and-eva

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!