Skip to content

Commit fb14521

Browse files
committed
Fix havocing of struct members when enforcing invariants
Members expressions were ignored when computing the modified set of loops, and were therefore not being havoc'ed correctly. In this change we fix this behavior by adding these expressions to the `modified` set.
1 parent 5601657 commit fb14521

File tree

5 files changed

+85
-4
lines changed

5 files changed

+85
-4
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
typedef struct test
2+
{
3+
int x;
4+
} test;
5+
6+
void main()
7+
{
8+
struct test *t = malloc(sizeof(test));
9+
t->x = 0;
10+
11+
unsigned n;
12+
for(unsigned i = 0; i < n; ++i)
13+
__CPROVER_loop_invariant(i <= n)
14+
{
15+
switch(i % 4)
16+
{
17+
case 0:
18+
break;
19+
20+
case 1:
21+
case 2:
22+
t->x += 1;
23+
24+
default:
25+
t->x += 2;
26+
}
27+
}
28+
29+
assert(t->x == 0 || t->x == 1 || t->x == 2);
30+
}
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 .*: FAILURE$
9+
^VERIFICATION FAILED$
10+
--
11+
--
12+
This test checks that members of typedef'd and dynamically allocated structs
13+
are correctly havoced when enforcing loop invariants.
14+
The assertion is expected to fail when `t->x` is correctly havoced (so would be
15+
set to a nondet value).
16+
However, it `t->x` is not havoced then it stays at value `0` and would satisfy
17+
the assertion when the loop is replaced by a single nondet iteration.
18+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
struct test
2+
{
3+
int x;
4+
};
5+
6+
void main()
7+
{
8+
struct test t;
9+
t.x = 0;
10+
11+
unsigned n;
12+
for(unsigned i = 0; i < n; ++i)
13+
__CPROVER_loop_invariant(i <= n)
14+
{
15+
t.x += 2;
16+
}
17+
18+
assert(t.x == 0 || t.x == 2);
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 t.x == 0 || t.x == 2: FAILURE$
9+
^VERIFICATION FAILED$
10+
--
11+
--
12+
This test checks that members of statically allocated are correctly havoced
13+
when enforcing loop invariants.
14+
The `t.x == 0 || t.x == 2` assertion is expected to fail when `t.x` is correctly
15+
havoced (so would be set to a nondet value).
16+
However, it `t.x` is not havoced then it stays at value `0` and would satisfy
17+
the assertion when the loop is replaced by a single nondet iteration.

src/goto-instrument/loop_utils.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,14 @@ void get_modifies_lhs(
6161
const exprt &lhs,
6262
modifiest &modifies)
6363
{
64-
if(lhs.id()==ID_symbol)
64+
if(lhs.id() == ID_symbol || lhs.id() == ID_member)
6565
modifies.insert(lhs);
6666
else if(lhs.id()==ID_dereference)
6767
{
6868
const auto &pointer = to_dereference_expr(lhs).pointer();
6969
for(const auto &mod : local_may_alias.get(t, pointer))
7070
modifies.insert(dereference_exprt{mod});
7171
}
72-
else if(lhs.id()==ID_member)
73-
{
74-
}
7572
else if(lhs.id()==ID_index)
7673
{
7774
}

0 commit comments

Comments
 (0)