How to verify Why3 output of Proof Obligations - frama-c

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.

Related

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

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.

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.

Frama-c fails to prove fact about pointer comparison

Consider the following C code:
#include <assert.h>
//# requires p < q;
void f(char *p, char *q)
{
assert(p <= q-1);
}
//# requires a < b;
void g(int a, int b)
{
assert(a <= b-1);
}
Using alt-ergo, frama-c successfully proves that the assertion in g() holds but fail to prove the same with f(). Why?
Formally, pointers and integers are two very different things. In particular, C semantics states that pointer comparison is well defined only for pointers that points in the same allocated block (or one offset past the end of said allocated block) . This is reflected in the model used by the WP plugin of Frama-C in the definition of addr_le and friends (see $(frama-c -print-share-path)/wp/why3/Memory.why), where the pointers are checked to have the same address before the comparison is done on their offset.

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.

WP plugin: Alt-Ergo Syntax Error

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).

Resources