Slide 1

Slide 1 text

Modifications in the Frama-C framework for handling Linux kernel code Denis Efremov, Mikhail Mandrykin, Alexey Khoroshilov CEA, Paris, France

Slide 2

Slide 2 text

Toolset Frama-C/AstraVer(Jessie)/Why3 • Official page https://forge.ispras.ru/projects/astraver/wiki $ opam switch create astraver 4.07.1 $ opam repo add ispras https://forge.ispras.ru/git/astraver.opam-repository.git $ opam update $ opam install frama-c astraver why3 altgr-ergo coq coqide • Travis-CI https://github.com/evdenis/verker/blob/master/.travis.yml

Slide 3

Slide 3 text

Automatic relevant code extraction in the deductive verification plugin (1) • Motivation • Reduce and simplify the code for the deductive analysis • Implementation • Take into the analysis scope only code with ACSL specification • Replace the irrelevant structure fields with stubs • Inline enum constants • …

Slide 4

Slide 4 text

Automatic relevant code extraction in the deductive verification plugin (2) struct test { int a; struct unused { int d; int e; } b; } t1; //@ requires \true; void test1(int a) { t1.a = a; } void test2(int d) { t1.b.d = d; } struct test { int a; int __padding[2]; } t1; //@ requires \true; void test1(int a) { t1.a = a; }

Slide 5

Slide 5 text

File dependent VC generation test2.c #include "test2.h" /*@ ensures \result == 0; */ int test2(void) { return test1(); } test2.h /*@ ensures \result == 0; */ int test1(void) { return 0; } $ frama-c –av test2.c $ frama-c –av test2.c test2.h * Mainly for lemma functions. Usually you don’t want to prove them every time you include a file.

Slide 6

Slide 6 text

Support for an arbitrary order of logic definitions • There is no forward declaration in ACSL • Definitions should be written before the first use • This limitation was removed in AstraVer plugin • Direct recursion in a definition is allowed • Why3 doesn’t allow indirect recursion in the predicates struct a; struct b {struct a *pa;}; struct a {struct b *pb;struct a *pa}; //@ requires valid_a(a); void order(struct a *a) {} /*@ predicate valid_a(struct a *pa) = \valid(pa) && (pa->pa == \null || valid_a(pa->pa)) && valid_b(pa->pb); */ /*@ predicate valid_b(struct b *pb) = \valid(pb) && pb->pa == \null; */

Slide 7

Slide 7 text

Simple context management for logic definitions • Only global_lemma1 is in the global scope • lemma1, lemma2 are not imported to the VCs by default • To “import” a lemma in the verification task a predicate or a logic function from the block should be used • Allows one to keep global context clean /*@ axiomatic lemma1 { lemma lemma1: \true != \false; predicate import_lemma1 = \true; }*/ /*@ axiomatic axs { logic boolean prd; axiom ax1: prd == \true; lemma lemma2: prd == \true ; }*/ //@ lemma global_lemma1: 1/0 == 1/0; /*@ requires import_lemma1; requires prd;*/ int main(void) { return 0; }

Slide 8

Slide 8 text

Logic Import • $ frama-c –av import.c b.c c.c • Sometimes it’s not allowed to modify the file structure of the project • Even add “#include” in the sources • “Import” allows one to import logic declarations from other files • Or if you don’t want to “#include” all definitions but to import a single import.c //@ import "b.c" (valid_b); //@ import "c.c" (*); /*@ requires valid_b(b); requires valid_c(c); */ int test(int b, int c) { return b + c; } c.c /*@ predicate valid_c(int c) = -100 <= c <= 100; */ b.c /*@ predicate valid_b(int b) = -10 <= b <= 10; */

Slide 9

Slide 9 text

Limited support for function pointers /*@ requires \valid(s) && flag == 0; ensures \result == 0; */ int dummy_callback1(int flag, char *s); /*@ requires \valid(s); ensures \result == 1; */ int dummy_callback2(int flag, char *s); //@ ensures \result == 0; int dummy_callback3(); struct callbacks { int (*cb1) (int, char *); int (*cb2) (void); }; struct outer { struct callbacks *cbs; }; /*@ requires \valid(p) && \valid(p->cbs); requires p->cbs->cb1 == &dummy_callback1 || p->cbs->cb1 == &dummy_callback2; requires p->cbs->cb2 == (int (*)(void))&dummy_callback3; ensures \result == 0 || \result == 1; */ int caller(struct outer *p) { return p->cbs->cb1(0, 0, "const char") || p->cbs->cb2(); } • All possible values for a pointer should be specified • VCs will be generated for every function

Slide 10

Slide 10 text

