Specifications with Addressable Local Variables - verifiable-c

I'm trying to understand how VST handles (addressable) local variables, so I wrote this function:
int main() {
int x = 5, y = 7;
int *a = &x;
int *b = &y;
*a = 8;
*b = 9;
return x;
}
then I tried to verify it with the following specification:
Definition main_spec :=
DECLARE _main
WITH p : int (* still toying with things, couldn't figure out how to drop this *)
PRE [ ]
PROP ()
LOCAL ()
SEP ()
POST [ tint ]
EX i : Z,
PROP ( i = 8 )
LOCAL (temp ret_temp (Vint (Int.repr i)))
SEP ().
Everything goes well simply using (forward) until the return statement where I am left to prove the following
data_at Tsh tint (vint 9) v_y * data_at Tsh tint (vint 8) v_x |-- FF
which seems like it should be unprovable (note that I have just applied forward up until this point). I was expecting some specification stating that the heap is empty after the local variables had been de-allocated, i.e. emp |-- emp.
Is there somewhere that I could look to get more information about this?
Thanks!
Additional Information: I dug into the source of the FF post-condition, and it comes from typecheck_expr, in particular, the case for Evar:
| Evar id ty =>
match access_mode ty with
| By_reference =>
match get_var_type Delta id with
| Some ty' =>
tc_bool (eqb_type ty ty')
(mismatch_context_type ty ty')
| None =>
tc_FF
(var_not_in_tycontext Delta id)
end
| _ => tc_FF (deref_byvalue ty) (* ?? *)
end
Unless I'm reading something wrong, this seems to suggest that you can not access local variables by value. Is this just an oversight? Or is there something in the semantics that prevents this?

The reason for this failure is that you forgot the -normalize flag to clightgen.
If you translate your .c file using clightgen -normalize instead of clightgen, it should work fine.

Which version of VST are you using? In a recent master-branch version (commit 506f8e7), the following straightforward proof works just fine.
Lemma body_main: semax_body Vprog Gprog f_main
main_spec.
Proof.
start_function.
forward.
forward.
forward.
forward.
forward.
forward.
forward.
forward.
Exists 8.
entailer!.
Qed.

Related

VST Verification of Global Array of Doubles

I am currently attempting to use VST to verify the correctness of a project which involves a global array of doubles. However, when attempting to access the array I have that the head of the array is given as a data_at statement while the rest of the array is given as a sepcon list of mapsto statements and there does not appear to be any way to prove field_compatible for elements beyond the head of the array.
Trying to access elements beyond offset_val 0 seems to inevitably involve proving a size_compatible statement. This is where I run into a problem. Since the alignment of tdouble is set to 4 and the size is set to 8, there seems to be a possibility that the head of the array is at Ptrofs.modulus - 12 making size_compatible false for the next element in the array. Am I going about this the wrong way?
I made a toy example with the same problem that I've mentioned above.
double dbls[] = {0.0, 1.1};
int main() {
double sum;
sum = dbls[0] + dbls[1];
return 0;
}
I will frame my answer in the form of a Coq development:
Require Import VST.floyd.proofauto.
Require Import VST.progs.foo.
#[export] Instance CompSpecs : compspecs. make_compspecs prog. Defined.
Definition Vprog : varspecs. mk_varspecs prog. Defined.
Definition main_spec :=
DECLARE _main
WITH gv : globals
PRE [] main_pre prog tt gv
POST [ tint ] main_post prog gv.
Definition Gprog : funspecs := [ ].
Lemma body_main: semax_body Vprog Gprog f_main main_spec.
Proof.
start_function.
(* Remark 1: it seems to be a bug in VST 2.11.1 (and earlier versions)
that the array is not packaged up into
(data_at Ews (tarray Tdouble 2) ...)
the way it ought to be. This seems to work better for integer
arrays, et cetera.*)
(* Remark 2: you are right to be concerned about alignment, but
VST addresses that issue correctly. Any extern global variable
in a C program, such as your [dbls] array, is aligned at the
biggest possible alignment requirement. VST expresses this
with the "headptr" predicate, and for any identifier id,
(gv id) is a headptr. So therefore, *)
assert_PROP (headptr (gv _dbls)) by entailer!.
(* and you can see above the line, H: headptr (gv _dbls). *)
Print headptr.
(* This shows that (gv _dbls) must be at offset zero within
some block, which guarantees alignment at any type.
One useful consequence is proved by the lemma
headptr_field_compatible: *)
Check headptr_field_compatible.
(* And now, let's apply that lemma: *)
pose proof headptr_field_compatible (tarray tdouble 2) nil _
H (eq_refl _) Logic.I ltac:(simpl; rep_lia).
(* So we see that as long as the pesky 'align_compatible_rec' is proved,
the pointer (gv _dbls) should be 'field_compatible' with the array
type that you want. And it's straightforward though tedious to prove
the 'align_compatible_rec' premise, as follows: *)
spec H0.
apply align_compatible_rec_Tarray; intros.
Search align_compatible_rec.
eapply align_compatible_rec_by_value; [ reflexivity | ].
apply Z.divide_add_r.
apply Z.divide_0_r.
apply Z.divide_mul_l.
apply Z.mod_divide; compute; intros; congruence.
(* Normally, VST users shouldn't have to do this 'by hand'.
We should fix the bug (failure to nicely package the precondition).
But in the interim, perhaps this gives what you need for a workaround.*)

How to share the information of logic variable between environment and use?

I'm trying to generate some annotations for functions. I have succeeded in implementing a plugin that achieves this purpose, but the problem that I am facing is that when I run tests on my plugin, an integrity check is executed automatically and I get an error that says:
Built-in logic variable \subset information is not shared between environment and use
To demonstrate this problem I have written a simplified plugin that will generate an ACSL annotation for a function called get_x().
The code for the simplified plugin is
open Cil_types
open Cil
module M = Plugin.Register
let my_emitter =
Emitter.create "Generate function contract" [ Emitter.Funspec ]
~correctness:[] ~tuning:[]
let construct_subset_ip e =
let t = Logic_utils.expr_to_term e in
let its =
[ Logic_const.tinteger 1; Logic_const.tinteger 2; Logic_const.tinteger 3 ]
in
let li = Cil_const.make_logic_info "\\subset" in
li.l_tparams <- [ "a" ];
let s1 = Cil_const.make_logic_var_formal "s1" Linteger in
let s2 = Cil_const.make_logic_var_formal "s2" Linteger in
li.l_profile <- [ s1; s2 ];
let tn1 = Tunion [ t ] in
let t1 = Logic_const.term tn1 Linteger in
let tn2 = Tunion its in
let t2 = Logic_const.term tn2 Linteger in
let p = Logic_const.papp (li, [], [ t1; t2 ]) in
Logic_const.new_predicate p
class my_visitor prj =
object (self)
inherit Visitor.generic_frama_c_visitor (Visitor_behavior.copy prj)
method! vglob_aux =
function
| GFun (fd, _) -> if fd.svar.vname = "get_x" then DoChildren else JustCopy
| _ -> JustCopy
method! vstmt_aux s =
match s.skind with
| Return (Some e, _) ->
let new_kf =
Option.get self#current_kf
|> Visitor_behavior.Get.kernel_function self#behavior
in
let bhv = Cil.mk_behavior ~name:"my_behavior" () in
let ip =
Visitor.visitFramacExpr self#frama_c_plain_copy e
|> construct_subset_ip
in
Queue.add
(fun () ->
Annotations.add_requires my_emitter new_kf ~behavior:"my_behavior"
[ ip ])
self#get_filling_actions;
Queue.add
(fun () -> Annotations.add_behaviors my_emitter new_kf [ bhv ])
self#get_filling_actions;
JustCopy
| _ -> JustCopy
end
let execute () =
ignore (File.create_project_from_visitor "ACSL annotated" (new my_visitor))
I applied this plugin on the following C module:
/* run.config
OPT: -autoload-plugins -sandbox
*/
int X;
int get_x() {
return X;
}
The output when executing the plugin on the C module is:
/* Generated by Frama-C */
int X;
/*# behavior my_behavior:
requires X ∈ {1, 2, 3}; */
int get_x(void)
{
return X;
}
Now, when I run $ ptest.opt tests\s1\test.c -show I get
...
[kernel] Parsing tests/s1/test.c (with preprocessing)
[kernel] tests/s1/test.c:8: Failure:
[AST Integrity Check]
AST of ACSL annotated
Built-in logic variable \subset information is not shared between environment and use
[kernel] Current source was: tests/s1/test.c:7
The full backtrace is:
Raised at Project.on in file "src/libraries/project/project.ml", line 405, characters 59-66
Called from File.init_project_from_visitor in file "src/kernel_services/ast_queries/file.ml", line 1818, characters 4-64
Called from File.create_project_from_visitor in file "src/kernel_services/ast_queries/file.ml", line 1842, characters 2-43
Called from Sandbox_visitor.execute in file "sandbox_visitor.ml", line 60, characters 9-77
Called from Stdlib__Queue.iter.iter in file "queue.ml", line 121, characters 6-15
Called from Boot.play_analysis in file "src/kernel_internals/runtime/boot.ml", line 36, characters 4-20
Called from Cmdline.play_in_toplevel_one_shot in file "src/kernel_services/cmdline_parameters/cmdline.ml", line 846, characters 2-9
Called from Cmdline.play_in_toplevel in file "src/kernel_services/cmdline_parameters/cmdline.ml", line 876, characters 18-64
Called from Cmdline.catch_toplevel_run in file "src/kernel_services/cmdline_parameters/cmdline.ml", line 235, characters 4-8
Frama-C aborted: internal error.
Please report as 'crash' at https://git.frama-c.com/pub/frama-c/issues
Your Frama-C version is 24.0 (Chromium).
Note that a version and a backtrace alone often do not contain enough
information to understand the bug. Guidelines for reporting bugs are at:
https://git.frama-c.com/pub/frama-c/-/wikis/Guidelines-for-reporting-bugs
What is this error about, and how do I solve it?
PS: Excuse me for any bad coding practices that you might notice. I'm a beginner in both Ocaml and Frama-C.

Functional composition of Optionals

I have 2 Optionals (or Maybe objects) that I would like to combine so that I get the following results:
|| first operand
second ++-------------+-------------
operand || empty | optional(x)
============||=============|=============
empty || empty | optional(x)
------------++-------------+-------------
optional(y) || optional(y) |optional(x+y)
In other words, a non-empty Optional always replaces/overwrites an empty one, and two non-empty Optionals are combined according to some + function.
Initially, I assumed that the standard monadic flatMap method would do the trick, but (at least in Java) Optional.flatMap always returns an empty optional when the original Optional was already empty (and I'm not sure if any other implementation would comply with the Monad Laws).
Then, as both operands are wrapped in the same monadic type, I figured that this might be a good job for an Applicative Functor. I tried a couple different functional libraries, but I couldn't implement the desired behavior with any of the zip/ap methods that I tried.
What I'm trying to do seems to me a fairly common operation that one might do with Optionals, and I realize that I could just write my own operator with the desired behavior. Still, I am wondering if there is a standard function/method in functional programming to achieve this common operation?
Update: I removed the java tag, as I'm curious how other languages handle this situation
In a functional language, you'd do this with pattern matching, such as (Haskell):
combine :: Maybe t -> Maybe t -> (t -> t -> t) -> Maybe t
combine (Some x) (Some y) f = Some (f x y)
combine (Some x) _ _ = (Some x)
combine _ (Some y) _ = (Some y)
combine None None _ = None
There are other ways to write it, but you are basically pattern matching on the cases. Note that this still involves "unpacking" the optionals, but because its built into the language, it is less obvious.
In Haskell you can do this by wrapping any semigroup in a Maybe. Specifically, if you want to add numbers together:
Prelude> import Data.Semigroup
Prelude Data.Semigroup> Just (Sum 1) <> Just (Sum 2)
Just (Sum {getSum = 3})
Prelude Data.Semigroup> Nothing <> Just (Sum 2)
Just (Sum {getSum = 2})
Prelude Data.Semigroup> Just (Sum 1) <> Nothing
Just (Sum {getSum = 1})
Prelude Data.Semigroup> Nothing <> Nothing
Nothing
The above linked article contains more explanations, and also some C# examples.
It's not possible to combine optional objects without "unpacking" them.
I don't know the specifics of your case. For me, creating such a logic just in order to fuse the two optionals is an overkill.
But nevertheless, there's a possible solution with streams.
I assume that you're not going to pass optional objects as arguments (because such practice is discouraged). Therefore, there are two dummy methods returning Optional<T>.
Method combine() expects a BinaryOperator<T> as an argument and creates a stream by concatenating singleton-streams produced from each of the optional objects returned by getX() and getY().
The flavor of reduce(BinaryOperator) will produce an optional result.
public static <T> Optional<T> getX(Class<T> t) {
return // something
}
public static <T> Optional<T> getY(Class<T> t) {
return // something
}
public static <T> Optional<T> combine(BinaryOperator<T> combiner,
Class<T> t) {
return Stream.concat(getX(t).stream(), getY(t).stream())
.reduce(combiner);
}
If we generalize the problem to "how to combine N optional objects" then it can be solved like this:
#SafeVarargs
public static <T> Optional<T> combine(BinaryOperator<T> combiner,
Supplier<Optional<T>>... suppliers) {
return Arrays.stream(suppliers)
.map(Supplier::get) // fetching Optional<T>
.filter(Optional::isPresent) // filtering optionals that contain results to avoid NoSuchElementException while invoking `get()`
.map(Optional::get) // "unpacking" optionals
.reduce(combiner);
}
Here's one way:
a.map(x -> b.map(y -> x + y).orElse(x)).or(() -> b)
Ideone Demo
OptionalInt x = ...
OptionalInt y = ...
OptionalInt sum = IntStream.concat(x.stream(), y.stream())
.reduce(OptionalInt.empty(),
(opt, z) -> OptionalInt.of(z + opt.orElse(0)));
Since java 9 you can turn an Optional into a Stream.
With concat you get a Stream of 0, 1 or 2 elements.
Reduce it to an empty when 0 elements,and for more add it to the previous OptionalInt, defaulting to 0.
Not very straight (.sum()) because of the need for an empty().
You can implement your function in Java by combining flatMap and map:
optA.flatMap(a -> optB.map(b -> a + b));
More general example:
public static void main(String[] args) {
test(Optional.empty(), Optional.empty());
test(Optional.of(3), Optional.empty());
test(Optional.empty(), Optional.of(4));
test(Optional.of(3), Optional.of(4));
}
static void test(Optional<Integer> optX, Optional<Integer> optY) {
final Optional<Integer> optSum = apply(Integer::sum, optX, optY);
System.out.println(optX + " + " + optY + " = " + optSum);
}
static <A, B, C> Optional<C> apply(BiFunction<A, B, C> fAB, Optional<A> optA, Optional<B> optB) {
return optA.flatMap(a -> optB.map(b -> fAB.apply(a, b)));
}
Since flatMap and map are standard functions for Optional/Maybe (and monad types generally), this approach should work in any other language (though most FP languages will have a more concise solution). E.g. in Haskell:
combine ma mb = do a <- ma ; b <- mb ; return (a + b)
In F#, i would call this logic reduce.
Reason:
The function must be of type 'a -> 'a -> 'a as it only can combine thinks of equal type.
Like other reduce operations, like on list, you always need at least one value, otherwise it fails.
With a option and two of them, you just need to cover four cases. In F# it will be written this way.
(* Signature: ('a -> 'a -> 'a) -> option<'a> -> option<'a> -> option<'a> *)
let reduce fn x y =
match x,y with
| Some x, Some y -> Some (fn x y)
| Some x, None -> Some x
| None , Some y -> Some y
| None , None -> None
printfn "%A" (reduce (+) (Some 3) (Some 7)) // Some 10
printfn "%A" (reduce (+) (None) (Some 7)) // Some 7
printfn "%A" (reduce (+) (Some 3) (None)) // Some 3
printfn "%A" (reduce (+) (None) (None)) // None
In another lets say Pseudo-like C# language, it would look like.
Option<A> Reduce(Action<A,A,A> fn, Option<A> x, Option<A> y) {
if ( x.isSome ) {
if ( y.isSome ) {
return Option.Some(fn(x.Value, y.Value));
}
else {
return x;
}
}
else {
if ( y.isSome ) {
return y;
}
else {
return Option.None;
}
}
}

How to handle "Forall (closed_wrt_vars (eq _z')) P " goals in VST?

This time I'm proving function calling other. vars.c:
int pure0 ()
{
return 0;
}
int get0(int* arr)
{
int z = pure0();
return 0;
}
My proof start - verif_vars.v:
Require Import floyd.proofauto.
Require Import vars.
Local Open Scope logic.
Local Open Scope Z.
Definition get0_spec :=
DECLARE _get0
WITH sh : share, arr : Z->val, varr : val
PRE [_arr OF (tptr tint)]
PROP ()
LOCAL (`(eq varr) (eval_id _arr);
`isptr (eval_id _arr))
SEP (`(array_at tint sh arr 0 100) (eval_id _arr))
POST [tint] `(array_at tint sh arr 0 100 varr).
Definition pure0_spec :=
DECLARE _pure0
WITH sh : share
PRE []
PROP ()
LOCAL ()
SEP ()
POST [tint] local(`(eq (Vint (Int.repr 0))) retval).
Definition Vprog : varspecs := nil.
Definition Gprog : funspecs := get0_spec :: pure0_spec ::nil.
Lemma body_pure0: semax_body Vprog Gprog f_pure0 pure0_spec.
Proof.
start_function.
forward.
Qed.
Lemma body_get0: semax_body Vprog Gprog f_get0 get0_spec.
Proof.
start_function.
name arrarg _arr.
forward_call (sh).
entailer!.
Which induces the goal:
2 subgoals, subgoal 1 (ID 566)
Espec : OracleKind
sh : share
arr : Z -> val
varr : val
Delta := abbreviate : tycontext
POSTCONDITION := abbreviate : ret_assert
MORE_COMMANDS := abbreviate : statement
Struct_env := abbreviate : type_id_env.type_id_env
arrarg : name _arr
============================
Forall (closed_wrt_vars (eq _z')) [`(array_at tint sh arr 0 100 varr)]
subgoal 2 (ID 567) is:
DO_THE_after_call_TACTIC_NOW
I suppose it states, that the function call does not alter arr contents, which is quite obvious for me.
What can I do with this goal? Which tactic applies here, and what exactly means the statement? Should I enrich the pure0 spec to somehow point out, that it does not modify anything?
FIRST: When writing VST/Verifiable-C questions, please indicate which version of VST you are using. It appears you are using 1.4.
SECOND: I am not sure this answers all your questions, but,
"closed_wrt_vars S P" says that the lifted assertion P is closed with respect to all the variables in the set S. That is, S is a set of C-language identifiers that may stand for nonaddressable local variables ("temps", not "vars"). P is an assertion of the form "environ->mpred", and "closed" means that if you change the "environ" to have different values for any of the variables in set S, then the truth of P will not change.
"Forall" is Coq's standard library predicate to apply a predicate to a list. So,
Forall (closed_wrt_vars (eq _z')) [`(array_at tint sh arr 0 100 varr)]
means, let the set S be the singleton set containing just the variable _z'.
We assert here that all the predicates in the list are closed w.r.t. S.
There's exactly one predicate in the list, and it's "trivially lifted",
that is, for any predicate (P: mpred), the lifted predicate
`(P)
is equivalent to (fun rho:environ => P). Trivially, then, `P doesn't
care what you do to rho, including changing the value of _z'.
The "auto with closed" (or just to be sure, "auto 50 with closed")
should take care of this, and you indicate that it does take care of it.
So I assume that the rest of your question was, "what's going on here?",
and I hope I answered it.
Solution, used in vst/progs/verif_reverse.v:
auto with closed.
Unfortunately, it answers only half of the questions.
By the way (unrelated to your question), the precondition
`isptr (eval_id _arr) for get0 is probably unnecessary.
It is implied already by `(array_at tint sh arr 0 100) (eval_id _arr)).
Furthermore, suppose you did want the `isptr (eval_id _arr) in your precondition; you might consider writing it as,
PROP (isptr varr)
LOCAL (`(eq varr) (eval_id _arr))
SEP (`(array_at tint sh arr 0 100 varr))
which is (in some ways) simpler and more "canonical".

ensures proved even though code is defective?

In the following, how are the postconditions for behavior neg_limit being proven true when the relevant C code is commented-out?
One of the Safety->check arithmetic overflow isn't provable, as expected, but it seems like neg_limit should also be unprovable.
Context: I'm using Frama-C-Boron, Jessie and, via gWhy, Alt-Ergo in order to learn how to write specifications and prove that functions meet them. Any cluebatting, RTFMing, etc., about specification strategies, tools, etc. is also appreciated. So far, I am reading both the ACSL 1.7 implementation manual (which is more recent that -Boron's) and the Jessie tutorial & ref. manual.
Thanks!
/*# behavior non_neg:
assumes v >= 0;
ensures \result == v;
behavior neg_in_range:
assumes INT32_MIN < v < 0;
ensures \result == -v;
behavior neg_limit:
assumes v == INT32_MIN;
ensures \result == INT32_MAX;
disjoint behaviors;
complete behaviors;
*/
int32_t my_abs32(int32_t v)
{
if (v >= 0)
return v;
//if (v == INT32_MIN)
// return INT32_MAX;
return -v;
}
Here is the gWhy goal for the first postcondition:
goal my_abs32_ensures_neg_limit_po_1:
forall v_2:int32.
(integer_of_int32(v_2) = ((-2147483647) - 1)) ->
(integer_of_int32(v_2) >= 0) ->
forall __retres:int32.
(__retres = v_2) ->
forall return:int32.
(return = __retres) ->
("JC_13": (integer_of_int32(return) = 2147483647))
and for the second:
goal my_abs32_ensures_neg_limit_po_2:
forall v_2:int32.
(integer_of_int32(v_2) = ((-2147483647) - 1)) ->
(integer_of_int32(v_2) < 0) ->
forall result:int32.
(integer_of_int32(result) = (-integer_of_int32(v_2))) ->
forall __retres:int32.
(__retres = result) ->
forall return:int32.
(return = __retres) ->
("JC_13": (integer_of_int32(return) = 2147483647))
Regarding documentation, you might want to have a look at Fraunhofer FOKUS' ACSL By Example: http://www.fokus.fraunhofer.de/de/quest/_download_quest/_projekte/acsl_by_example.pdf
Concerning your question, I've repeated your result (BTW, you're missing an #include <stdint.h>" in your code) with Frama-C Fluorine, and Jessie+Alt-ergo still manages to prove the post-condition. But remember that the post-condition is proved under the hypothesis that no runtime error occurs, which is not the case of your code, as the failed safety PO shows.
Namely, the second post-condition contains the hypothesis (integer_of_int32(result) = (-integer_of_int32(v_2))) which can be rewritten as (integer_of_int32(result) = 2147483648). This is in contradiction with an axiom in Jessie's prelude, that says that
forall v:int32. integer_of_int32(v)<=2147483647.
I guess that this outlines once again that you cannot claim to have verified an ACSL annotation as long as some proof obligations remain unchecked, even if they do not stem directly from this annotation.

Resources