Skip to content

Commit 6a2d7c4

Browse files
committed
Simplifies assigns clause implementation
No need to track write set with temporary variables. This also avoids reporting NULL dereferences when users adds targets that might be NULL in the assigns clause. Signed-off-by: Felipe R. Monteiro <[email protected]>
1 parent d8296bd commit 6a2d7c4

File tree

7 files changed

+110
-108
lines changed

7 files changed

+110
-108
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include <assert.h>
2+
#include <stdint.h>
3+
#include <stdlib.h>
4+
5+
struct pair
6+
{
7+
uint8_t *buf;
8+
size_t size;
9+
};
10+
11+
struct pair_of_pairs
12+
{
13+
struct pair *p;
14+
};
15+
16+
void f1(struct pair *p) __CPROVER_assigns(*(p->buf))
17+
{
18+
p->buf[0] = 0;
19+
}
20+
21+
void f2(struct pair_of_pairs *pp) __CPROVER_assigns(*(pp->p->buf))
22+
{
23+
pp->p->buf[0] = 0;
24+
}
25+
26+
int main()
27+
{
28+
struct pair *p = nondet_bool() ? malloc(sizeof(*p)) : NULL;
29+
f1(p);
30+
struct pair_of_pairs *pp = malloc(sizeof(*pp));
31+
f2(pp);
32+
33+
return 0;
34+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts _ --pointer-check
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[f1.\d+\] line \d+ Check that p\-\>buf\[\(.*\)0\] is assignable\: FAILURE$
7+
^\[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.pointer\_dereference.\d+\] line \d+ dereference failure\: pointer NULL in pp\-\>p\-\>buf\: FAILURE$
10+
^VERIFICATION FAILED$
11+
--
12+
--
13+
Checks whether CBMC properly evaluates write set of members
14+
from invalid objects. In this case, they are not writable.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include <assert.h>
2+
#include <stdint.h>
3+
#include <stdlib.h>
4+
5+
struct pair
6+
{
7+
uint8_t *buf;
8+
size_t size;
9+
};
10+
11+
void f1(struct pair *p) __CPROVER_assigns(*(p->buf))
12+
__CPROVER_ensures(p->buf[0] == 0)
13+
{
14+
p->buf[0] = 0;
15+
}
16+
17+
int main()
18+
{
19+
struct pair *p = nondet_bool() ? malloc(sizeof(*p)) : NULL;
20+
f1(p);
21+
assert(p != NULL == > p->buf[0] == 0);
22+
23+
return 0;
24+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
--replace-all-calls-with-contracts _ --pointer-check
4+
^EXIT=10$
5+
^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$
9+
--
10+
--
11+
Checks whether CBMC properly evaluates write set of members
12+
from invalid objects. In this case, they are not writable.

src/goto-instrument/contracts/assigns.cpp

Lines changed: 12 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -24,49 +24,30 @@ assigns_clause_targett::assigns_clause_targett(
2424
code_contractst &contract,
2525
messaget &log_parameter,
2626
const irep_idt &function_id)
27-
: pointer_object(pointer_for(object)),
28-
contract(contract),
27+
: contract(contract),
2928
init_block(),
3029
log(log_parameter),
31-
target(typet()),
30+
target(pointer_for(object)),
3231
target_id(object.id())
3332
{
3433
INVARIANT(
35-
pointer_object.type().id() == ID_pointer,
34+
target.type().id() == ID_pointer,
3635
"Assigns clause targets should be stored as pointer expressions.");
37-
const symbolt &function_symbol = contract.ns.lookup(function_id);
38-
39-
// Declare a new symbol to stand in for the reference
40-
symbolt standin_symbol = contract.new_tmp_symbol(
41-
pointer_object.type(),
42-
function_symbol.location,
43-
function_symbol.mode);
44-
45-
target = standin_symbol.symbol_expr();
46-
47-
// Build standin variable initialization block
48-
init_block.add(goto_programt::make_decl(target, function_symbol.location));
49-
init_block.add(goto_programt::make_assignment(
50-
code_assignt(target, pointer_object), function_symbol.location));
5136
}
5237

5338
assigns_clause_targett::~assigns_clause_targett()
5439
{
5540
}
5641

57-
std::vector<symbol_exprt> assigns_clause_targett::temporary_declarations() const
58-
{
59-
std::vector<symbol_exprt> result;
60-
result.push_back(target);
61-
return result;
62-
}
63-
6442
exprt assigns_clause_targett::alias_expression(const exprt &lhs)
6543
{
6644
exprt::operandst condition;
6745
exprt lhs_ptr = (lhs.id() == ID_address_of) ? to_address_of_expr(lhs).object()
6846
: pointer_for(lhs);
6947

48+
// NULL targets aren't writable
49+
condition.push_back(not_exprt(null_pointer(target)));
50+
7051
// __CPROVER_same_object(lhs, target)
7152
condition.push_back(same_object(lhs_ptr, target));
7253

@@ -103,17 +84,12 @@ exprt assigns_clause_targett::alias_expression(const exprt &lhs)
10384
exprt assigns_clause_targett::compatible_expression(
10485
const assigns_clause_targett &called_target)
10586
{
106-
return same_object(called_target.get_direct_pointer(), target);
107-
}
108-
109-
const exprt &assigns_clause_targett::get_direct_pointer() const
110-
{
111-
return pointer_object;
87+
return same_object(called_target.get_target(), target);
11288
}
11389

114-
goto_programt &assigns_clause_targett::get_init_block()
90+
const exprt &assigns_clause_targett::get_target() const
11591
{
116-
return init_block;
92+
return target;
11793
}
11894

11995
assigns_clauset::assigns_clauset(
@@ -140,7 +116,7 @@ assigns_clauset::~assigns_clauset()
140116
}
141117
}
142118

143-
assigns_clause_targett *assigns_clauset::add_target(exprt target)
119+
void assigns_clauset::add_target(exprt target)
144120
{
145121
assigns_clause_targett *new_target = new assigns_clause_targett(
146122
(target.id() == ID_address_of)
@@ -150,42 +126,13 @@ assigns_clause_targett *assigns_clauset::add_target(exprt target)
150126
log,
151127
function_id);
152128
targets.push_back(new_target);
153-
return new_target;
154-
}
155-
156-
goto_programt assigns_clauset::init_block()
157-
{
158-
goto_programt result;
159-
for(assigns_clause_targett *target : targets)
160-
{
161-
for(goto_programt::instructiont inst :
162-
target->get_init_block().instructions)
163-
{
164-
result.add(goto_programt::instructiont(inst));
165-
}
166-
}
167-
return result;
168-
}
169-
170-
goto_programt assigns_clauset::dead_stmts()
171-
{
172-
goto_programt dead_statements;
173-
for(assigns_clause_targett *target : targets)
174-
{
175-
for(symbol_exprt symbol : target->temporary_declarations())
176-
{
177-
dead_statements.add(
178-
goto_programt::make_dead(symbol, symbol.source_location()));
179-
}
180-
}
181-
return dead_statements;
182129
}
183130

184131
goto_programt assigns_clauset::havoc_code()
185132
{
186133
modifiest modifies;
187134
for(const auto &t : targets)
188-
modifies.insert(to_address_of_expr(t->get_direct_pointer()).object());
135+
modifies.insert(to_address_of_expr(t->get_target()).object());
189136

190137
goto_programt havoc_statements;
191138
append_havoc_code(assigns.source_location(), modifies, havoc_statements);
@@ -231,7 +178,7 @@ exprt assigns_clauset::compatible_expression(
231178

232179
// Validating the called target through __CPROVER_w_ok() is
233180
// only useful when the called target is a dereference
234-
const auto &called_target_ptr = called_target->get_direct_pointer();
181+
const auto &called_target_ptr = called_target->get_target();
235182
if(
236183
to_address_of_expr(called_target_ptr).object().id() == ID_dereference)
237184
{

src/goto-instrument/contracts/assigns.h

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,23 +29,20 @@ class assigns_clause_targett
2929
const irep_idt &function_id);
3030
~assigns_clause_targett();
3131

32-
std::vector<symbol_exprt> temporary_declarations() const;
3332
exprt alias_expression(const exprt &lhs);
3433
exprt compatible_expression(const assigns_clause_targett &called_target);
35-
const exprt &get_direct_pointer() const;
36-
goto_programt &get_init_block();
34+
const exprt &get_target() const;
3735

3836
static exprt pointer_for(const exprt &object)
3937
{
4038
return address_of_exprt(object);
4139
}
4240

4341
protected:
44-
const exprt pointer_object;
4542
const code_contractst &contract;
4643
goto_programt init_block;
4744
messaget &log;
48-
symbol_exprt target;
45+
exprt target;
4946
const irep_idt &target_id;
5047
};
5148

@@ -59,9 +56,7 @@ class assigns_clauset
5956
messaget log_parameter);
6057
~assigns_clauset();
6158

62-
assigns_clause_targett *add_target(exprt target);
63-
goto_programt init_block();
64-
goto_programt dead_stmts();
59+
void add_target(exprt target);
6560
goto_programt havoc_code();
6661
exprt alias_expression(const exprt &lhs);
6762
exprt compatible_expression(const assigns_clauset &called_assigns);

src/goto-instrument/contracts/contracts.cpp

Lines changed: 11 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -750,11 +750,7 @@ void code_contractst::instrument_call_statement(
750750
typet cast_type = rhs.type();
751751

752752
// Make freshly allocated memory assignable, if we can determine its type.
753-
assigns_clause_targett *new_target =
754-
assigns_clause.add_target(dereference_exprt(rhs));
755-
goto_programt &pointer_capture = new_target->get_init_block();
756-
insert_before_swap_and_advance(
757-
program, instruction_iterator, pointer_capture);
753+
assigns_clause.add_target(dereference_exprt(rhs));
758754
}
759755
return; // assume malloc edits no pre-existing memory objects.
760756
}
@@ -895,7 +891,12 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
895891
return true;
896892
}
897893
goto_programt &program = old_function->second.body;
898-
if(program.instructions.empty()) // empty function body
894+
895+
if(
896+
program.empty() ||
897+
(program.instructions.size() <= 2 &&
898+
program.instructions.front().is_skip() &&
899+
program.instructions.back().is_end_function())) // empty function body
899900
{
900901
return false;
901902
}
@@ -923,33 +924,17 @@ void code_contractst::check_frame_conditions(
923924
// Create a list of variables that are okay to assign.
924925
std::set<irep_idt> freely_assignable_symbols;
925926

926-
// Create temporary variables to hold the assigns
927-
// clause targets before they can be modified.
928-
goto_programt standin_decls = assigns.init_block();
929-
// Create dead statements for temporary variables
930-
goto_programt mark_dead = assigns.dead_stmts();
931-
932-
// Skip lines with temporary variable declarations
933-
auto instruction_it = program.instructions.begin();
934-
insert_before_swap_and_advance(program, instruction_it, standin_decls);
935-
936-
for(; instruction_it != program.instructions.end(); ++instruction_it)
927+
for(auto instruction_it = program.instructions.begin();
928+
instruction_it != program.instructions.end();
929+
++instruction_it)
937930
{
938931
if(instruction_it->is_decl())
939932
{
940933
// Local variables are always freely assignable
941934
freely_assignable_symbols.insert(
942935
instruction_it->get_decl().symbol().get_identifier());
943936

944-
assigns_clause_targett *new_target =
945-
assigns.add_target(instruction_it->get_decl().symbol());
946-
goto_programt &pointer_capture = new_target->get_init_block();
947-
948-
for(auto in : pointer_capture.instructions)
949-
{
950-
program.insert_after(instruction_it, in);
951-
++instruction_it;
952-
}
937+
assigns.add_target(instruction_it->get_decl().symbol());
953938
}
954939
else if(instruction_it->is_assign())
955940
{
@@ -970,15 +955,6 @@ void code_contractst::check_frame_conditions(
970955
assigns);
971956
}
972957
}
973-
974-
// Walk the iterator back to the last statement
975-
while(!instruction_it->is_end_function())
976-
{
977-
--instruction_it;
978-
}
979-
980-
// Make sure the temporary symbols are marked dead
981-
program.insert_before_swap(instruction_it, mark_dead);
982958
}
983959

984960
bool code_contractst::enforce_contract(const irep_idt &function)

0 commit comments

Comments
 (0)