Skip to content

Commit f97776b

Browse files
committed
Add a predicate to check validity of all derefs
This is useful for: + checking containment in assigns clause verification + havocing only valid memory locations in assigns clause replacement + creating history snapshots only for valid `old` expressions
1 parent 46af9ff commit f97776b

File tree

12 files changed

+110
-67
lines changed

12 files changed

+110
-67
lines changed

regression/contracts/assigns_enforce_structs_07/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ int main()
2828
struct pair *p = nondet_bool() ? malloc(sizeof(*p)) : NULL;
2929
f1(p);
3030
struct pair_of_pairs *pp = malloc(sizeof(*pp));
31+
pp->p = nondet_bool() ? malloc(sizeof(*(pp->p))) : NULL;
3132
f2(pp);
3233

3334
return 0;
Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
KNOWNBUG
1+
CORE
22
main.c
33
--enforce-all-contracts _ --pointer-check
44
^EXIT=10$
55
^SIGNAL=0$
6-
^\[f1.\d+\] line \d+ Check that p->buf\[\(.*\)0\] is assignable: FAILURE$
6+
^\[f1.\d+\] line \d+ Check that p->buf\[\(.*\)0\] is assignable: SUCCESS$
7+
^\[f1.pointer\_dereference.\d+\] line \d+ dereference failure: pointer invalid in p->buf\[\(.*\)0\]: FAILURE$
78
^\[f1.pointer\_dereference.\d+\] line \d+ dereference failure: pointer NULL in p->buf: FAILURE$
8-
^\[f2.\d+\] line \d+ Check that pp->p->buf\[\(.*\)0\] is assignable: FAILURE$
9+
^\[f2.\d+\] line \d+ Check that pp->p->buf\[\(.*\)0\] is assignable: SUCCESS$
10+
^\[f2.pointer\_dereference.\d+\] line \d+ dereference failure: pointer invalid in pp->p->buf\[\(.*\)0\]: FAILURE$
911
^\[f2.pointer\_dereference.\d+\] line \d+ dereference failure: pointer NULL in pp->p->buf: FAILURE$
1012
^VERIFICATION FAILED$
1113
--
1214
--
1315
Checks whether CBMC properly evaluates write set of members
1416
from invalid objects. In this case, they are not writable.
15-
16-
Bug: We need to check the validity of each dereference
17-
when accessing struct members.

regression/contracts/assigns_replace_07/main.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,21 @@
44

55
struct pair
66
{
7-
uint8_t *buf;
8-
size_t size;
7+
uint8_t buf[8];
98
};
109

1110
void f1(struct pair *p) __CPROVER_assigns(*(p->buf))
12-
__CPROVER_ensures(p->buf[0] == 0)
11+
__CPROVER_ensures((p == NULL) || p->buf[0] == 0)
1312
{
14-
p->buf[0] = 0;
13+
if(p != NULL) p->buf[0] = 0;
1514
}
1615

1716
int main()
1817
{
1918
struct pair *p = nondet_bool() ? malloc(sizeof(*p)) : NULL;
2019
f1(p);
2120
// clang-format off
22-
assert(p != NULL ==> p->buf[0] == 0);
21+
assert(p == NULL || p->buf[0] == 0);
2322
// clang-format on
2423
return 0;
2524
}
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
CORE
22
main.c
33
--replace-all-calls-with-contracts _ --pointer-check
4-
^EXIT=10$
4+
^EXIT=0$
55
^SIGNAL=0$
6-
^\[pointer\_dereference.\d+\] file main.c line \d+ dereference failure: pointer NULL in p->buf: FAILURE$
7-
^\[main.assertion.\d+\] line \d+ assertion p \!\= NULL \=\=> p->buf\[0\] \=\= 0: FAILURE$
8-
^VERIFICATION FAILED$
6+
^\[main.assertion.\d+\] line \d+ assertion p == NULL \|\| p->buf\[0\] == 0: SUCCESS$
7+
^VERIFICATION SUCCESSFUL$
98
--
109
--
11-
Checks whether CBMC properly evaluates write set of members
12-
from invalid objects. In this case, they are not writable.
10+
Checks whether CBMC properly evaluates write set of members from invalid objects.
11+
Functions are not expected to write to invalid locations; CBMC flags such writes.
12+
For contract checking, we ignore invalid targets in assigns clauses and assignment LHS.

