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?
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 bymalloc
. 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)
Launch Frama-C with
In the code,
//@ split n
indicates that Eva should consider separately each possible value ofn
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 loopn
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 from0
(less precise than default) up to11
(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 tomalloc
it encounters. Otherwise, you'd get a single base for all lengths, defeating the purpose of splitting onn
in the first place.