Skip to content

Commit d84d2b4

Browse files
committed
Unify havocing code for all contracts
This change fixes multiple known issues: - (function contracts) entire arrays were not being havoced - (loop contracts) pointers not checked for nullness before havocing
1 parent 85d15ee commit d84d2b4

File tree

5 files changed

+78
-100
lines changed

5 files changed

+78
-100
lines changed

regression/contracts/assigns_replace_06/main.c

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,45 +4,30 @@ void foo(char c[]) __CPROVER_assigns(c)
44
{
55
}
66

7-
void bar(char d[]) __CPROVER_assigns(d)
7+
void bar(char *d) __CPROVER_assigns(*d)
88
{
99
}
1010

1111
int main()
1212
{
13-
char b[10];
14-
b[0] = 'a';
15-
b[1] = 'b';
16-
b[2] = 'c';
17-
b[3] = 'd';
18-
b[4] = 'e';
19-
b[5] = 'f';
20-
b[6] = 'g';
21-
b[7] = 'h';
22-
b[8] = 'i';
23-
b[9] = 'j';
13+
char b[4] = {'a', 'b', 'c', 'd'};
14+
2415
foo(b);
25-
assert(b[0] == 'a');
26-
assert(b[1] == 'b');
27-
assert(b[5] == 'f');
28-
assert(b[6] == 'g');
29-
assert(b[7] == 'h');
30-
assert(b[8] == 'i');
31-
assert(b[9] == 'j');
32-
33-
b[2] = 'c';
34-
b[3] = 'd';
35-
b[4] = 'e';
36-
bar(b);
16+
3717
assert(b[0] == 'a');
3818
assert(b[1] == 'b');
3919
assert(b[2] == 'c');
4020
assert(b[3] == 'd');
41-
assert(b[4] == 'e');
42-
assert(b[5] == 'f');
43-
assert(b[6] == 'g');
44-
assert(b[8] == 'i');
45-
assert(b[9] == 'j');
21+
22+
b[1] = '1';
23+
b[3] = '3';
24+
25+
bar(b + 3);
26+
27+
assert(b[0] == 'a');
28+
assert(b[1] == '1');
29+
assert(b[2] == 'c');
30+
assert(b[3] == '3');
4631

4732
return 0;
4833
}
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
CORE
22
main.c
3-
--replace-all-calls-with-contracts
3+
--replace-all-calls-with-contracts _ --pointer-primitive-check
4+
^\[main.assertion.1\] line \d+ assertion b\[0\] == 'a': FAILURE$
5+
^\[main.assertion.2\] line \d+ assertion b\[1\] == 'b': FAILURE$
6+
^\[main.assertion.3\] line \d+ assertion b\[2\] == 'c': FAILURE$
7+
^\[main.assertion.4\] line \d+ assertion b\[3\] == 'd': FAILURE$
8+
^\[main.assertion.5\] line \d+ assertion b\[0\] == 'a': FAILURE$
9+
^\[main.assertion.6\] line \d+ assertion b\[1\] == '1': SUCCESS$
10+
^\[main.assertion.7\] line \d+ assertion b\[2\] == 'c': FAILURE$
11+
^\[main.assertion.8\] line \d+ assertion b\[3\] == '3': FAILURE$
412
^EXIT=10$
513
^SIGNAL=0$
614
^VERIFICATION FAILED$
715
--
16+
^\[.+\.pointer_primitives\.\d+] line .*: FAILURE$
817
--
9-
Checks whether the values outside the array range specified by the assigns
10-
target are not havocked when calling the associated function.
18+
Checks that entire arrays and fixed single elements are correctly havoced
19+
when functions are replaced by contracts.

src/goto-instrument/contracts/assigns.cpp

Lines changed: 9 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -106,22 +106,6 @@ exprt assigns_clause_targett::compatible_expression(
106106
return same_object(called_target.get_direct_pointer(), target);
107107
}
108108