regression/contracts/invar_loop-entry_check/main.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,17 @@ void main()
3333
assert(x2 == z2);
3434

3535
int y3;
36-
s *s1, *s2;
36+
s s0, s1, *s2 = &s0;
3737
s2->n = malloc(sizeof(int));
38-
s1->n = s2->n;
38+
s1.n = s2->n;
3939

4040
while(y3 > 0)
41-
__CPROVER_loop_invariant(s1->n == __CPROVER_loop_entry(s1->n))
41+
__CPROVER_loop_invariant(s2->n == __CPROVER_loop_entry(s2->n))
4242
{
4343
--y3;
44-
s1->n = s1->n + 1;
45-
s1->n = s1->n - 1;
44+
s0.n = s0.n + 1;
45+
s2->n = s2->n - 1;
4646
}
4747

48-
assert(*(s1->n) == *(s2->n));
48+
assert(*(s1.n) == *(s2->n));
4949
}

regression/contracts/invar_loop-entry_check/test.desc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
CORE
22
main.c
3-
--apply-loop-contracts
3+
--apply-loop-contracts _ --pointer-primitive-check
44
^EXIT=0$
55
^SIGNAL=0$
66
^\[main.1\] .* Check loop invariant before entry: SUCCESS$
@@ -11,7 +11,7 @@ main.c
1111
^\[main.assertion.2\] .* assertion x2 == z2: SUCCESS$
1212
^\[main.5\] .* Check loop invariant before entry: SUCCESS$
1313
^\[main.6\] .* Check that loop invariant is preserved: SUCCESS$
14-
^\[main.assertion.3\] .* assertion \*\(s1->n\) == \*\(s2->n\): SUCCESS$
14+
^\[main.assertion.3\] .* assertion \*\(s1\.n\) == \*\(s2->n\): SUCCESS$
1515
^VERIFICATION SUCCESSFUL$
1616
--
1717
--

src/goto-instrument/contracts/assigns.cpp

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ Date: July 2021
1717

1818
#include <langapi/language_util.h>
1919

20-
#include <util/arith_tools.h>
21-
#include <util/c_types.h>
2220
#include <util/pointer_offset_size.h>
2321
#include <util/pointer_predicates.h>
2422

