Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

invariant statements is not supported? #96

Open
muyaview opened this issue Aug 20, 2019 · 19 comments
Open

invariant statements is not supported? #96

muyaview opened this issue Aug 20, 2019 · 19 comments

Comments

@muyaview
Copy link

say below codes

procedure {:entrypoint} main(x: int)
returns ($r: int)
{
    var i: int;

    i := 0;
    while (i < 10)
        invariant (i <=11);
    {
        i := i + 1;
    }
    assert i > 10;

    $r := i;
    return;
}

the assert error could be found by boogie verifier without specify /unroll argument.
However corral couldn't do this. Seems corral always handle the loops by unrolling. And the invariant statement is not be used.
Or if invariant is supported by corral, how to enable it?

@akashlal
Copy link
Contributor

akashlal commented Aug 20, 2019

Corral must return a full program trace witnessing an assert failure. It relies on unrolling to produce this trace. It does support using program invariants to prove the absence of errors whenever possible. I have used procedure requires and ensures in the past. I am not sure what happens with the invariant keyword for loops. Corral converts loops to procedures and its likely that the invariant gets lost.

@shuvendu-lahiri
Copy link
Collaborator

Agree with Akash that invariants may not be useful for an assertion that does not hold.

In fact, here is an example that shows that Corral fails to exploit an invariant to get a proof (something Akash and I discussed offline a few months back). But it is not a bug, but more of an enhancement.

var a:[int]int;
var b:[int]int;

procedure Foo(n: int) 
modifies a, b;
{
  var i:int;
  
  a[0] := 0;
  b[0] := 0;
  
  i := 0;
  while (i < n) 
  invariant a[0] == b[0];
  {
    a[i] := a[i] + 1;
    b[i] := b[i] + 1;
    i := i + 1;
  }
  assert (a[0] == b[0]);
}

@bitcalc
Copy link

bitcalc commented Aug 20, 2019

I ran shuvendu-lahiri's example with or without the invariant, corral always reports "no bugs" with and without the invariant clause. And also from reading the above comments, we can conclude that corral ignores the loop invariant clause, right?

@akashlal
Copy link
Contributor

Yeah, I think invariant needs to properly get lifted to ensures postconditions when loop are lifted to recursive procedures. This can be done in the Boogie code -- the relevant procedure is ExtractLoops

@akashlal
Copy link
Contributor

Corral doesn't exactly ignore invariant. I think it still gets converted to assert or assume. It is just that corral isn't able to make best use of it.

@bitcalc
Copy link

bitcalc commented Aug 20, 2019

Corral doesn't exactly ignore invariant. I think it still gets converted to assert or assume. It is just that corral isn't able to make best use of it.

I'm confused about this. If it gets converted to assume, then it should be used.

@akashlal
Copy link
Contributor

Yes, it is used, but still corral isn't able to construct a proof of correctness. There are other ways of making it put a proof together, e.g., using Houdini to infer pre-post conditions,

@bitcalc
Copy link

bitcalc commented Aug 20, 2019

We're Smack/Corral users. After looking through some Corral code, we don't think that we currently have the in-depth knowledge of Corral to make non-trivial changes or making use of Houdini for it. Could you please make a plan to solve this problem? The loop invariant is a distinctive feature of BoogiePL, and Corral should support it.

@akashlal
Copy link
Contributor

Can you more precisely define the behavior that you're looking for? Example: "if I supply this input, then this is how corral should behave".

@bitcalc
Copy link

bitcalc commented Aug 20, 2019

Here're two handy examples:

  1. For muyaview's example, corral should report the assertion violation.
  2. For a slightly modified shuvendu's example:
var a:[int]int;
var b:[int]int;

procedure {:entrypoint} Foo(n: int)
modifies a, b;
{
  var i:int;
  assume (n > 1); // make the loop interate

  a[0] := 0;
  b[0] := 0;

  i := 0;
  while (i < n)
  invariant a[0] == b[0];
  {
    a[i] := a[i] + 1;
    b[i] := b[i] + 1;
    i := i + 1;
  }
  assert (a[0] != b[0]);
}

Corral should report the assertion violation too.

We'll try to collect more examples to show the expected behaviors.

Thank you very much.

@shazqadeer
Copy link
Collaborator

