2

Lets say we have the following C code:

int my_main(int x){
    if (x > 5){
        x++;
        if (x > 8){
            x++;
            if (x < 15){
                //@(x >= 9 && x <= 14);
            }
        }   
    }
    return 0;
}

I'd like to calculate using static analysis the bounds on the variable x at initialization which result in a satisfied predicate. In this example, the interval of x at the start of main should be [8, 12].

TL;DR: What would be the best way to calculate these ranges given an assertion somewhere in the code?

What I tried so far:

I think the best way to approach this is using a weakest precondition calculator. Iv'e tried toying around with the wp plugin of frama-c but since it is built for verification purposes, I am not sure how useful it is in this use case. When applying the plugin on the following code:

int main(void){
    int n = 0;
    int x;

    if (x > 5){
        x++;
        if (x > 8){
            x++;
            if (x < 15){
                n = x;
            }
        }   
    }
    //@ assert p: n >= 9 && n <= 14;
    return 0;
}

I get the following predicate sent to the alt-ergo solver:

  goal main_assert_p:
    forall i_1,i : int.
    is_sint32(i) ->
    is_sint32(i_1) ->
    (((i < 6) -> (0 = i_1)) and
     (**(6 <= i)** ->
      (((i < 8) -> (0 = i_1)) and
       (**(8 <= i)** ->
        (((12 < i) -> (0 = i_1)) and (**(i <= 12)** -> (i_1 = (2 + i)))))))) ->
    ((9 <= i_1) and (i_1 <= 14))

If you look carefully, it is possible to identify the required interval on the input by following the bounds on the variable i which don't result in (i_1 = 0). Iv'e marked these bounds. This is not very robust, for example if the assertion changes to && n <= 13, the 'left side' of the implies predicate stays the same as none of the conditions changed. Also i'm not sure how useful this is in other scenarios, for example altering my assertion to a requires statement when calling a function, I can't understand the resulting predicate:

if (x < 15){
      sum(x);
}

And adding a requires statement to the function:

//@requires (n >= 6 && n <= 11);
int sum(int n){

I get:

    goal main_call_sum_pre:
  forall i : int.
  (6 <= i) ->
  (8 <= i) ->
  (i <= 12) ->
  is_sint32(i) ->
  is_sint32(1 + i) ->
  is_sint32(2 + i) ->
  ((4 <= i) and (i <= 9))
Maor Veitsman
  • 1,544
  • 9
  • 21

2 Answers2

3

You are right that WP (or Jessie), being based on the “weakest precondition” paradigm, are the right tools to use here. What they do, however, is to build the implication:

precondition given by the specification ==> computed weakest precondition

The external prover(s) then attempt to prove the above implication, with (in the general case) a true/false/timeout answer only being provided.

You could do it by trial and error, using “LOWER_BOUND ≤ x ≤ UPPER_BOUND” as post-condition of user_input(*), and seeing whether the implication gets proved or not. Using the tools you have as black boxes, you would arrive by dichotomy to the interval in a few steps. You'll never know if you have the optimal interval or if the prover just ceased to be able to prove a property that still held, but that's life.

Or you could let the prover do the simplification work for you, but that requires a more complex kind of interaction than just “is this property true?”. Some provers will let you have access to that information more easily that others. It's all in the prover's hand, really, after WP has done its job, and your question is really about “a prover that reduces a logical formula to bounds on x that make the formula true”, rather than about Frama-C.

This study involved the question “just give me the best interval you can” in some places. It is about floating-point but since floating-point is only more difficult that integers to reason about, the tools and techniques used there might apply to your problem as well. In particular, the “Gappa” prover, the speciality of which is floating-point, works natively with intervals and IIRC was the prover that provided the “best” intervals where necessary in that study (page 11, “For example, how did we determine the bound 1/16 in our illustrative example?”)


(*) Note that after having added the call to user_input() to clarify the meaning, what you are looking for is really the post-condition of that function, rather than the pre-condition of the main function.

Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
  • Thank you for your response. A few remarks & questions: 1. To simplify, I changed the main function to my_main which receives x as input. I'd like to discover which input interval results in a satisfied predicate. 2. Not sure I understand the implication you wrote, what I see is (predicate that describes the transformations in the program) -> my_predicate. 3. Isn't there a tool that can perform a backward transformer calculation and calculate the weakest precondition? Is it possible to retrieve this predicate from frama-c? – Maor Veitsman Feb 09 '16 at 11:08
  • @MaorVeitsman 2) The implication is the other way round from what you appear to have written: the written specification, containing the explicit pre-condition that will be enforced at call sites, must imply the computed weakest pre-condition. – Pascal Cuoq Feb 09 '16 at 11:49
  • @MaorVeitsman 3) backwards transformations on predicates written in an understandable logic in which logic variables map simply to program variables is how weakest precondition algorithm are described in book for toy languages without aliasing. The principle works in practice except that YOU DON'T WANT TO SEE THE ACTUAL LOGIC FORMULAS. You really don't. They are horrible (actually there is one in your question so you see what I mean). So in order not to have to see them, you write your own pre-condition in a friendly specification language, it is translated too in a horrible formula, but… – Pascal Cuoq Feb 09 '16 at 11:53
  • …you don't need to see either the horrible translation of the pre-condition you wrote or the horrible computed weakest precondition because the implication between them is proved automatically. In theory. As I said, WP has done its job. What you are asking for is a tool that takes a formula and provides bounds for `x` that guarantee the formula holds. One tool that can do that for some usecases is Gappa. There likely are others, especially since intervals play a major role in constraint solving. – Pascal Cuoq Feb 09 '16 at 11:56
  • 2) Have a look at goal main_assert_p in my question. The right clause of the 'implies' is the user-given predicate and the left side is sort of a model of the program, using the variable i which corresponds to the value of the initial variable. 3) I see what you mean, I will have a look at it. – Maor Veitsman Feb 09 '16 at 12:15
  • I think I see the confusion. My goal is not to prove that a function indeed satisfies a precondition. What I want is to deduce the interval of an input at initialization which later causes the program to reach a certain point in the code and satisfy the assertion written there. – Maor Veitsman Feb 09 '16 at 12:27
-1

assert takes a Boolean expression and if FALSE, aborts the application with a message. assert is generally a macro and in the non-debug version of your program, calls to these macros are removed during preprocessing.

Your Boolean expression contains constants. If you replace those with variables, then you have your flexible assert.

Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
  • 1
    My use of "assert" was misleading, I wasn't addressing the assert function in C. I'm interested in statically calculating the range of the input which results in the predicate being satisfied. – Maor Veitsman Feb 09 '16 at 10:34