@@ -46,19 +44,13 @@ exprt assigns_clauset::targett::normalize(const exprt &expr)
4644
exprt assigns_clauset::targett::generate_containment_check(
4745
const address_of_exprt &lhs_address) const
4846
{
49-
exprt::operandst address_validity;
50-
exprt::operandst containment_check;
51-
5247
// __CPROVER_w_ok(target, sizeof(target))
53-
address_validity.push_back(w_ok_exprt(
54-
address,
55-
size_of_expr(dereference_exprt(address).type(), parent.ns).value()));
56-
5748
// __CPROVER_w_ok(lhs, sizeof(lhs))
58-
address_validity.push_back(w_ok_exprt(
59-
lhs_address,
60-
size_of_expr(dereference_exprt(lhs_address).type(), parent.ns).value()));
49+
const auto address_validity = and_exprt{
50+
validate_all_derefs(dereference_exprt{address}, parent.ns),
51+
validate_all_derefs(dereference_exprt{lhs_address}, parent.ns)};
6152

53+
exprt::operandst containment_check;
6254
// __CPROVER_same_object(lhs, target)
6355
containment_check.push_back(same_object(lhs_address, address));
6456

@@ -100,8 +92,7 @@ exprt assigns_clauset::targett::generate_containment_check(
10092
// && __CPROVER_offset(lhs) >= __CPROVER_offset(target)
10193
// && (sizeof(lhs) + __CPROVER_offset(lhs)) <=
10294
// (sizeof(target) + __CPROVER_offset(target))
103-
return binary_relation_exprt(
104-
conjunction(address_validity), ID_implies, conjunction(containment_check));
95+
return or_exprt{not_exprt{address_validity}, conjunction(containment_check)};
10596
}
10697

10798
assigns_clauset::assigns_clauset(
@@ -155,7 +146,7 @@ goto_programt assigns_clauset::generate_havoc_code() const
155146
modifies.insert(target.address.object());
156147

157148
goto_programt havoc_statements;
158-
append_havoc_code(location, modifies, havoc_statements);
149+
append_havoc_code_if_valid(location, ns, modifies, havoc_statements);
159150
return havoc_statements;
160151
}
161152

src/goto-instrument/contracts/contracts.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Date: February 2016
3333
#include <util/mathematical_types.h>
3434
#include <util/message.h>
3535
#include <util/pointer_offset_size.h>
36+
#include <util/pointer_predicates.h>
3637
#include <util/replace_symbol.h>
3738

3839
#include "assigns.h"
@@ -366,6 +367,11 @@ void code_contractst::replace_history_parameter(
366367

367368
if(it == parameter2history.end())
368369
{
370+
// 0. Create a skip target to jump to, if the parameter is invalid
371+
goto_programt skip_program;
372+
const auto skip_target =
373+
skip_program.add(goto_programt::make_skip(location));
374+
369375
// 1. Create a temporary symbol expression that represents the
370376
// history variable
371377
symbol_exprt tmp_symbol =
@@ -376,14 +382,21 @@ void code_contractst::replace_history_parameter(
376382
parameter2history[parameter] = tmp_symbol;
377383

378384
// 3. Add the required instructions to the instructions list
379-
// 3.1 Declare the newly created temporary variable
385+
// 3.1. Declare the newly created temporary variable
380386
history.add(goto_programt::make_decl(tmp_symbol, location));
381387

382-
// 3.2 Add an assignment such that the value pointed to by the new
388+
// 3.2. Skip storing the history if the expression is invalid
389+
history.add(goto_programt::make_goto(
390+
skip_target, not_exprt{validate_all_derefs(parameter, ns)}, location));
391+
392+
// 3.3. Add an assignment such that the value pointed to by the new
383393
// temporary variable is equal to the value of the corresponding
384394
// parameter
385395
history.add(
386396
goto_programt::make_assignment(tmp_symbol, parameter, location));
397+
398+
// 3.4. Add a skip target
399+
history.destructive_append(skip_program);
387400
}
388401

389402
expr = parameter2history[parameter];

src/goto-instrument/havoc_utils.cpp

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ Date: July 2021
1616
#include <util/arith_tools.h>
1717
#include <util/c_types.h>
1818
#include <util/pointer_expr.h>
19+
#include <util/pointer_predicates.h>
1920

2021
void append_safe_havoc_code_for_expr(
2122
const source_locationt source_location,
23+
const namespacet &ns,
2224
const exprt &expr,
2325
goto_programt &dest,
2426
const std::function<void()> &havoc_code_impl)
@@ -27,47 +29,37 @@ void append_safe_havoc_code_for_expr(
2729
const auto skip_target =
2830
skip_program.add(goto_programt::make_skip(source_location));
2931

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-
}
32+
// skip havocing only if all pointer derefs in the expression are valid
33+
// (to avoid spurious pointer deref errors)
34+
dest.add(goto_programt::make_goto(
35+
skip_target, not_exprt{validate_all_derefs(expr, ns)}, source_location));
3936

4037
havoc_code_impl();
4138

42-
// for deref expressions, add the final skip target
43-
if(expr.id() == ID_dereference)
44-
dest.destructive_append(skip_program);
39+
// add the final skip target
40+
dest.destructive_append(skip_program);
4541
}
4642

4743
void append_object_havoc_code_for_expr(
4844
const source_locationt source_location,
4945
const exprt &expr,
5046
goto_programt &dest)
5147
{
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-
});
48+
codet havoc(ID_havoc_object);
49+
havoc.add_source_location() = source_location;
50+
havoc.add_to_operands(expr);
51+
dest.add(goto_programt::make_other(havoc, source_location));
5852
}
5953

