Skip to content

Commit ae4f1d6

Browse files
authored
Merge pull request #6701 from padhi-aws-forks/loop-contracts-simplify
CONTRACTS: Several fixes and sanity checks for loop contracts instrumentation
2 parents fdda7a8 + af24259 commit ae4f1d6

File tree

15 files changed

+713
-235
lines changed

15 files changed

+713
-235
lines changed

regression/contracts/invar_check_nested_loops/test.desc

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ main.c
99
^\[main\.\d+\] .* Check loop invariant before entry: SUCCESS$
1010
^\[main\.\d+\] .* Check that loop invariant is preserved: SUCCESS$
1111
^\[main\.\d+\] .* Check decreases clause on loop iteration: SUCCESS$
12-
^\[main.assigns.\d+] line 23 Check that s is assignable: SUCCESS$
13-
^\[main.assigns.\d+] line 24 Check that a is assignable: SUCCESS$
14-
^\[main.assigns.\d+] line 27 Check that s is assignable: SUCCESS$
12+
^\[main.assigns.\d+] .* line 23 Check that s is assignable: SUCCESS$
13+
^\[main.assigns.\d+] .* line 24 Check that a is assignable: SUCCESS$
14+
^\[main.assigns.\d+] .* line 27 Check that s is assignable: SUCCESS$
1515
^\[main\.assertion.\d+\] .* assertion s == n: SUCCESS$
1616
^VERIFICATION SUCCESSFUL$
1717
--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <assert.h>
2+
3+
int main()
4+
{
5+
int i = 0, j = 0, k = 0;
6+
7+
while(j < 10)
8+
{
9+
while(k < 10)
10+
__CPROVER_loop_invariant(1 == 1)
11+
{
12+
while(i < 10)
13+
{
14+
i++;
15+
}
16+
}
17+
j++;
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
--apply-loop-contracts
4+
activate-multi-line-match
5+
Inner loop at: file main.c line 12 function main does not have contracts, but an enclosing loop does.\nPlease provide contracts for this loop, or unwind it first.
6+
^EXIT=(6|10)$
7+
^SIGNAL=0$
8+
--
9+
--
10+
This test checks that our instrumentation routines verify that inner loops
11+
either have contracts or are unwound, if any enclosing loop has contracts.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#include <assert.h>
2+
#include <limits.h>
3+
#include <stdbool.h>
4+
5+
bool check(unsigned *i, unsigned *j, unsigned *k)
6+
{
7+
(*j)++;
8+
return *i < *k;
9+
}
10+
11+
int main()
12+
{
13+
unsigned i, j, k;
14+
__CPROVER_assume(k < UINT_MAX);
15+
16+
i = j = 0;
17+
18+
while(check(&i, &j, &k))
19+
// clang-format off
20+
__CPROVER_assigns(i, j)
21+
__CPROVER_loop_invariant(0 <= i && i == j && i <= k)
22+
// clang-format on
23+
{
24+
i++;
25+
}
26+
27+
assert(i == k);
28+
assert(j == k + 1);
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
CORE
2+
main.c
3+
--apply-loop-contracts _ --unsigned-overflow-check
4+
\[check.assigns\.\d+\] line \d+ Check that \*j is assignable: SUCCESS$
5+
\[check.overflow\.\d+\] line \d+ arithmetic overflow on unsigned \+ in \*j \+ 1u: SUCCESS
6+
\[check.overflow\.\d+\] line \d+ arithmetic overflow on unsigned \+ in \*j \+ 1u: SUCCESS
7+
\[check.assigns\.\d+\] line \d+ Check that return_value_check is assignable: SUCCESS$
8+
\[main\.\d+\] line \d+ Check loop invariant before entry: SUCCESS$
9+
\[main\.\d+\] line \d+ Check that loop invariant is preserved: SUCCESS$
10+
\[main.assigns\.\d+\] line \d+ Check that i is assignable: SUCCESS$
11+
\[main.assigns\.\d+\] line \d+ Check that j is assignable: SUCCESS$
12+
\[main.assigns\.\d+\] line \d+ Check that k is assignable: SUCCESS$
13+
\[main.assigns\.\d+\] line \d+ Check that i is valid: SUCCESS$
14+
\[main.assigns\.\d+\] line \d+ Check that j is valid: SUCCESS$
15+
\[main.assigns\.\d+\] line \d+ Check that i is assignable: SUCCESS$
16+
\[main.overflow\.\d+\] line \d+ arithmetic overflow on unsigned \+ in i \+ 1u: SUCCESS
17+
\[main.assertion\.\d+\] line \d+ assertion i == k: SUCCESS$
18+
\[main.overflow\.\d+\] line \d+ arithmetic overflow on unsigned \+ in k \+ \(unsigned int\)1: SUCCESS
19+
\[main.assertion\.\d+\] line \d+ assertion j == k \+ 1: SUCCESS$
20+
^EXIT=0$
21+
^SIGNAL=0$
22+
--
23+
--
24+
This test demonstrates a case where the loop guard has side effects.
25+
The loop contracts must specify the state of all modified variables,
26+
including those in the loop guard.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include <assert.h>
2+
#include <stdbool.h>
3+
4+
bool check(unsigned *i, unsigned *j, unsigned *k)
5+
{
6+
(*j)++;
7+
return *i < *k;
8+
}
9+
10+
int main()
11+
{
12+
unsigned i, j, k;
13+
14+
i = j = 0;
15+
16+
while(check(&i, &j, &k))
17+
// clang-format off
18+
__CPROVER_assigns(i)
19+
__CPROVER_loop_invariant(0 <= i && i <= k)
20+
// clang-format on
21+
{
22+
i++;
23+
}
24+
25+
assert(i == k);
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
CORE
2+
main.c
3+
--apply-loop-contracts _ --unsigned-overflow-check
4+
\[check.assigns\.\d+\] line \d+ Check that \*j is assignable: FAILURE$
5+
\[check.assigns\.\d+\] line \d+ Check that return_value_check is assignable: SUCCESS$
6+
\[main\.\d+\] line \d+ Check loop invariant before entry: SUCCESS$
7+
\[main\.\d+\] line \d+ Check that loop invariant is preserved: SUCCESS$
8+
\[main.assigns\.\d+\] line \d+ Check that i is assignable: SUCCESS$
9+
\[main.assigns\.\d+\] line \d+ Check that j is assignable: SUCCESS$
10+
\[main.assigns\.\d+\] line \d+ Check that k is assignable: SUCCESS$
11+
\[main.assigns\.\d+\] line \d+ Check that i is valid: SUCCESS$
12+
\[main.assigns\.\d+\] line \d+ Check that i is assignable: SUCCESS$
13+
\[main.assertion\.\d+\] line \d+ assertion i == k: SUCCESS$
14+
^EXIT=(6|10)$
15+
^SIGNAL=0$
16+
--
17+
--
18+
This test demonstrates a case where the loop guard has side effects.
19+
The writes performed in the guard must also be specified
20+
in the assigns clause of the loop.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
int main()
5+
{
6+
size_t size;
7+
8+
char *buf = malloc(size);
9+
char c;
10+
11+
size_t start;
12+
size_t end = start;
13+
14+
while(end < size)
15+
// clang-format off
16+
__CPROVER_loop_invariant(start <= end && end <= size)
17+
__CPROVER_decreases(size - end)
18+
// clang-format on
19+
{
20+
if(buf[end] != c)
21+
break;
22+
end++;
23+
}
24+
25+
if(start > size)
26+
{
27+
assert(end == start);
28+
}
29+
else
30+
{
31+
assert(start <= end && end <= size);
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
CORE
2+
main.c
3+
--apply-loop-contracts _ --signed-overflow-check --unsigned-overflow-check
4+
\[main.\d+\] line \d+ Check loop invariant before entry: SUCCESS$
5+
\[main.\d+\] line \d+ Check that loop invariant is preserved: SUCCESS$
6+
\[main.assigns.\d+\] line \d+ Check that end is valid: SUCCESS$
7+
\[main.assigns.\d+\] line \d+ Check that end is assignable: SUCCESS$
8+
\[main.assertion.\d+\] line \d+ assertion end == start: SUCCESS$
9+
\[main.assertion.\d+\] line \d+ assertion start <= end && end <= size: SUCCESS$
10+
^EXIT=0$
11+
^SIGNAL=0$
12+
--
13+
--
14+
This test demonstrates a simplification that is now possible on loop contracts.
15+
16+
Prior to this commit, loop contracts were unconditionally applied on loops,
17+
and thus had to also consider the case when the loop doesn't run even once.
18+
19+
The contracts that were previously necessary were:
20+
__CPROVER_loop_invariant(
21+
(start > size && end == start) || (start <= end && end <= size)
22+
)
23+
__CPROVER_decreases(
24+
max(start, size) - end
25+
)

regression/contracts/variant_missing_invariant_warning/test.desc

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ CORE
22
main.c
33
--apply-loop-contracts
44
activate-multi-line-match
5-
^The loop at file main.c line 4 function main does not have an invariant.*.\nHence, a default invariant \('true'\) is being used to prove those.$
5+
^The loop at file main.c line 4 function main does not have an invariant.*.\nHence, a default invariant \('true'\) is being used.*.$
66
^\[main\.\d+\] .* Check loop invariant before entry: SUCCESS$
77
^\[main\.\d+\] .* Check that loop invariant is preserved: SUCCESS$
88
^\[main\.\d+\] .* Check decreases clause on loop iteration: SUCCESS$

regression/contracts/variant_multi_instruction_loop_head/main.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ int main()
55

66
while(*y <= 0 && *y < 10)
77
// clang-format off
8-
__CPROVER_loop_invariant(1 == 1)
8+
__CPROVER_loop_invariant(0 <= *y && *y <= 10)
99
__CPROVER_decreases(10 - x)
1010
// clang-format on
1111
{
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
CORE
22
main.c
33
--apply-loop-contracts
4-
^VERIFICATION FAILED$
5-
^EXIT=10$
4+
\[main\.\d+\] line \d+ Check loop invariant before entry: SUCCESS$
5+
\[main\.\d+\] line \d+ Check that loop invariant is preserved: SUCCESS$
6+
\[main\.\d+\] line \d+ Check decreases clause on loop iteration: SUCCESS$
7+
\[main\.\d+\] line \d+ Check that loop instrumentation was not truncated: SUCCESS$
8+
\[main\.assigns\.\d+\] line \d+ Check that x is valid: SUCCESS$
9+
\[main\.assigns\.\d+\] line \d+ Check that x is assignable: SUCCESS$
10+
^VERIFICATION SUCCESSFUL$
11+
^EXIT=0$
612
^SIGNAL=0$
713
--
814
--
9-
This test fails even without the fix proposed in the commit, so it should be improved.
10-
It is expected to fail because the proposed invariant isn't strong enough to help prove
11-
termination using the specified variant.
12-
13-
The test highlights a case where a C loop guard is compiled to multiple GOTO instructions.
14-
Therefore the loop_head pointer needs to be advanced multiple times to get to the loop body,
15-
where the initial value of the loop variant (/ ranking function) must be recorded.
16-
17-
The proposed fix advances the pointer until the source_location differs from the original
18-
loop_head's source_location. However, this might still not work if the loop guard in the
19-
original C code was split across multiple lines in the first place.
15+
This test checks that decreases clause is properly initialized
16+
when the loop head and loop invariant compilation emit several goto instructions.

0 commit comments

Comments
 (0)