Skip to content

Commit 1d7618c

Browse files
committed
Fix havocing of arrays when enforcing invariants
As reported in #6020, only the first element of an array was being havoced earlier. In this change, we fix this behavior using `havoc_object`.
1 parent a341c11 commit 1d7618c

File tree

15 files changed

+337
-17
lines changed

15 files changed

+337
-17
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
#define SIZE 8
5+
6+
void main()
7+
{
8+
char *data = malloc(SIZE * sizeof(char));
9+
data[5] = 0;
10+
11+
for(unsigned i = 0; i < SIZE; i++)
12+
__CPROVER_loop_invariant(i <= SIZE)
13+
{
14+
data[i] = 1;
15+
}
16+
17+
assert(data[5] == 0);
18+
assert(data[5] == 1);
19+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[main.1\] .* Check loop invariant before entry: SUCCESS$
7+
^\[main.2\] .* Check that loop invariant is preserved: SUCCESS$
8+
^\[main.assertion.1\] .* assertion data\[5\] == 0: FAILURE$
9+
^\[main.assertion.2\] .* assertion data\[5\] == 1: FAILURE$
10+
^VERIFICATION FAILED$
11+
--
12+
--
13+
Test case related to issue #6020: it checks that arrays are correctly havoced
14+
when enforcing loop invariant contracts.
15+
The `data[5] == 0` assertion is expected to fail since `data` is havoced.
16+
The `data[5] == 1` assertion is also expected to fail since the invariant does
17+
not reestablish the value for `data[5]`.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
#define SIZE 8
5+
6+
void main()
7+
{
8+
char *data = malloc(SIZE * sizeof(char));
9+
data[1] = 0;
10+
data[4] = 0;
11+
12+
for(unsigned i = 0; i < SIZE; i++)
13+
__CPROVER_loop_invariant(i <= SIZE)
14+
{
15+
data[1] = i;
16+
}
17+
18+
assert(data[1] == 0 || data[1] == 1);
19+
assert(data[4] == 0);
20+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[main.1\] .* Check loop invariant before entry: SUCCESS$
7+
^\[main.2\] .* Check that loop invariant is preserved: SUCCESS$
8+
^\[main.assertion.1\] .* assertion data\[1\] == 0 \|\| data\[1\] == 1: FAILURE$
9+
^\[main.assertion.2\] .* assertion data\[4\] == 0: SUCCESS$
10+
^VERIFICATION FAILED$
11+
--
12+
--
13+
Test case related to issue #6020: it checks that arrays are correctly havoced
14+
when enforcing loop invariant contracts.
15+
The `data[1] == 0 || data[1] == 1` assertion is expected to fail since `data[1]`
16+
is havoced and the invariant does not reestablish the value of `data[1]`.
17+
However, the `data[4] == 0` assertion is expected to pass -- we should not havoc
18+
the entire `data` array, if only a constant index if being modified.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
#define SIZE 8
5+
6+
void main()
7+
{
8+
char data[SIZE];
9+
data[5] = 0;
10+
11+
for(unsigned i = 0; i < SIZE; i++)
12+
__CPROVER_loop_invariant(i <= SIZE)
13+
{
14+
data[i] = 1;
15+
}
16+
17+
assert(data[5] == 0);
18+
assert(data[5] == 1);
19+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[main.1\] .* Check loop invariant before entry: SUCCESS$
7+
^\[main.2\] .* Check that loop invariant is preserved: SUCCESS$
8+
^\[main.assertion.1\] .* assertion data\[5\] == 0: FAILURE$
9+
^\[main.assertion.2\] .* assertion data\[5\] == 1: FAILURE$
10+
^VERIFICATION FAILED$
11+
--
12+
--
13+
Test case related to issue #6020: it checks that arrays are correctly havoced
14+
when enforcing loop invariant contracts.
15+
The `data[5] == 0` assertion is expected to fail since `data` is havoced.
16+
The `data[5] == 1` assertion is also expected to fail since the invariant does
17+
not reestablish the value for `data[5]`.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
#define SIZE 8
5+
6+
void main()
7+
{
8+
char data[SIZE];
9+
data[1] = 0;
10+
data[4] = 0;
11+
12+
for(unsigned i = 0; i < SIZE; i++)
13+
__CPROVER_loop_invariant(i <= SIZE)
14+
{
15+
data[1] = i;
16+
}
17+
18+
assert(data[1] == 0 || data[1] == 1);
19+
assert(data[4] == 0);
20+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[main.1\] .* Check loop invariant before entry: SUCCESS$
7+
^\[main.2\] .* Check that loop invariant is preserved: SUCCESS$
8+
^\[main.assertion.1\] .* assertion data\[1\] == 0 \|\| data\[1\] == 1: FAILURE$
9+
^\[main.assertion.2\] .* assertion data\[4\] == 0: SUCCESS$
10+
^VERIFICATION FAILED$
11+
--
12+
--
13+
Test case related to issue #6020: it checks that arrays are correctly havoced
14+
when enforcing loop invariant contracts.
15+
The `data[1] == 0 || data[1] == 1` assertion is expected to fail since `data[1]`
16+
is havoced and the invariant does not reestablish the value of `data[1]`.
17+
However, the `data[4] == 0` assertion is expected to pass -- we should not havoc
18+
the entire `data` array, if only a constant index if being modified.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
#define SIZE 8
5+
6+
void main()
7+
{
8+
char data[SIZE][SIZE][SIZE];
9+
10+
data[1][2][3] = 0;
11+
char *old_data123 = &(data[1][2][3]);
12+
13+
for(unsigned i = 0; i < SIZE; i++)
14+
__CPROVER_loop_invariant(i <= SIZE)
15+
{
16+
data[i][(i + 1) % SIZE][(i + 2) % SIZE] = 1;
17+
}
18+
19+
assert(__CPROVER_same_object(old_data123, &(data[1][2][3])));
20+
assert(data[1][2][3] == 0);
21+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[main.1\] .* Check loop invariant before entry: SUCCESS$
7+
^\[main.2\] .* Check that loop invariant is preserved: SUCCESS$
8+
^\[main.assertion.1\] .* assertion __CPROVER_same_object\(old_data123, &\(data\[1\]\[2\]\[3\]\)\): SUCCESS$
9+
^\[main.assertion.2\] .* assertion data\[1\]\[2\]\[3\] == 0: FAILURE$
10+
^VERIFICATION FAILED$
11+
--
12+
--
13+
Test case related to issue #6020: it checks that multi dimensional arrays
14+
are correctly havoced when enforcing invariant contracts.
15+
16+
The `__CPROVER_same_object(old_data123, &(data[1][2][3]))` assertion is expected
17+
to pass -- we should not mistakenly havoc the allocations, just their values.
18+
However, the `data[1][2][3] == 0` assertion is expected to fail since `data`
19+
is havoced.

0 commit comments

Comments
 (0)