6054
void append_scalar_havoc_code_for_expr(
6155
const source_locationt source_location,
6256
const exprt &expr,
6357
goto_programt &dest)
6458
{
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-
});
59+
side_effect_expr_nondett rhs(expr.type(), source_location);
60+
goto_programt::targett t = dest.add(
61+
goto_programt::make_assignment(expr, std::move(rhs), source_location));
62+
t->code_nonconst().add_source_location() = source_location;
7163
}
7264

7365
void append_havoc_code(
@@ -92,3 +84,29 @@ void append_havoc_code(
9284
append_scalar_havoc_code_for_expr(source_location, expr, dest);
9385
}
9486
}
87+
88+
void append_havoc_code_if_valid(
89+
const source_locationt source_location,
90+
const namespacet &ns,
91+
const modifiest &modifies,
92+
goto_programt &dest)
93+
{
94+
havoc_utils_is_constantt is_constant(modifies);
95+
for(const auto &expr : modifies)
96+
{
97+
if(expr.id() == ID_index || expr.id() == ID_dereference)
98+
{
99+
address_of_exprt address_of_expr(expr);
100+
if(!is_constant(address_of_expr))
101+
{
102+
append_safe_havoc_code_for_expr(source_location, ns, expr, dest, [&]() {
103+
append_object_havoc_code_for_expr(source_location, expr, dest);
104+
});
105+
continue;
106+
}
107+
}
108+
append_safe_havoc_code_for_expr(source_location, ns, expr, dest, [&]() {
109+
append_scalar_havoc_code_for_expr(source_location, expr, dest);
110+
});
111+
};
112+
}

src/goto-instrument/havoc_utils.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Date: July 2021
1616

1717
#include <set>
1818

19-
#include <goto-programs/goto_program.h>
19+
#include <goto-programs/goto_model.h>
2020

2121
#include <util/expr_util.h>
2222

@@ -48,6 +48,12 @@ void append_havoc_code(
4848
const modifiest &modifies,
4949
goto_programt &dest);
5050

51+
void append_havoc_code_if_valid(
52+
const source_locationt source_location,
53+
const namespacet &ns,
54+
const modifiest &modifies,
55+
goto_programt &dest);
56+
5157
void append_object_havoc_code_for_expr(
5258
const source_locationt source_location,
5359
const exprt &expr,

src/util/pointer_predicates.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,17 @@ exprt object_lower_bound(
205205

206206
return binary_relation_exprt(std::move(p_offset), ID_lt, std::move(zero));
207207
}
208+
209+
exprt validate_all_derefs(const exprt &expr, const namespacet &ns)
210+
{
211+
exprt::operandst validity_checks;
212+
213+
if(expr.id() == ID_dereference)
214+
validity_checks.push_back(
215+
good_pointer_def(to_dereference_expr(expr).pointer(), ns));
216+
217+
for(const auto &op : expr.operands())
218+
validity_checks.push_back(validate_all_derefs(op, ns));
219+
220+
return conjunction(validity_checks);
221+
}

src/util/pointer_predicates.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ exprt object_upper_bound(
5050
const exprt &pointer,
5151
const exprt &access_size);
5252

53+
exprt validate_all_derefs(const exprt &ptr, const namespacet &);
54+
5355
class is_invalid_pointer_exprt : public unary_predicate_exprt
5456
{
5557
public:

0 commit comments

Comments
 (0)