Skip to content

Commit 556187d

Browse files
authored
Merge pull request #6365 from padhi-aws-forks/chain-validate
A predicate to validate all pointer derefs in an expression AST
2 parents c3d7235 + fcca0e5 commit 556187d

File tree

18 files changed

+398
-205
lines changed

18 files changed

+398
-205
lines changed

regression/contracts/assigns_enforce_structs_07/main.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@ void f2(struct pair_of_pairs *pp) __CPROVER_assigns(*(pp->p->buf))
2525

2626
int main()
2727
{
28-
struct pair *p = nondet_bool() ? malloc(sizeof(*p)) : NULL;
28+
struct pair *p = malloc(sizeof(*p));
2929
f1(p);
30+
3031
struct pair_of_pairs *pp = malloc(sizeof(*pp));
32+
if(pp)
33+
pp->p = malloc(sizeof(*(pp->p)));
3134
f2(pp);
3235

3336
return 0;
Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
KNOWNBUG
1+
CORE
22
main.c
3-
--enforce-all-contracts _ --pointer-check
3+
--enforce-all-contracts _ --malloc-may-fail --malloc-fail-null --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: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,23 @@
22
#include <stdint.h>
33
#include <stdlib.h>
44

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

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

1717
int main()
1818
{
19-
struct pair *p = nondet_bool() ? malloc(sizeof(*p)) : NULL;
19+
struct test *p = malloc(sizeof(*p));
20+
uint8_t buf_1 = (p == NULL) ? 0 : p->buf[1];
2021
f1(p);
21-
// clang-format off
22-
assert(p != NULL ==> p->buf[0] == 0);
23-
// clang-format on
24-
return 0;
22+
assert(p == NULL || p->buf[0] == 0);
23+
assert(p == NULL || p->buf[1] == buf_1);
2524
}
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
CORE
22
main.c
3-
--replace-all-calls-with-contracts _ --pointer-check
3+
--replace-all-calls-with-contracts _ --malloc-may-fail --malloc-fail-null --pointer-check
44
^EXIT=10$
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$
6+
^\[main.assertion.\d+\] line \d+ assertion p == NULL \|\| p->buf\[0\] == 0: SUCCESS$
7+
^\[main.assertion.\d+\] line \d+ assertion p == NULL \|\| p->buf\[1\] == buf_1: FAILURE$
88
^VERIFICATION FAILED$
99
--
1010
--
11-
Checks whether CBMC properly evaluates write set of members
12-
from invalid objects. In this case, they are not writable.
11+
Checks whether CBMC properly evaluates write set of members from invalid objects.
12+
Functions are not expected to write to invalid locations; CBMC flags such writes.
13+
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/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ SRC = accelerate/accelerate.cpp \
1717
branch.cpp \
1818
call_sequences.cpp \
1919
contracts/assigns.cpp \
20-
contracts/memory_predicates.cpp \
2120
contracts/contracts.cpp \
21+
contracts/memory_predicates.cpp \
22+
contracts/utils.cpp \
2223
concurrency.cpp \
2324
count_eloc.cpp \
2425
cover.cpp \

src/goto-instrument/contracts/assigns.cpp

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@ Date: July 2021
1515

1616
#include <goto-instrument/havoc_utils.h>
1717

18-
#include <util/arith_tools.h>
19-
#include <util/c_types.h>
18+
#include <langapi/language_util.h>
19+
20+
#include <util/pointer_offset_size.h>
2021
#include <util/pointer_predicates.h>
2122

23+
#include "utils.h"
24+
2225
assigns_clauset::targett::targett(
2326
const assigns_clauset &parent,
2427
const exprt &expr)
@@ -43,27 +46,16 @@ exprt assigns_clauset::targett::normalize(const exprt &expr)
4346
exprt assigns_clauset::targett::generate_containment_check(
4447
const address_of_exprt &lhs_address) const
4548
{
46-
exprt::operandst address_validity;
47-
exprt::operandst containment_check;
48-
49-
// __CPROVER_w_ok(target, sizeof(target))
50-
address_validity.push_back(w_ok_exprt(
51-
address,
52-
size_of_expr(dereference_exprt(address).type(), parent.ns).value()));
49+
const auto address_validity = and_exprt{
50+
all_dereferences_are_valid(dereference_exprt{address}, parent.ns),
51+
all_dereferences_are_valid(dereference_exprt{lhs_address}, parent.ns)};
5352

54-
// __CPROVER_w_ok(lhs, sizeof(lhs))
55-
address_validity.push_back(w_ok_exprt(
56-
lhs_address,
57-
size_of_expr(dereference_exprt(lhs_address).type(), parent.ns).value()));
58-
59-
// __CPROVER_same_object(lhs, target)
53+
exprt::operandst containment_check;
6054
containment_check.push_back(same_object(lhs_address, address));
6155

6256
// If assigns target was a dereference, comparing objects is enough
63-
// and the resulting condition will be
64-
// __CPROVER_w_ok(target, sizeof(target))
65-
// && __CPROVER_w_ok(lhs, sizeof(lhs))
66-
// ==> __CPROVER_same_object(lhs, target)
57+
// and the resulting condition will be:
58+
// VALID(self) && VALID(lhs) ==> __CPROVER_same_object(lhs, self)
6759
if(id != ID_dereference)
6860
{
6961
const auto lhs_offset = pointer_offset(lhs_address);
@@ -86,19 +78,17 @@ exprt assigns_clauset::targett::generate_containment_check(
8678
own_offset);
8779

8880
// (sizeof(lhs) + __CPROVER_offset(lhs)) <=
89-
// (sizeof(target) + __CPROVER_offset(target))
81+
// (sizeof(self) + __CPROVER_offset(self))
9082
containment_check.push_back(
9183
binary_relation_exprt(lhs_region, ID_le, own_region));
9284
}
9385

94-
// __CPROVER_w_ok(target, sizeof(target))
95-
// && __CPROVER_w_ok(lhs, sizeof(lhs))
96-
// ==> __CPROVER_same_object(lhs, target)
97-
// && __CPROVER_offset(lhs) >= __CPROVER_offset(target)
98-
// && (sizeof(lhs) + __CPROVER_offset(lhs)) <=
99-
// (sizeof(target) + __CPROVER_offset(target))
100-
return binary_relation_exprt(
101-
conjunction(address_validity), ID_implies, conjunction(containment_check));
86+
// VALID(self) && VALID(lhs)
87+
// ==> __CPROVER_same_object(lhs, self)
88+
// && __CPROVER_offset(lhs) >= __CPROVER_offset(self)
89+
// && (sizeof(lhs) + __CPROVER_offset(lhs)) <=
90+
// (sizeof(self) + __CPROVER_offset(self))
91+
return or_exprt{not_exprt{address_validity}, conjunction(containment_check)};
10292
}
10393

10494
assigns_clauset::assigns_clauset(
@@ -147,12 +137,13 @@ goto_programt assigns_clauset::generate_havoc_code() const
147137
modifiest modifies;
148138
for(const auto &target : global_write_set)
149139
modifies.insert(target.address.object());
150-
151140
for(const auto &target : local_write_set)
152141
modifies.insert(target.address.object());
153142

154143
goto_programt havoc_statements;
155-
append_havoc_code(location, modifies, havoc_statements);
144+
havoc_if_validt havoc_gen(modifies, ns);
145+
havoc_gen.append_full_havoc_code(location, havoc_statements);
146+
156147
return havoc_statements;
157148
}
158149

src/goto-instrument/contracts/assigns.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@ Date: July 2021
1414
#ifndef CPROVER_GOTO_INSTRUMENT_CONTRACTS_ASSIGNS_H
1515
#define CPROVER_GOTO_INSTRUMENT_CONTRACTS_ASSIGNS_H
1616

17-
#include "contracts.h"
17+
#include <unordered_set>
1818

19-
#include <util/pointer_offset_size.h>
19+
#include <goto-programs/goto_model.h>
20+
21+
#include <util/message.h>
22+
#include <util/pointer_expr.h>
2023

2124
/// \brief A class for representing assigns clauses in code contracts
2225
class assigns_clauset

src/goto-instrument/contracts/contracts.cpp

Lines changed: 24 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -24,73 +24,21 @@ Date: February 2016
2424

2525
#include <goto-programs/remove_skip.h>
2626

27+
#include <langapi/language_util.h>
28+
2729
#include <util/c_types.h>
2830
#include <util/expr_util.h>
2931
#include <util/fresh_symbol.h>
3032
#include <util/mathematical_expr.h>
3133
#include <util/mathematical_types.h>
3234
#include <util/message.h>
3335
#include <util/pointer_offset_size.h>
36+
#include <util/pointer_predicates.h>
3437
#include <util/replace_symbol.h>
3538

3639
#include "assigns.h"
3740
#include "memory_predicates.h"
38-
39-
// Create a lexicographic less-than relation between two tuples of variables.
40-
// This is used in the implementation of multidimensional decreases clauses.
41-
static exprt create_lexicographic_less_than(
42-
const std::vector<symbol_exprt> &lhs,
43-
const std::vector<symbol_exprt> &rhs)
44-
{
45-
PRECONDITION(lhs.size() == rhs.size());
46-
47-
if(lhs.empty())
48-
{
49-
return false_exprt();
50-
}
51-
52-
// Store conjunctions of equalities.
53-
// For example, suppose that the two input vectors are <s1, s2, s3> and <l1,
54-
// l2, l3>.
55-
// Then this vector stores <s1 == l1, s1 == l1 && s2 == l2,
56-
// s1 == l1 && s2 == l2 && s3 == l3>.
57-
// In fact, the last element is unnecessary, so we do not create it.
58-
exprt::operandst equality_conjunctions(lhs.size());
59-
equality_conjunctions[0] = binary_relation_exprt(lhs[0], ID_equal, rhs[0]);
60-
for(size_t i = 1; i < equality_conjunctions.size() - 1; i++)
61-
{
62-
binary_relation_exprt component_i_equality{lhs[i], ID_equal, rhs[i]};
63-
equality_conjunctions[i] =
64-
and_exprt(equality_conjunctions[i - 1], component_i_equality);
65-
}
66-
67-
// Store inequalities between the i-th components of the input vectors
68-
// (i.e. lhs and rhs).
69-
// For example, suppose that the two input vectors are <s1, s2, s3> and <l1,
70-
// l2, l3>.
71-
// Then this vector stores <s1 < l1, s1 == l1 && s2 < l2, s1 == l1 &&
72-
// s2 == l2 && s3 < l3>.
73-
exprt::operandst lexicographic_individual_comparisons(lhs.size());
74-
lexicographic_individual_comparisons[0] =
75-
binary_relation_exprt(lhs[0], ID_lt, rhs[0]);
76-
for(size_t i = 1; i < lexicographic_individual_comparisons.size(); i++)
77-
{
78-
binary_relation_exprt component_i_less_than{lhs[i], ID_lt, rhs[i]};
79-
lexicographic_individual_comparisons[i] =
80-
and_exprt(equality_conjunctions[i - 1], component_i_less_than);
81-
}
82-
return disjunction(lexicographic_individual_comparisons);
83-
}
84-
85-
static void insert_before_swap_and_advance(
86-
goto_programt &program,
87-
goto_programt::targett &target,
88-
goto_programt &payload)
89-
{
90-
const auto offset = payload.instructions.size();
91-
program.insert_before_swap(target, payload);
92-
std::advance(target, offset);
93-
}
41+
#include "utils.h"
9442

9543
void code_contractst::check_apply_loop_contracts(
9644
goto_functionst::goto_functiont &goto_function,
@@ -203,7 +151,8 @@ void code_contractst::check_apply_loop_contracts(
203151
}
204152

205153
// havoc the variables that may be modified
206-
append_havoc_code(loop_head->source_location, modifies, havoc_code);
154+
havoc_if_validt havoc_gen(modifies, ns);
155+
havoc_gen.append_full_havoc_code(loop_head->source_location, havoc_code);
207156

208157
// Generate: assume(invariant) just after havocing
209158
// We use a block scope to create a temporary assumption,
@@ -289,7 +238,8 @@ void code_contractst::check_apply_loop_contracts(
289238
// after the loop is smaller than the value before the loop.
290239
// Here, we use the lexicographic order.
291240
code_assertt monotonic_decreasing_assertion{
292-
create_lexicographic_less_than(new_decreases_vars, old_decreases_vars)};
241+
generate_lexicographic_less_than_check(
242+
new_decreases_vars, old_decreases_vars)};
293243
monotonic_decreasing_assertion.add_source_location() =
294244
loop_head->source_location;
295245
converter.goto_convert(monotonic_decreasing_assertion, havoc_code, mode);
@@ -418,6 +368,11 @@ void code_contractst::replace_history_parameter(
418368

419369
if(it == parameter2history.end())
420370
{
371+
// 0. Create a skip target to jump to, if the parameter is invalid
372+
goto_programt skip_program;
373+
const auto skip_target =
374+
skip_program.add(goto_programt::make_skip(location));
375+
421376
// 1. Create a temporary symbol expression that represents the
422377
// history variable
423378
symbol_exprt tmp_symbol =
@@ -428,14 +383,23 @@ void code_contractst::replace_history_parameter(
428383
parameter2history[parameter] = tmp_symbol;
429384

430385
// 3. Add the required instructions to the instructions list
431-
// 3.1 Declare the newly created temporary variable
386+
// 3.1. Declare the newly created temporary variable
432387
history.add(goto_programt::make_decl(tmp_symbol, location));
433388

434-
// 3.2 Add an assignment such that the value pointed to by the new
389+
// 3.2. Skip storing the history if the expression is invalid
390+
history.add(goto_programt::make_goto(
391+
skip_target,
392+
not_exprt{all_dereferences_are_valid(parameter, ns)},
393+
location));
394+
395+
// 3.3. Add an assignment such that the value pointed to by the new
435396
// temporary variable is equal to the value of the corresponding
436397
// parameter
437398
history.add(
438399
goto_programt::make_assignment(tmp_symbol, parameter, location));
400+
401+
// 3.4. Add a skip target
402+
history.destructive_append(skip_program);
439403
}
440404

441405
expr = parameter2history[parameter];

src/goto-instrument/contracts/contracts.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ Date: February 2016
2525
#include <goto-programs/goto_functions.h>
2626
#include <goto-programs/goto_model.h>
2727

28-
#include <langapi/language_util.h>
29-
3028
#include <util/message.h>
3129
#include <util/namespace.h>
3230
#include <util/pointer_expr.h>

0 commit comments

Comments
 (0)