Skip to content

Commit 1268cc1

Browse files
authored
Merge pull request #6292 from padhi-aws-forks/havoc_fix
Unify havocing codegen within code contracts
2 parents 24dd735 + 55cbcb7 commit 1268cc1

File tree

12 files changed

+206
-173
lines changed

12 files changed

+206
-173
lines changed

regression/contracts/assigns_replace_06/main.c

+14-29
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
}
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/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ SRC = accelerate/accelerate.cpp \
4343
goto_instrument_parse_options.cpp \
4444
goto_program2code.cpp \
4545
havoc_loops.cpp \
46+
havoc_utils.cpp \
4647
horn_encoding.cpp \
4748
insert_final_assert_false.cpp \
4849
interrupt.cpp \

src/goto-instrument/contracts/assigns.cpp

+7-56
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Date: July 2021
1313

1414
#include "assigns.h"
1515

16+
#include <goto-instrument/havoc_utils.h>
17+
1618
#include <util/arith_tools.h>
1719
#include <util/c_types.h>
1820
#include <util/pointer_predicates.h>
@@ -104,22 +106,6 @@ exprt assigns_clause_targett::compatible_expression(
104106
return same_object(called_target.get_direct_pointer(), target);
105107
}
106108

107-
goto_programt assigns_clause_targett::havoc_code() const
108-
{
109-
goto_programt assigns_havoc;
110-
source_locationt location = pointer_object.source_location();
111-
112-
exprt lhs = dereference_exprt(pointer_object);
113-
side_effect_expr_nondett rhs(lhs.type(), location);
114-
115-
goto_programt::targett target =
116-
assigns_havoc.add(goto_programt::make_assignment(
117-
code_assignt(std::move(lhs), std::move(rhs)), location));
118-
target->code_nonconst().add_source_location() = location;
119-
120-
return assigns_havoc;
121-
}
122-
123109
const exprt &assigns_clause_targett::get_direct_pointer() const
124110
{
125111
return pointer_object;
@@ -197,47 +183,12 @@ goto_programt assigns_clauset::dead_stmts()
197183

198184
goto_programt assigns_clauset::havoc_code()
199185
{
200-
goto_programt havoc_statements;
201-
source_locationt location = assigns.source_location();
202-
203-
for(assigns_clause_targett *target : targets)
204-
{
205-
// (1) If the assigned target is not a dereference,
206-
// only include the havoc_statement
207-
208-
// (2) If the assigned target is a dereference, do the following:
209-
210-
// if(!__CPROVER_w_ok(target, 0)) goto z;
211-
// havoc_statements
212-
// z: skip
186+
modifiest modifies;
187+
for(const auto &t : targets)
188+
modifies.insert(to_address_of_expr(t->get_direct_pointer()).object());
213189

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

src/goto-instrument/contracts/assigns.h

-1
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/contracts/contracts.cpp

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*******************************************************************\
22
3-
Module: Verify and use annotated invariants and pre/post-conditions
3+
Module: Verify and use annotated loop and function contracts
44
55
Author: Michael Tautschnig
66
@@ -9,20 +9,19 @@ Date: February 2016
99
\*******************************************************************/
1010

1111
/// \file
12-
/// Verify and use annotated invariants and pre/post-conditions
12+
/// Verify and use annotated loop and function contracts
1313

1414
#include "contracts.h"
1515

16-
#include "assigns.h"
17-
#include "memory_predicates.h"
18-
1916
#include <algorithm>
2017
#include <map>
2118

2219
#include <analyses/local_may_alias.h>
2320

2421
#include <ansi-c/c_expr.h>
2522

23+
#include <goto-instrument/havoc_utils.h>
24+
2625
#include <goto-programs/remove_skip.h>
2726

2827
#include <util/c_types.h>
@@ -34,6 +33,9 @@ Date: February 2016
3433
#include <util/pointer_offset_size.h>
3534
#include <util/replace_symbol.h>
3635

36+
#include "assigns.h"
37+
#include "memory_predicates.h"
38+
3739
// Create a lexicographic less-than relation between two tuples of variables.
3840
// This is used in the implementation of multidimensional decreases clauses.
3941
static exprt create_lexicographic_less_than(
@@ -185,8 +187,8 @@ void code_contractst::check_apply_loop_contracts(
185187
"Check loop invariant before entry");
186188
}
187189

188-
// havoc variables being written to
189-
build_havoc_code(loop_head, modifies, havoc_code);
190+
// havoc the variables that may be modified
191+
append_havoc_code(loop_head->source_location, modifies, havoc_code);
190192

191193
// Generate: assume(invariant) just after havocing
192194
// We use a block scope to create a temporary assumption,

src/goto-instrument/havoc_loops.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Author: Daniel Kroening, [email protected]
1717
#include <goto-programs/remove_skip.h>
1818

1919
#include "function_modifies.h"
20+
#include "havoc_utils.h"
2021
#include "loop_utils.h"
2122

2223
class havoc_loopst
@@ -67,7 +68,7 @@ void havoc_loopst::havoc_loop(
6768

6869
// build the havocking code
6970
goto_programt havoc_code;
70-
build_havoc_code(loop_head, modifies, havoc_code);
71+
append_havoc_code(loop_head->source_location, modifies, havoc_code);
7172

7273
// Now havoc at the loop head. Use insert_swap to
7374
// preserve jumps to loop head.

src/goto-instrument/havoc_utils.cpp

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*******************************************************************\
2+
3+
Module: Utilities for building havoc code for expressions.
4+
5+
Author: Saswat Padhi, [email protected]
6+
7+
Date: July 2021
8+
9+
\*******************************************************************/
10+
11+
/// \file
12+
/// Utilities for building havoc code for expressions
13+
14+
#include "havoc_utils.h"
15+
16+
#include <util/arith_tools.h>
17+
#include <util/c_types.h>
18+
#include <util/pointer_expr.h>
19+
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+
47+
void append_object_havoc_code_for_expr(
48+
const source_locationt source_location,
49+
const exprt &expr,
50+
goto_programt &dest)
51+
{
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+
});
58+
}
59+
60+
void append_scalar_havoc_code_for_expr(
61+
const source_locationt source_location,
62+
const exprt &expr,
63+
goto_programt &dest)
64+
{
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+
});
71+
}
72+
73+
void append_havoc_code(
74+
const source_locationt source_location,
75+
const modifiest &modifies,
76+
goto_programt &dest)
77+
{
78+
havoc_utils_is_constantt is_constant(modifies);
79+
for(const auto &expr : modifies)
80+
{
81+
if(expr.id() == ID_index || expr.id() == ID_dereference)
82+
{
83+
address_of_exprt address_of_expr(expr);
84+
if(!is_constant(address_of_expr))
85+
{
86+
append_object_havoc_code_for_expr(
87+
source_location, address_of_expr, dest);
88+
continue;
89+
}
90+
}
91+
92+
append_scalar_havoc_code_for_expr(source_location, expr, dest);
93+
}
94+
}

0 commit comments

Comments
 (0)