Skip to content

Commit 64fe788

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 dcf9101 commit 64fe788

File tree

6 files changed

+78
-98
lines changed

6 files changed

+78
-98
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: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,29 +13,61 @@ Date: July 2021
1313

1414
#include "havoc_utils.h"
1515

16-
#include <util/expr_util.h>
16+
#include <util/arith_tools.h>
17+
#include <util/c_types.h>
1718
#include <util/pointer_expr.h>
1819

20+
void append_safe_havoc_code_for_expr(
21+
const source_locationt source_location,
22+
const exprt &expr,
23+
goto_programt &dest,
24+
const std::function<void()> &havoc_code_impl)
25+
{
26+
goto_programt skip_program;
27+
const auto skip_target =
28+
skip_program.add(goto_programt::make_skip(source_location));
29+
30+
// for deref expressions, check for validity of underlying pointer,
31+
// and skip havocing if invalid (to avoid spurious pointer deref errors)
32+
if(expr.id() == ID_dereference)
33+
{
34+
const auto condition = not_exprt(w_ok_exprt(
35+
to_dereference_expr(expr).pointer(),
36+
from_integer(0, unsigned_int_type())));
37+
dest.add(goto_programt::make_goto(skip_target, condition, source_location));
38+
}
39+
40+
havoc_code_impl();
41+
42+
// for deref expressions, add the final skip target
43+
if(expr.id() == ID_dereference)
44+
dest.destructive_append(skip_program);
45+
}
46+
1947
void append_object_havoc_code_for_expr(
2048
const source_locationt source_location,
2149
const exprt &expr,
2250
goto_programt &dest)
2351
{
24-
codet havoc(ID_havoc_object);
25-
havoc.add_source_location() = source_location;
26-
havoc.add_to_operands(expr);
27-
dest.add(goto_programt::make_other(havoc, source_location));
52+
append_safe_havoc_code_for_expr(source_location, expr, dest, [&]() {
53+
codet havoc(ID_havoc_object);
54+
havoc.add_source_location() = source_location;
55+
havoc.add_to_operands(expr);
56+
dest.add(goto_programt::make_other(havoc, source_location));
57+
});
2858
}
2959

3060
void append_scalar_havoc_code_for_expr(
3161
const source_locationt source_location,
3262
const exprt &expr,
3363
goto_programt &dest)
3464
{
35-
side_effect_expr_nondett rhs(expr.type(), source_location);
36-
goto_programt::targett t = dest.add(
37-
goto_programt::make_assignment(expr, std::move(rhs), source_location));
38-
t->code_nonconst().add_source_location() = source_location;
65+
append_safe_havoc_code_for_expr(source_location, expr, dest, [&]() {
66+
side_effect_expr_nondett rhs(expr.type(), source_location);
67+
goto_programt::targett t = dest.add(
68+
goto_programt::make_assignment(expr, std::move(rhs), source_location));
69+
t->code_nonconst().add_source_location() = source_location;
70+
});
3971
}
4072

4173
void append_havoc_code(

src/goto-instrument/havoc_utils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ Date: July 2021
1818

1919
#include <goto-programs/goto_program.h>
2020

21+
#include <util/expr_util.h>
22+
2123
typedef std::set<exprt> modifiest;
2224

2325
class havoc_utils_is_constantt : public is_constantt

0 commit comments

Comments
 (0)