Why can small size of array be proved, but can't large one? - frama-c

I tried to prove a example from frama-c-wp-tutorial
That example is at Sect.6.2.4, but I modified some code.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define size 150
/*#
axiomatic Counter{
logic integer counter{L}(bool arr[size], integer begin, integer end) reads arr[0 .. (size-1)];
axiom count_empty_range{L}:
\forall bool arr[size], integer begin, integer end;
begin<0 || begin>=end || end>size
==> counter{L}(arr, begin, end) == 0;
axiom count_others{L}:
\forall bool arr[size], integer begin, integer end;
0 <= begin < end <= size && arr[end-1] != true
==> counter{L}(arr, begin, end) == counter{L}(arr, begin, end-1);
axiom count_true{L}:
\forall bool arr[size], integer begin, integer end;
0 <= begin < end <= size && arr[end-1] == true
==> counter{L}(arr, begin, end) == counter{L}(arr, begin, end-1) + 1;
}
lemma counter_range{L}:
\forall bool arr[size], integer begin, integer end;
0 <= begin < end <= size
==> 0 <= counter(arr, begin, end) <= end-begin;
*/
The result was obtained with Frama-c 25.0, Alt-Ergo 2.4.2, cvc4 1.8 and Z3 4.8.6.
$ frama-c -wp -wp-rte -wp-prover alt-ergo,cvc4,z3 test.c
[kernel] Parsing test.c (with preprocessing)
[wp] 1 goal scheduled
[wp] [Failed] Goal typed_lemma_counter_range
Z3 4.8.6: Timeout (10s) (cached)
CVC4 1.8: Timeout (10s) (cached)
Alt-Ergo 2.4.2: Timeout (10s) (cached)
[wp] [Cache] found:3
[wp] Proved goals: 0 / 1
Alt-Ergo 2.4.2: 0 (interrupted: 1) (cached: 1)
CVC4 1.8: 0 (interrupted: 1) (cached: 1)
Z3 4.8.6: 0 (interrupted: 1) (cached: 1)
The thing that I can't understand is it will be proved successfully if I change the size into 14 (or even less).Is there anything wrong?

