Reputation: 15103
I'm trying to verify a simple program from Frama-C + WP.
#include <string.h>
/*@
requires valid_read_string(s);
assigns \result \from indirect:s[0..];
ensures \result == strlen(s);
*/
size_t get_len(const char *s) {
return strlen(s);
}
int main() {
static const char foo[4] = { 'H', 'e', 'y', 0 };
size_t sz = get_len(foo);
//@ assert sz == 3;
return 0;
}
Everything validates correctly except for one proof:
frama-c -rte -pp-annot -wp -wp-rte /tmp/test.c -c11 -then -report -report-no-proven
[ - ] Default behavior
tried with Frama-C kernel.
1 To be validated
1 Total
I can't seem to find any information as to what this "Default behavior" means, nor can I figure out why it's failing to validate it.
Am I doing something wrong here?
Upvotes: 3
Views: 155
Reputation: 10148
An ACSL contract can be structured into a list of behaviors, which describe the various cases in which the function might be called (see ACSL manual for more information). They are introduced like this:
/*@
behavior A:
assumes some_condition;
requires ...
assigns ...
ensures ...
behavior B: ...
*/
If the assumes
clause is true when the function is called, the behavior is active, and the other clauses in the behavior must be satisfied.
The so-called Default behavior encompasses the clauses that do not belong to an explicit behavior: it is a bit like if you had written your contract as such:
/*@
behavior Default:
assumes \true;
requires valid_read_string(s);
assigns \result \from indirect:s[0..];
ensures \result == strlen(s);
*/
The validity status of a behavior (including the default one) is simply the consolidation of the statuses of its components (i.e. it will be valid if and only if all components are validated). It is computed by the kernel from the statuses put on each individual components by the plug-ins (here, WP).
Now, where does the "Unknown" come from, since it looks like all annotations have been proved? In fact, this is not the case: the way you have written your assigns clause, assigns \result \from indirect:s[0..];
implies that there are two things to prove: first that the function does not modify the global state of the program (this is validated by WP), and second that the result only depend on the content of s (this is not done, and in fact no plug-in is currently capable of doing it). This is this second property, often referred to as a from
clause, that is causing the kernel to consider that the Default behavior is not fully proved. Unfortunately, it appears that this from
clause is not even present in Report's output, which make things even more confusing.
UPDATE As suggested by an esteemed former colleague, I've dug a bit into the lack of report for the from
clause: by default untried properties are not displayed by -report
, you have to explicitly set -report-untried
(which then gives you all the preconditions of the function of the standard library that you don't call, and the from
of get_len
as expected).
Upvotes: 3
Reputation: 3422
Virgile's answer is excellent (FD: we are former colleagues), but I wanted to point out that in your very restrictive case, you can prove the from
clause of your contract. More precisely, once it is corrected, you can prove it. The correct clause is
assigns \result \from direct:s[0..], indirect:s
Indeed, you need to read the contents of the string pointed to by s
to compute its length, and there is an indirect dependency on the pointer s
to access the memory to be read. (The notion of direct
/indirect
dependencies is not part of ACSL, and was added to make from
clauses more useful for the verification of programs using the Eva
plugin.)
A more precise version would be
assigns \result \from direct:s[0..StrLen(s)], indirect:s
But this becomes harder to prove if s
and the memory it points to is not known precisely.
Finally, the plugin that is able to perform the proof is the plugin From
, with option -from-verify-assigns
enabled. Beware that the proof is not done by WP, but by Eva+From. In particular, the proof is not modular. Instead, the contract is checked for all the callsites of get_len
that occur during the executions that start from main
. This may or may not be what you're looking for.
Upvotes: 2