109-
goto_programt assigns_clause_targett::havoc_code() const
110-
{
111-
goto_programt assigns_havoc;
112-
source_locationt location = pointer_object.source_location();
113-
114-
exprt lhs = dereference_exprt(pointer_object);
115-
side_effect_expr_nondett rhs(lhs.type(), location);
116-
117-
goto_programt::targett target =
118-
assigns_havoc.add(goto_programt::make_assignment(
119-
code_assignt(std::move(lhs), std::move(rhs)), location));
120-
target->code_nonconst().add_source_location() = location;
121-
122-
return assigns_havoc;
123-
}
124-
125109
const exprt &assigns_clause_targett::get_direct_pointer() const
126110
{
127111
return pointer_object;
@@ -199,47 +183,16 @@ goto_programt assigns_clauset::dead_stmts()
199183

200184
goto_programt assigns_clauset::havoc_code()
201185
{
202-
goto_programt havoc_statements;
203-
source_locationt location = assigns.source_location();
204-
205-
for(assigns_clause_targett *target : targets)
206-
{
207-
// (1) If the assigned target is not a dereference,
208-
// only include the havoc_statement
209-
210-
// (2) If the assigned target is a dereference, do the following:
211-
212-
// if(!__CPROVER_w_ok(target, 0)) goto z;
213-
// havoc_statements
214-
// z: skip
186+
modifiest modifies;
187+
std::for_each(
188+
targets.begin(),
189+
targets.end(),
190+
[&modifies](const assigns_clause_targett *t) {
191+
modifies.insert(to_address_of_expr(t->get_direct_pointer()).object());
192+
});
215193

216-
// create the z label
217-
goto_programt tmp_z;
218-
goto_programt::targett z = tmp_z.add(goto_programt::make_skip(location));
219-
220-
const auto &target_ptr = target->get_direct_pointer();
221-
222-
if(to_address_of_expr(target_ptr).object().id() == ID_dereference)
223-
{
224-
// create the condition
225-
exprt condition =
226-
not_exprt(w_ok_exprt(target_ptr, from_integer(0, unsigned_int_type())));
227-
havoc_statements.add(goto_programt::make_goto(z, condition, location));
228-
}
229-
230-
// create havoc_statements
231-
for(goto_programt::instructiont instruction :
232-
target->havoc_code().instructions)
233-
{
234-
havoc_statements.add(std::move(instruction));
235-
}
236-
237-
if(to_address_of_expr(target_ptr).object().id() == ID_dereference)
238-
{
239-
// add the z label instruction
240-
havoc_statements.destructive_append(tmp_z);
241-
}
242-
}
194+
goto_programt havoc_statements;
195+
append_havoc_code(assigns.source_location(), modifies, havoc_statements);
243196
return havoc_statements;
244197
}
245198

src/goto-instrument/contracts/assigns.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ class assigns_clause_targett
3232
std::vector<symbol_exprt> temporary_declarations() const;
3333
exprt alias_expression(const exprt &lhs);
3434
exprt compatible_expression(const assigns_clause_targett &called_target);
35-
goto_programt havoc_code() const;
3635
const exprt &get_direct_pointer() const;
3736
goto_programt &get_init_block();
3837

src/goto-instrument/havoc_utils.cpp

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Date: July 2021
1313

1414
#include "havoc_utils.h"
1515

16+
#include <util/arith_tools.h>
17+
#include <util/c_types.h>
1618
#include <util/expr_util.h>
1719
#include <util/pointer_expr.h>
1820

@@ -36,28 +38,57 @@ class havoc_utils_is_constantt : public is_constantt
3638
const modifiest &modifies;
3739
};
3840

41+
void append_safe_havoc_code_for_expr(
42+
const source_locationt source_location,
43+
const exprt &expr,
44+
goto_programt &dest,
45+
std::function<void()> havoc_code_impl)
46+
{
47+
goto_programt skip_program;
48+
goto_programt::targett skip_target =
49+
skip_program.add(goto_programt::make_skip(source_location));
50+
51+
if(expr.id() == ID_dereference)
52+
{
53+
// check for validity of underlying pointer and skip havocing if invalid
54+
const auto condition = not_exprt(w_ok_exprt(
55+
to_dereference_expr(expr).pointer(),
56+
from_integer(0, unsigned_int_type())));
57+
dest.add(goto_programt::make_goto(skip_target, condition, source_location));
58+
}
59+
60+
havoc_code_impl();
61+
62+
if(expr.id() == ID_dereference)
63+
dest.destructive_append(skip_program);
64+
}
65+
3966
void append_object_havoc_code_for_expr(
4067
const source_locationt source_location,
41-
const exprt &lhs,
68+
const exprt &expr,
4269
goto_programt &dest)
4370
{
44-
codet havoc(ID_havoc_object);
45-
havoc.add_source_location() = source_location;
46-
havoc.add_to_operands(lhs);
71+
append_safe_havoc_code_for_expr(source_location, expr, dest, [&]() {
72+
codet havoc(ID_havoc_object);
73+
havoc.add_source_location() = source_location;
74+
havoc.add_to_operands(expr);
4775

48-
dest.add(goto_programt::make_other(havoc, source_location));
76+
dest.add(goto_programt::make_other(havoc, source_location));
77+
});
4978
}
5079

5180
void append_scalar_havoc_code_for_expr(
5281
const source_locationt source_location,
53-
const exprt &lhs,
82+
const exprt &expr,
5483
goto_programt &dest)
5584
{
56-
side_effect_expr_nondett rhs(lhs.type(), source_location);
85+
append_safe_havoc_code_for_expr(source_location, expr, dest, [&]() {
86+
side_effect_expr_nondett rhs(expr.type(), source_location);
5787

58-
goto_programt::targett t = dest.add(
59-
goto_programt::make_assignment(lhs, std::move(rhs), source_location));
60-
t->code_nonconst().add_source_location() = source_location;
88+
goto_programt::targett t = dest.add(
89+
goto_programt::make_assignment(expr, std::move(rhs), source_location));
90+
t->code_nonconst().add_source_location() = source_location;
91+
});
6192
}
6293

6394
void append_havoc_code(
@@ -73,7 +104,8 @@ void append_havoc_code(
73104
address_of_exprt address_of_lhs(lhs);
74105
if(!is_constant(address_of_lhs))
75106
{
76-
append_object_havoc_code_for_expr(source_location, address_of_lhs, dest);
107+
append_object_havoc_code_for_expr(
108+
source_location, address_of_lhs, dest);
77109
continue;
78110
}
79111
}

0 commit comments

Comments
 (0)