I suspect that with a smaller size the provers are able to exhaustively unfold all possible instantiations of the axioms count_others and count_true until they can apply count_empty_range, while this becomes of course impossible for a larger value.
In fact, what you want to do is a proof by induction, something first order automated solvers can't really do. Fortunately, there's a tactic dedicated to that in WP's interactive prover in Frama-C's GUI (see WP manual, section 2.2). Namely, while editing the script corresponding to the proof of the lemma, you can select the end_0 variable, and then the induction tactic:
(NB: the list of tactics on the right side of the panel depends on the context, hence you won't see the induction tactic until you select an integer variable).
Launching the tactic should complete the proof. You can then save the script and replay it later with frama-c -wp -wp-rte -wp-prover script,alt-ergo,z3,cvc4 test.c

The thing is that SMT solvers usually cannot performa reasoning by induction. So in the most general case, this proof is beyond what they can do. However, when you force a really small size (for example in your case 14), they can exhaustively unfold the definition axiomatic definition in the limit of the timeout. This is not really an efficient proof, but it works.
This is why the tutorial indicates that in the (presented) general case, the lemma cannot be proved automatically using SMT solvers. However, one thing that has changed since the release of the tutorial is that there is now a tactic called Induction in the interactive theorem prover of WP. So Coq is not necessary anymore to finish the proof.
On the failed goal, select the end_0 term, then select the Induction tactical and set the base parameter to begin_0, and start the tactical:
It generates three cases:
end_0 == begin_0 (trivial by the axiom empty)
end_0 < begin_0 (+ induction hypothesis) (trivial)
end_0 > begin_0 (+ induction hypothesis) proved thanks to the other axioms.
The script can then be saved for future proof replay using the prover script on the command line.

Related

How to verify Why3 output of Proof Obligations

I believe I can generate proofs using why3 with different provers,
frama-c -wp -wp-prover cvc4 -wp-rte -wp-out proof swap.c
frama-c -wp -wp-prover z3-ce -wp-rte -wp-out proof swap.c
frama-c -wp -wp-prover alt-ergo -wp-rte -wp-out proof swap.c
This generates different 'why' files. I would like to validate the proof obligations with an external program. It seems each proof obligation is in a different format; LispClojure and OCaml? What exactly is the format? Is it correct that these are proof and are sufficient to show the contract/proof is correct without proving that Z3, alt-ergo, etc are correct?
swap.c
For the wp tutorial,
int h = 42;
/*#
requires \valid(a) && \valid(b);
assigns *a, *b;
ensures *a == \old(*b) && *b == \old(*a);
*/
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int main()
{
int a = 24;
int b = 37;
//# assert h == 42;
swap(&a, &b);
//# assert a == 37 && b == 24;
//# assert h == 42;
return 0;
}
This works fine and the frama-c-gui shows me how to develop contracts and annotations.
Alter-ergo
(* WP Task for Prover Alt-Ergo,2.4.1 *) (* this is the prelude for Alt-Ergo, version >= 2.4.0 *) (* this is a prelude for Alt-Ergo integer arithmetic *) (* this is a prelude for Alt-Ergo real arithmetic *) type string
logic match_bool : bool, 'a, 'a -> 'a
axiom match_bool_True : (forall z:'a. forall z1:'a. (match_bool(true, z, z1) = z))
The full proof is truncated for brevity.
z3-ce
(* WP Task for Prover Z3,4.8.11,counterexamples *) ;;; generated by SMT-LIB2 driver ;;; SMT-LIB2 driver: bit-vectors, common part ;;; generated by SMT-LIB strings ;;; generated by SMT-LIB strings (set-option :produce-models true) ;;; SMT-LIB2: integer arithmetic ;;; SMT-LIB2: real arithmetic (declare-sort uni 0)
(declare-sort ty 0)
(declare-fun sort (ty uni) Bool)
(declare-fun witness (ty) uni)
The full proof is truncated for brevity.
I have to admit that I'm not completely sure that I completely understand what you want to achieve here, but here are the answers to your questions:
It seems each proof obligation is in a different format; Lisp and OCaml? What exactly is the format?
These files represent the formulas that are given to the provers you ask Frama-C to launch. The format depends on the prover. If I recall correctly, for many provers, this will be smtlib, or tptp, but some provers such as Alt-Ergo can also enjoy a custom output. The generation of the file is described by Why3's driver files, as mentioned (quite briefly) in section 12.4 of the Why3 manual.
Is it correct that these are proof ?
No, these are formulas to be validated by the prover they have been generated for.
and are sufficient to show the contract/proof is correct without proving that Z3, alt-ergo, etc are correct?
No. If the provers you use have a bug, they might mistakenly tell you that a given proof obligation is valid. Some provers are able to provide a proof trace (e.g. if you tweak a driver to use the get-proof command of smtlib), but as far as I know, the format of such a trace is prover-specific, so that it would probably be difficult to have it checked by an external tool.

Frama-C acsl max example from manual not working

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)

How to validate code that read/write to hardware memory mapped registers (mmio) with frama-c Eva plugin or WP-RTE?

The closest answer I found maybe related to -absolute-valid-range for the Eva plugin but is that it? Do I have to come up with read/write ACSL predicates to do dummy read/write?
Sample code:
#include <stdint.h>
#define BASE_ADDR 0x0e000000
#define BASE_LIMIT 0x0e001000
#define TEST_REG 0x10
/*# requires BASE_ADDR <= addr < BASE_LIMIT;
# assigns \nothing;
*/
static inline uint32_t mmio_read32(volatile uintptr_t addr)
{
volatile uint32_t *ptr = (volatile uint32_t *)addr;
return *ptr;
}
/*#
# requires 0 <= offset <= 0x1000;
# assigns \nothing;
*/
static inline uint32_t read32(uintptr_t offset)
{
return mmio_read32((uintptr_t)BASE_ADDR + offset);
}
void main(){
uint32_t test;
test = read32(TEST_REG);
return;
}
Frama-c command and output:
[frama -absolute-valid-range 0x0e000000-0x0e001000 -wp mmio2.c
[kernel] Parsing mmio2.c (with preprocessing)
[wp] Warning: Missing RTE guards
[wp] 6 goals scheduled
[wp] [Alt-Ergo] Goal typed_read32_call_mmio_read32_pre : Unknown (Qed:4ms) (51ms)
[wp] Proved goals: 5 / 6
Qed: 5
Alt-Ergo: 0 (unknown: 1)][1]
How to discharge goal "typed_read32_call_mmio_read32_pre" or is this expected?
The fact that the proof fails is related to two independent issues, but none of them is related to using absolute addresses.
First, as the argument of mmio_read32 is marked as volatile, WP consider that its value can be anything. In particular, none of the hypotheses that are made on offset are known to hold when evaluating addr. You can see that in the GUI by looking at the generated goal (go in the WP Goals tab at the bottom and double click in the Script colon and the line of the failed proof attempt):
Goal Instance of 'Pre-condition'
(call 'mmio_read32'):
Assume {
Type: is_uint32(offset_0).
(* Pre-condition *)
Have: (0 <= offset_0) /\ (offset_0 <= 4095).
}
Prove: (234881024 <= w) /\ (w_1 <= 234885119).
w and w_1 correspond to two read accesses to the volatile content of addr. I'm not sure if you really intended the addr parameter to be volatile (as opposed to a non-volatile pointer to a volatile location), as this would require a pretty weird execution environment.
Assuming that the volatile qualifier should not present in the declaration of addr, a remaining issue is that the constraint on offset in read32 is too weak: it should read offset < 0x1000 with a strict inequality (or the precondition of mmio_read32 be made non-strict, but again this would be quite uncommon).
Regarding the initial question about physical adressing and volatile, in ACSL (see section 2.12.1 of the manual), you have a specific volatile clause that allows you to specify (C, usually ghost) functions to represent read and write accesses to volatile locations. Unfortunately, support for these clauses is currently only accessible through a non-publicly distributed plug-in.
I'm afraid that if you want to use WP on code with physical adressing, you need indeed to use an encoding (e.g. with a ghost array of appropriate size), and/or model volatile accesses with appropriate functions.

