3

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?

Qix - MONICA WAS MISTREATED
  • 14,451
  • 16
  • 82
  • 145

2 Answers2

3

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

Virgile
  • 9,724
  • 18
  • 42
  • Thanks for the clear explanation :) One thing I don't understand though - why is it that this exact ACSL is on the strlen function in Frama's standard library but it's usage can still be verified fully? – Qix - MONICA WAS MISTREATED Mar 30 '21 at 00:36
  • In the general case, proving `from` clauses requires a quite different approach from the annotations that are handled by WP at present (see [this article](https://link.springer.com/article/10.1007/s10009-011-0192-z) for more information - FD: I'm one of the authors). This is why no attempt at proving them is done, even though for this specific case it could indeed be argued that the proof is not that hard. – Virgile Mar 30 '21 at 07:15
2

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.

byako
  • 3,372
  • 2
  • 21
  • 36
  • I wish I could 'accept' two answers, but this one definitely explains how to fix the problem. Thanks for the followup, and thank you two for all the work you've done :) – Qix - MONICA WAS MISTREATED Mar 30 '21 at 10:32
  • By the way, it was this + [a frama-c bug](https://stackoverflow.com/questions/66552738/frama-c-problem-with-proving-assigns-until-strlen#comment118016255_66552738) that was giving me problems. – Qix - MONICA WAS MISTREATED Mar 30 '21 at 11:22