Improvements in the reinterpretation of memory for pointers to integral types (1) • Works for integral types only • Respects endianness • Switch between different memory representations • Not possible to “view” the memory in two different representations at the same time /*@ requires n >= 0; requires \valid(p + (0..n-1)); */ void int_access(int *p, int n) { if (n > 0) p[n-1] = 0; } void example(void) { int p[1000]; //@ av pragma p :> char *; char *q = (char *) p; //@ assert \base_addr(q) == q; //@ assert \valid(q + (0..3999)); //@ av pragma q :> int *; int_access(p, 1); }

Slide 11

Slide 11 text

Special predicates uint\d\d_as_uint\d\d (1) // To import /*@ axiomatic casts { // true if “a” consists from “a1” and “a2”, respects endianness predicate uint16_as_uint8(unsigned short a, // import unsigned char a1, unsigned char a2); } */ /*@ requires \typeof(p) <: \type(unsigned short *) && \valid((unsigned short *)p); allocates \nothing; assigns *((unsigned short *)p); ensures uint16_as_uint8(*(unsigned short *)p, (unsigned char) (v & (unsigned short)0xff), (unsigned char) ((v >> (unsigned short)8) & (unsigned short)0xff)); */ void set_w16(void *p, unsigned short v);

Slide 12

Slide 12 text

Special predicates uint\d\d_as_uint\d\d (2) void set_w16(void *p, unsigned short v) { unsigned short *tmp = p; //@ av pragma tmp :> unsigned char *; unsigned char *ptr = (unsigned char *) tmp; ptr[0] = v & 0xff; ptr[1] = (v >> 8) & 0xff; //@ av pragma ptr :> unsigned short *; /*@ assert \forall unsigned short *q; \base_addr(q) == \base_addr(tmp) ==> (0 <= tmp - q <= 0 <==> tmp == q); */ }

Slide 13

Slide 13 text

Template specifications for standard mem* functions (1) /*@ requires n >= 0 && n % (sizeof (_type)) == 0 && \let _n = n / sizeof (_type); \valid(((_type *) dest)+(0 .. _n - 1)) && \valid(((_type *) src)+(0 .. _n - 1)) && separated__type{Pre, Pre}((_type *) dest,(_type *) src, n); assigns ((_type *) dest)[0 .. (n / sizeof (_type)) - 1] \from ((_type *) src)[0 .. (n / sizeof (_type)) - 1]; allocates \nothing; ensures memcmp__type{Here, Here}((_type *) dest, (_type *) src, n); ensures \result == dest; */ extern _type *memcpy__type(_type *restrict dest, const _type *restrict src, size_t n);

Slide 14

Slide 14 text

Template specifications for standard mem* functions (2) // To import /*@ axiomatic memcmps { predicate memcmp_int{L1, L2}(int *a, int *b, size_t n); predicate separated_int(int *a, int *b, unsigned int n); } */ int test(int *a, int *b) { memcpy(a, b, sizeof (int)); //@ assert memcmp_int{Here, Here}(a, b, (unsigned long long) sizeof (int)); return 0; }

Slide 15

Slide 15 text

Allocates and \null • Malloc function could return \null, zero-sized pointer in case requested size == 0, or a valid pointer • allocates a && a == \null ==> allocates \nothing; • Useful in case of a multiple allocations in a function /*@ assigns \nothing; allocates \result; ensures \valid(\result) || \result == \null; ensures a <= 0 ==> \result == \null; */ int *atest(int a) { if (a > 0) return malloc(sizeof(int)); return NULL; } /*@ requires \valid(a); assigns *a; allocates \nothing; */ void test(int **a) { *a = atest(0); }

Slide 16

Slide 16 text

Per-operation switch between integer models in arithmetic terms and expressions char *strnchr(const char *s, size_t count, int c) { for (; count-- && *s != '\0'; ++s) if (*s == (char)c) return (char *)s; return NULL; } • The underflow of an unsigned loop iterator at the last iteration step due to the postfix decrement;

Slide 17

Slide 17 text

Per-operation switch between integer models in arithmetic terms and expressions char *strnchr(const char *s, size_t count, int c) { for (; count-- /*@%*/ && *s != '\0'; ++s) if (*s == (char)c) return (char *)s; return NULL; } • The underflow of an unsigned loop iterator at the last iteration step due to the postfix decrement;

Slide 18

Slide 18 text

Per-operation switch between integer models in arithmetic terms and expressions char *strnchr(const char *s, size_t count, int c) { for (; count-- /*@%*/ && *s != '\0'; ++s) if (*s == (char)c) return (char *)s; return NULL; } • The underflow of an unsigned loop iterator at the last iteration step due to the postfix decrement; • The intended cast to a smaller integer type;

Slide 19

Slide 19 text

Per-operation switch between integer models in arithmetic terms and expressions char *strnchr(const char *s, size_t count, int c) { for (; count-- /*@%*/ && *s != '\0'; ++s) if (*s == (char) /*@%*/ c) return (char *)s; return NULL; } • The underflow of an unsigned loop iterator at the last iteration step due to the postfix decrement; • The intended cast to a smaller integer type;

Slide 20

Slide 20 text

Why3 Sprove tool Differences between why3prove and why3sprove: • Sprove works with existing sessions • Sprove creates session if it doesn’t exists • Sprove saves its results to a session • Sprove supports strategies Motivation: sprove automatically proves most of the VCs with an appropriate strategy without requiring manual interaction with the ide