shazqadeer commented Aug 20, 2019 via email

@akashlal
Copy link
Contributor

Let me reply to @bitcalc first. Corral is not design to meet your expectations. If there is an assertion that can fail, then Corral must be given an unroll bound that is enough to unroll the loops that are needed to get to the assertion. This issue is orthogonal to supporting invariant. Think about it as follows. Corral is under the obligation of returning a concrete error trace when it believes an assertion can fail. In @muyaview's example, the trace needs to go through the loop 10 times. There is no other way to get to the assert. Therefore, you must supply an unroll bound of 10, regardless of any invariant you supply.

The case of using invariant properly is only relevant to establishing a proof of correctness (or pruning search).

Does this make sense?

@akashlal
Copy link
Contributor

I will given an operational answer to @shazqadeer. When given a program with multiple procedures (no loops) that have ensures and requires, corral first instruments the program in the following manner. For each requires R, an assertion assert R is inserted on all call sites, and the requires is converted to a free requires. For each ensures E, an assertion assert E is inserted at the point of return from the procedure and the ensures itself becomes free ensures !assertsPassed || E. The variable assertsPassed is a global variable that is initially true but is assigned false when an assertion fails.

Therefore, corral does use procedure specifications, but it does check them as well to retain soundness. The free annotations act like default summaries that stratified inlining will use to prune search whenever possible.

Hope this is clear.

An invariant is lowered to an assert but no attempt is made to lift it to spec of the extracted procedure for the loop.

@bitcalc
Copy link

bitcalc commented Aug 21, 2019

I now see how Corral handles assertion violations and invariants. In Corral's terms, we are actually making an enhancement request here to combine these two so that Corral can use invariants provided manually during its verification process.

In its general form, the request comes in at different levels:

  1. For a given loop invariant, Corral should assume and use its truth in verifying assertions, and the use of invariant should take precedence over (or be controlled by a command option) loop unrolling.

  2. If Corral unrolls the loop with an invariant, it should check the correctness of the invariant up to the unrolling bounds, and gives information about it.

  3. Ideally, Corral should combine 1 and 2 so that a given loop invariant is assumed and used and meanwhile "checked" up to certain unrolling depth.

I don't know how difficult to do the above, but from a user's point of view, they're extremely useful because we have manually written loop invariants and need to use them in verifying program properties.

@akashlal
Copy link
Contributor

Some of this is possible, while other parts sound like research. I think if you want corral to check the invariant, then its very likely that corral will end up unrolling the loop completely. This is because corral does not fire off a separate inductiveness check for invariants. So it will keep unrolling and checking the asserted invariant.

On the flip side, if you provide a free invariant (i.e., assumed, not asserted) then we should enhance the ExtractLoops procedure in Boogie to lift free invariant to a free ensures of the extracted procedure. Shuvendu wanted to do this. @shuvendu-lahiri are you still up for it?

I think its a nice research problem to enhance corral with inductiveness checks (for loop invariants, as well as postconditions of recursive procedures) so that it can dramatically prune off search. Sounds like a good paper can come out of it.

@bitcalc
Copy link

bitcalc commented Aug 21, 2019

On the flip side, if you provide a free invariant (i.e., assumed, not asserted) then we should enhance the ExtractLoops procedure in Boogie to lift free invariant to a free ensures of the extracted procedure. Shuvendu wanted to do this. @shuvendu-lahiri are you still up for it?

I think its a nice research problem to enhance corral with inductiveness checks (for loop invariants, as well as postconditions of recursive procedures) so that it can dramatically prune off search. Sounds like a good paper can come out of it.

Both sound great plans, and we'd love to see them in Corral. Yes, free should be used before invariant to keep its semantics consistent with free ensures and free requires. For inductiveness checks, it's surely doable. We're happy to validate its effectiveness in some real-world application areas.

@shazqadeer
Copy link
Collaborator

shazqadeer commented Aug 21, 2019 via email

@zvonimir
Copy link
Collaborator

This makes sense to me, and I was wondering myself if there is anything more to it.

@bitcalc
Copy link

bitcalc commented Aug 22, 2019

Since ExtractLoops is in the boogie module, so I assume that it's Boogie code. I'm wondering how the Boogie verifier handles loop invariants. Does it check the validity of loop invariants by inductiveness or any other means?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants