I am able to prove the following program using Frama-C, which is surprising because 1) there is an assert false, and 2) the loop invariant does not hold (array a holds 1's not 2's). Am I missing something here?
/*# requires \valid(a+ (0..9)) && \valid(b+ (0..9));
*/
void test_foo(int *a, char *b) {
int i = 0;
/*# loop invariant \forall int j; 0 <= j < i ==> a[j] == 2;
loop invariant \forall int j; 0 <= j < i ==> b[j] == 'a';
*/
for (int i = 0; i < 10; i++) {
a[i] = 1;
b[i] = 'a';
}
//# assert \false;
}
I am running frama-c as:
frama-c -wp -wp-invariants -wp-prover "why3:alt-ergo" -wp-model "Typed,int,real" -wp-par 8 -wp-timeout 15 -wp-out wp.out test.c
I see same behavior on both Sodium and Magnesium versions.
-wp-invariants is not meant to handle "normal" loop invariants such as the ones you have provided, but "general inductive invariants" in the sense of ACSL section 2.4.2. You can thus remove this option from your command line. Then some proof obligations fail as expected.
Note in addition that your loop annotations are incomplete: as warned by WP, you should have a loop assigns, such as
loop assigns i, a[0 .. 9], b[0 .. 9];
Then, in order to be able to prove this loop assigns, you will need to specify i interval of variation:
loop invariant 0<=i<=10;
Finally, the fact that -wp-invariants make proof obligations behave strangely in presence of normal loop annotations should probably be considered as a bug and reported on Frama-C's bts.
Related
In the following minimal example (slightly altered from Allan Blanchard's excellent tutorial -- section 3.2.3.3 Side Effects):
int h = 42;
/*#
requires \valid(a) && \valid(b);
requires \valid_read(a) && \valid_read(b);
ensures *a == \old(*b) && *b == \old(*a);
*/
void swap( int *a, int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
//int test_swap(void) {
int main(void) {
int a = 37;
int b = 91;
//# assert h == 42;
swap(&a, &b);
//# assert h == 42;
return 0;
}
With this code as is above, WP is able to prove the first //# assert h == 42; in main as expected from the discussion in the tutorial.
IF the function main is renamed test_swap WP is un-able to prove the first //# assert h == 42;
Why?
I can understand why EVA cares about the presence of main, but I can not understand why WP should even care, as I am only asking WP to prove the single function main (in one case) and test_swap (in the other case).
I am using Frama-C version: 25.0-beta (Manganese)
By default, Frama-C indeed assumes (at kernel level) that the main function is the entry point of the program. That means that WP can make some assumptions about the global context (basically the initial values of global variables), but also that we might have additional verification to perform on the function. For example, here, we could write:
//# requires 0 <= h <= 100 ;
int main(void) {
...
}
In such a case, WP generates a VC for the requires of the main function and proves it:
If one really wants a behavior where the main function is just handled as any other function, the Frama-C kernel must be parameterized with the -lib-entry option. In such a case:
WP does not try to prove the requires of main,
WP cannot prove that h is 42 in the first assert.
frama-c-gui b.c -lib-entry -wp
I believe I am missing something obvious, but I have tried a lot and I haven't managed to find the source of the problem.
I am following the acsl guide from Frama-C.
There is this introductory example of how to verify the correctness of finding the maximum value in an array:
/*# requires n > 0;
requires \valid(p+ (0 .. n-1));
ensures \forall int i; 0 <= i <= n-1 ==> \result >= p[i];
ensures \exists int e; 0 <= e <= n-1 && \result == p[e];
*/
int max_seq(int* p, int n) {
int res = *p;
for(int i = 0; i < n; i++) {
if (res < *p) { res = *p; }
p++;
}
return res;
}
However, running frama-c -wp -wp-prover alt-ergo samenum.c -then -report I get:
[wp] Warning: Missing RTE guards
[wp] samenum.c:8: Warning: Missing assigns clause (assigns 'everything' instead)
[wp] 2 goals scheduled
[wp] [Alt-Ergo] Goal typed_max_seq_ensures_2 : Timeout (Qed:1ms) (10s)
[wp] [Alt-Ergo] Goal typed_max_seq_ensures : Timeout (Qed:2ms) (10s)
[wp] Proved goals: 0 / 2
Alt-Ergo: 0 (interrupted: 2)
[report] Computing properties status...
--------------------------------------------------------------------------------
--- Properties of Function 'max_seq'
--------------------------------------------------------------------------------
[ - ] Post-condition (file samenum.c, line 3)
tried with Wp.typed.
[ - ] Post-condition (file samenum.c, line 4)
tried with Wp.typed.
[ - ] Default behavior
tried with Frama-C kernel.
It seems that alt-ergo times out before proving the property. For what is worth, I tried with higher time-out but it still doesn't work.
I am using:
frama-c: 19.1
why3: 1.2.0
alt-ergo: 2.3.2
I am running this on Ubuntu 18.04 and before running the command I run: why3 config --detect to make sure why3 knows about alt-ergo.
Any ideas? Can anyone verify that this is example is not working?
Any help would be greatly appreciated!
This mini-tutorial was written quite a long time ago and is not really up to date. A new version of the website should appear in the upcoming months. Basically, this contract, as well as the invariant for the loop pointed by #iguerNL were meant to be verified using the Jessie plugin, and not the WP plugin of Frama-C. Among the differences between these two plugins, Jessie did not need assigns clauses for loops and functions, while WP needs them.
Thus, a complete annotated max_seq function could be this one:
/*# requires n > 0;
requires \valid(p+ (0..n−1));
assigns \nothing;
ensures \forall int i; 0 <= i <= n−1 ==> \result >= p[i];
ensures \exists int e; 0 <= e <= n−1 && \result == p[e];
*/
int max_seq(int* p, int n) {
int res = *p;
//# ghost int e = 0;
/*# loop invariant \forall integer j; 0 <= j < i ==> res >= \at(p[j],Pre);
loop invariant \valid(\at(p,Pre)+e) && \at(p,Pre)[e] == res;
loop invariant 0<=i<=n;
loop invariant p==\at(p,Pre)+i;
loop invariant 0<=e<n;
loop assigns i, res, p, e;
loop variant n-i;
*/
for(int i = 0; i < n; i++) {
if (res < *p) {
res = *p;
//# ghost e = i;
}
p++;
}
return res;
}
where we specify that the functions does not assign anything in memory, and that the loop assigns the different local variables i, res, p and e (thus leaving n unchanged).
Note that more recent tutorials are available to learn about the use of Frama-C with the WP plugin. The future version of the Frama-C website mentions:
A comprehensive tutorial on ACSL and WP
Another tutorial, with specifications inspired notably from C++ containers
You probably forgot to add invariants for the "for" loop. See Section "10.2 Loop Invariants" in the manual you gave (https://frama-c.com/acsl_tutorial_index.html)
I'm using Frama-C version Silicon-20161101. Every time a reference a pointer value *x in an ensures clause the preprocessor inserts *\old(x) unnecessarily. For example
// File swap.c:
/*# requires \valid(a) && \valid(b);
# ensures A: *a == \old(*b) ;
# ensures B: *\at(\old(b),Post) == \old(*a) ;
# assigns *a,*b ;
#*/
void swap(int *a,int *b)
{
int tmp = *a ;
*a = *b ;
*b = tmp ;
return ;
}
when processed with frama-c swap.c -print outputs
/* Generated by Frama-C */
/*# requires \valid(a) ∧ \valid(b);
ensures A: *\old(a) ≡ \old(*b);
ensures B: *\old(b) ≡ \old(*a);
assigns *a, *b;
*/
void swap(int *a, int *b)
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
return;
}
Interestingly enough, this is still verified as correct by the WP plugin! I assume this is because *\old(a) is the post value of \old(a) (which is still the same pointer since it hasn't been changed)? Is this a bug? Is there any quick user end fix for this?
There are two points in your question. First, *\old(a) should not be confused with \old(*a): in both cases, we evaluate the pointer a in the Old state, but in *\old(a), we dereference it in the current state, while in \old(*a), the dereference is also done in the Old state.
Second, formal parameters are a bit special: as mentioned in the ACSL manual, in the post-state of the function they have the same the value as in the pre-state, even if they have been modified by the function, such as in the following:
/*#
requires \valid(a + 0 .. 1);
ensures *a == \old(*a) + 1;
ensures a[1] == \old(a[1]) + 1;
*/
void f(int *a) {
*a = *a + 1;
a++;
*a = *a + 1;
}
The rationale here is that since C has a call-by-value semantics, the outside world cannot see any internal modification made to the formal (which acts as a local variable for this purpose). In other words, as long as the contract is concerned, the value tied to a in the example above is the one of the actual argument passed to it, no matter what happens in the implementation of the function. Note that in the case of a pointer, this of course applies only to the value of the pointer itself, not to the values stored in the memory block it points to.
In order to make this semantics explicit, the type-checker wraps all occurrences of a formal parameter in an ensures clause inside \old.
You may also want to see this answer to a related, albeit not completely similar question.
I'm trying to prove a function like strlen in C, but frama-c don't prove the
post condition and the loop variant len clause. I can't understand why!
What I've tried:
/*#
axiomatic elementNumber_axioms
{
logic unsigned elementNumber{L}(char *a);
axiom elementNumber_base{L}:
elementNumber(\null) == 0;
axiom elementNumber_step{L}:
\forall char *a;
\valid(a) ==> elementNumber(a) == elementNumber(a+1) + 1;
}
*/
/*#
assigns \nothing;
ensures \result == elementNumber(\old(s));
*/
unsigned stringlen(const char *s)
{
unsigned len = 0;
/*#
loop assigns len;
loop assigns s;
loop variant len;
*/
while(*s)
{
++s;
++len;
}
return len;
}
What am I doing wrong?
There are several issues with what you have written. A non-exhaustive list:
Your stringlen() does not handle the case where s is NULL.
If annotating the standard C strlen() function, you would not need to handle this case because the standard C strlen() function does not allow the parameter to be NULL. However, the axiomatic definition of your elementNumber() logic function defines elementNumber(\null) to be 0, and a postcondition of stringlen() is that the result equals elementNumber(s). Thus, you would need to handle this case.
The while loop in stringlen() terminates when encountering a nul byte. However, the definition of your elementNumber() logic function only depends on whether a pointer is valid.
There are no preconditions on stringlen() as to whether s, s + 1, etc. are valid.
Your elementNumber() logic function does not define a value for an invalid pointer.
You will need to specify loop invariants.
I recommend taking a look at how Frama-C annotates strlen():
/*# requires valid_string_src: valid_string(s);
# assigns \result \from s[0..];
# ensures \result == strlen(s);
#*/
extern size_t strlen (const char *s);
The strlen() logic function and valid_string() predicate are defined in share/libc/__fc_string_axiomatic.h of the source distribution, which as of this writing is Sodium-20150201.
I have some troubles when I try to use the default logic labels LoopEntry and LoopCurrent. Here is a simple example the different provers (alt-ergo, coq, cvc3, z3) I use are not able to prove :
/*# requires n > 0;*/
void f(int n){
int i = 0;
/*# loop invariant \at(i,LoopEntry) == 0;
# loop invariant \at(i,LoopCurrent) >= \at(i,LoopEntry);
# loop invariant 0 <= i <= n;
# loop assigns i;
# loop variant n-i;
*/
while(i < n){
i++;
}
}
In particular, the first and second invariants are not proved (no problem with the others). Now if I modify this simple example by adding a label "label" after the declaration/definition of i and if I refer to that label, and change LoopCurrent by Here (which gives this snippet :
/*# requires n > 0;*/
void f(int n){
int i = 0;
label : ;
/*# loop assigns i;
# loop invariant \at(i,label) == 0;
# loop invariant \at(i,Here) >= \at(i,label);
# loop invariant 0 <= i <= n;
# loop variant n-i;
*/
while(i < n){
i++;
}
}
)
now everything is proved.
I found the documentation about Acsl default logic labels quite easy to understand and I expected the first example to be proved as the second. Could you explain where does the problem come from?
Roo
PS1 : what does Pre refer to when used in a loop clause? The state before first loop iteration or the previous iteration??
PS2 : I'm using Frama-C Fluorine, but maybe I didn't upgrade for every minor updates
LoopCurrent and LoopEntry are indeed not supported by WP in Fluorine. This is fixed in the development version (see http://bts.frama-c.com/view.php?id=1353), and should appear in the next release.
Regarding the other pre-defined labels,
Pre always refers to the state at the beginning of the function.
Old can only be used in a contract, and refers to the pre-state of this contract (i.e. the state in which the requires and assumes clauses are evaluated). It is thus equivalent to Pre for a function contract, but not for a statement contract (unless you make a contract enclosing the main block of your function).
Here means the program point where the corresponding annotation is evaluated. In a contract, its meaning depends on the clause in which it appears.
Post can only be used in ensures, assigns, allocates or frees clauses, and refer to the state at the end of the contract.