How to prove this assign clause?

/*#
# requires \valid(p);
# assigns \nothing;
*/
void foo(int *p)
{
int *pb;
pb = p;
*pb = 1;
return;
}
void main(){
int a = 0;
foo(&a);
return;
}
As I understand the assigns clause for a function contract only works with the function's input variables. So I make the assigns clause to nothing but then get yellow status with -wp.
frama-c -wp test1.c
[kernel] Parsing test1.c (with preprocessing)
[wp] Warning: Missing RTE guards
[wp] 3 goals scheduled
[wp] [Alt-Ergo] Goal typed_foo_assign_part2 : Unknown (Qed:4ms) (51ms)
[wp] Proved goals: 2 / 3
Qed: 2
Alt-Ergo: 0 (unknown: 1)
How to prove foo() if assigns \nothing is not working?
I'm unsure of what you mean by "assigns [...] only works with the function's input variable". An assigns clause is supposed to give a superset of all memory locations that are allocated in both the pre- and post- states of the contract (there are specific clauses for handling dynamic (de)allocation) and that might be modified during the execution of the function (or the statement for a statement contract).
In particular, your function foo can't have an assigns \nothing; clause since it modifies the value of the location pointed to by p: you must have something like assigns *p;, which is easily proved by frama-c -wp.

Frama-c cannot prove validity of buffer pointer of type other than `char*`

Consider the following C code:
#include <stdio.h>
/*#
requires \valid(p+(0..n-1));
requires \valid(fp);
*/
void f(/*unsigned*/ char *p, size_t n, FILE *fp)
{
fread(p, 1, n, fp);
}
Frama-c can prove that the pre-conditions to fread are met. However, with unsigned restored, frama-c fails to see the obvious fact that \valid((char*)p+(0..n-1)) holds. Why?
It looks like the conversion from unsigned char * in f to char * in the pre-condition of fread is confusing the WP plug-in of Frama-C. When using -wp-out foo to get the files given to the external provers in directory foo/, I get the following goal in Alt-Ergo's syntax:
goal f_call_fread_pre_valid_ptr_block:
forall i : int.
forall t : int farray.
forall t_1 : (addr,int) farray.
forall a_2,a_1,a : addr.
(region(a_1.base) <= 0) ->
(region(a_2.base) <= 0) ->
linked(t) ->
sconst(t_1) ->
is_uint32(i) ->
valid_rw(t, a_2, 2) ->
valid_rw(t, shift_uint8(a_1, 0), i) ->
valid_rw(t, shift_sint8(a, 0), i)
Note in particular that the hypothesis (i.e. the requires of f) mentions variable a_1, while the goal (i.e. the requires of fread that we are trying to prove), mentions variable a, and that there is absolutely nothing tying a_1 and a together.
I'd tend to see that as a bug, but I'm not expert in WP's internals. You might want to report that on Frama-C's BTS.

Resources