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)
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 have the following loop executed in my OpenCl kernel:
__kernel void kernelA(/* many parameters */)
{
/* Prefetching code and other stuff
* ...
* ...
*/
float2 valueA = 0.0f;
#pragma unroll //<----- line X
for(unsigned int i = 0; i < MAX_A; i++) // MAX_A > 0
{
#pragma unroll
for(unsigned int j = 0; j < MAX_B; j++) // MAX_B > 0
valueA += arrayA[(i * MAX_A) + j];
}
/*
* Code that uses the result saved to valueA
*/
}
As can be seen clearly the loop shall summarize values contained in arrayA. Now I wanted to try the #pragma unroll to see whether there is any performance difference between looped and unrolled execution.
But when I compile the kernel, the compiler notes LOOP UNROLL: pragma unroll (line X) ignored because this loop is dead and deleted. I don't understand that information, because the code in the loop is surely executed. MAX_A and MAX_B are definitely greater than zero and the the sum saved to valueA is also used after the loop.
I have the same structure somewhere else in the code and also this position is marked by the upper note.
The compiler I use is the AMD OpenCL C compiler delivered by the APP SDK.
The comment by #DarkZeroes is the solution of this question. There was no instruction to put the result into an output array of the kernel, so the code above and everything what depended on that was optimized away by the compiler.
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.
For the below C function, I'm getting syntax errors from Alt-Ergo for the latest version of Frama-c.
frama-c -wp -wp-rte -lib-entry RoundNearestFive.c -wp-out temp -wp-model="nat, real"
I'm not sure what is wrong in this generated line:
...
let r_0 = dat_0 / 5.0e0 : real in /* syntax error here */
...
C function under analysis
typedef unsigned short int uint16;
/*#
# requires 0<=dat<= 300;
*/
uint16 RoundNearestFive(float dat)
{
uint16 result= 0;
float fra = 0;
result = (uint16)(dat/5);
fra = dat - (float)result*5; // fractional part of the input
if (fra < 2.5)
result = (uint16) (dat-fra);
else
result = (uint16) (dat+(5-fra));
return result;
}
I tried Alt-Ergo (version 0.95.2 and trunk) on the formula below and I got no syntax error. Are you using an old version of Alt-Ergo ? Or maybe the syntax error is elsewhere.
--
logic dat_0 : real
goal g:
let r_0 = dat_0 / 5.0e0 : real in (* syntax error here *)
false
The WP user manual explicitly states that versions of Alt-Ergo prior to 0.95 are not supported (see page 21).
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.