Skip to content

Commit 1bba336

Browse files
klaaskarkhaz
authored andcommitted
Adds --expand-pointer-predicates to goto-instrument
The --expand-pointer-predicates pass resolves pointer predicates (util/pointer_predicates.{cpp, h}) which have side effects and so require expanding into multiple instructions. Currently, the only such predicate that needs to be expanded is __CPROVER_points_to_valid_memory (name subject to change). The __CPROVER_points_to_valid_memory predicates takes two parameters, a pointer and a size. Semantically, __CPROVER_points_to_valid_memory(p, size) should be true if and only if p points to a memory object which is valid to read/write for at least size bytes past p. When used in assertions, this will be checked in a similar manner to how --pointer-check checks validity of a dereference. When used in assumptions, this predicate creates (if none exists already) an object for p to refer to, using local_bitvector_analysis to make that determination, and then makes assumptions (again corresponding to the assertions made by --pointer-check) about that object.
1 parent c07a4f1 commit 1bba336

14 files changed

+439
-74
lines changed

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ Author: Daniel Kroening, [email protected]
3434
#include "anonymous_member.h"
3535
#include "padding.h"
3636

37+
bool is_lvalue(const exprt &expr);
38+
3739
void c_typecheck_baset::typecheck_expr(exprt &expr)
3840
{
3941
if(expr.id()==ID_already_typechecked)
@@ -2109,6 +2111,45 @@ exprt c_typecheck_baset::do_special_functions(
21092111

21102112
return same_object_expr;
21112113
}
2114+
else if(identifier == CPROVER_PREFIX "points_to_valid_memory")
2115+
{
2116+
if(expr.arguments().size() != 2)
2117+
{
2118+
error().source_location = f_op.source_location();
2119+
error() << "points_to_valid_memory expects two operands" << eom;
2120+
throw 0;
2121+
}
2122+
if(!is_lvalue(expr.arguments().front()))
2123+
{
2124+
error().source_location = f_op.source_location();
2125+
error() << "ptr argument to points_to_valid_memory"
2126+
<< " must be an lvalue" << eom;
2127+
throw 0;
2128+
}
2129+
2130+
exprt same_object_expr;
2131+
if(expr.arguments().size() == 2)
2132+
{
2133+
if(
2134+
expr.arguments()[1].type() != ID_unsignedbv &&
2135+
expr.arguments()[1].type() != ID_signedbv)
2136+
{
2137+
err_location(f_op);
2138+
error() << "size argument to points_to_valid_memory"
2139+
<< " must be coercible to size_t" << eom;
2140+
throw 0;
2141+
}
2142+
same_object_expr =
2143+
points_to_valid_memory(expr.arguments()[0], expr.arguments()[1]);
2144+
}
2145+
else
2146+
{
2147+
UNREACHABLE;
2148+
}
2149+
same_object_expr.add_source_location() = source_location;
2150+
2151+
return same_object_expr;
2152+
}
21122153
else if(identifier==CPROVER_PREFIX "buffer_size")
21132154
{
21142155
if(expr.arguments().size()!=1)

src/ansi-c/cprover_builtin_headers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ void __CPROVER_havoc_object(void *);
66
__CPROVER_bool __CPROVER_equal();
77
__CPROVER_bool __CPROVER_same_object(const void *, const void *);
88
__CPROVER_bool __CPROVER_invalid_pointer(const void *);
9+
__CPROVER_bool __CPROVER_points_to_valid_memory(const void *, __CPROVER_size_t);
910
__CPROVER_bool __CPROVER_is_zero_string(const void *);
1011
__CPROVER_size_t __CPROVER_zero_string_length(const void *);
1112
__CPROVER_size_t __CPROVER_buffer_size(const void *);

src/ansi-c/expr2c.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3432,6 +3432,9 @@ std::string expr2ct::convert_with_precedence(
34323432
else if(src.id()==ID_good_pointer)
34333433
return convert_function(src, "GOOD_POINTER", precedence=16);
34343434

3435+
else if(src.id() == ID_points_to_valid_memory)
3436+
return convert_function(src, "POINTS_TO_VALID_MEMORY", precedence = 16);
3437+
34353438
else if(src.id()==ID_object_size)
34363439
return convert_function(src, "OBJECT_SIZE", precedence=16);
34373440

src/goto-instrument/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ SRC = accelerate/accelerate.cpp \
3232
document_properties.cpp \
3333
dot.cpp \
3434
dump_c.cpp \
35+
expand_pointer_predicates.cpp \
3536
full_slicer.cpp \
3637
function.cpp \
3738
function_modifies.cpp \
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
/*******************************************************************\
2+
3+
Module: Expand pointer predicates in assertions/assumptions.
4+
5+
Author: Klaas Pruiksma
6+
7+
Date: June 2018
8+
9+
\*******************************************************************/
10+
11+
/// \file
12+
/// Replace pointer predicates (e.g. __CPROVER_points_to_valid_memory)
13+
/// in assumptions and assertions with suitable expressions and additional
14+
/// instructions.
15+
16+
#include "expand_pointer_predicates.h"
17+
18+
#include <analyses/local_bitvector_analysis.h>
19+
20+
#include <util/arith_tools.h>
21+
#include <util/c_types.h>
22+
#include <util/expr_iterator.h>
23+
#include <util/expr_util.h>
24+
#include <util/fresh_symbol.h>
25+
#include <util/namespace.h>
26+
#include <util/pointer_predicates.h>
27+
#include <util/simplify_expr.h>
28+
29+
#include <goto-programs/goto_model.h>
30+
31+
class expand_pointer_predicatest
32+
{
33+
public:
34+
expand_pointer_predicatest(
35+
symbol_tablet &_symbol_table,
36+
goto_functionst &_goto_functions)
37+
: ns(_symbol_table),
38+
symbol_table(_symbol_table),
39+
goto_functions(_goto_functions),
40+
local_bitvector_analysis(nullptr)
41+
{
42+
}
43+
44+
void operator()();
45+
46+
protected:
47+
namespacet ns;
48+
symbol_tablet &symbol_table;
49+
goto_functionst &goto_functions;
50+
local_bitvector_analysist *local_bitvector_analysis;
51+
52+
void expand_pointer_predicates();
53+
54+
void expand_assumption(
55+
goto_functionst::goto_functiont &,
56+
goto_programt::targett target,
57+
exprt &assume_expr);
58+
59+
void expand_assertion(exprt &assert_expr);
60+
61+
const symbolt &
62+
new_tmp_symbol(const typet &type, const source_locationt &source_location);
63+
};
64+
65+
bool is_lvalue(const exprt &expr);
66+
67+
void expand_pointer_predicatest::expand_pointer_predicates()
68+
{
69+
Forall_goto_functions(f_it, goto_functions)
70+
{
71+
goto_functionst::goto_functiont &goto_function = f_it->second;
72+
Forall_goto_program_instructions(i_it, goto_function.body)
73+
{
74+
if(i_it->is_assert())
75+
{
76+
expand_assertion(i_it->guard);
77+
}
78+
else if(i_it->is_assume())
79+
{
80+
expand_assumption(goto_function, i_it, i_it->guard);
81+
}
82+
else
83+
{
84+
continue;
85+
}
86+
}
87+
}
88+
}
89+
90+
void expand_pointer_predicatest::expand_assumption(
91+
goto_functionst::goto_functiont &goto_function,
92+
goto_programt::targett target,
93+
exprt &assume_expr)
94+
{
95+
goto_programt &program = goto_function.body;
96+
local_bitvector_analysist local_bitvector_analysis(goto_function, ns);
97+
goto_programt assume_code;
98+
for(depth_iteratort it = assume_expr.depth_begin();
99+
it != assume_expr.depth_end();)
100+
{
101+
if(it->id() == ID_points_to_valid_memory)
102+
{
103+
exprt &valid_memory_expr = it.mutate();
104+
exprt &pointer_expr = valid_memory_expr.op0();
105+
exprt size_expr = valid_memory_expr.op1();
106+
simplify(size_expr, ns);
107+
108+
// This should be forced by typechecking.
109+
INVARIANT(
110+
pointer_expr.type().id() == ID_pointer && is_lvalue(pointer_expr),
111+
"Invalid argument to valid_pointer.");
112+
113+
local_bitvector_analysist::flagst flags =
114+
local_bitvector_analysis.get(target, pointer_expr);
115+
// Only create a new object if the pointer may be uninitialized.
116+
if(flags.is_uninitialized() || flags.is_unknown())
117+
{
118+
typet object_type = type_from_size(size_expr, ns);
119+
120+
// Decl a new variable (which is therefore unconstrained)
121+
goto_programt::targett d = assume_code.add_instruction(DECL);
122+
d->source_location = assume_expr.source_location();
123+
const symbol_exprt obj =
124+
new_tmp_symbol(object_type, d->source_location).symbol_expr();
125+
d->code = code_declt(obj);
126+
127+
exprt rhs;
128+
if(object_type.id() == ID_array)
129+
{
130+
rhs = typecast_exprt(
131+
address_of_exprt(index_exprt(obj, from_integer(0, index_type()))),
132+
pointer_expr.type());
133+
}
134+
else
135+
{
136+
rhs = address_of_exprt(obj);
137+
}
138+
139+
// Point the relevant pointer to the new object
140+
goto_programt::targett a = assume_code.add_instruction(ASSIGN);
141+
a->source_location = assume_expr.source_location();
142+
a->code = code_assignt(pointer_expr, rhs);
143+
a->code.add_source_location() = assume_expr.source_location();
144+
}
145+
146+
// Because some uses of this occur before deallocated, dead, and malloc
147+
// objects are initialized, we need to make some additional assumptions
148+
// to clarify that our newly allocated object is not dead, deallocated,
149+
// or outside the bounds of a malloc region.
150+
151+
exprt check_expr =
152+
points_to_valid_memory_def(pointer_expr, size_expr, ns);
153+
valid_memory_expr.swap(check_expr);
154+
it.next_sibling_or_parent();
155+
}
156+
else
157+
{
158+
++it;
159+
}
160+
}
161+
162+
program.destructive_insert(target, assume_code);
163+
}
164+
165+
void expand_pointer_predicatest::expand_assertion(exprt &assert_expr)
166+
{
167+
for(depth_iteratort it = assert_expr.depth_begin();
168+
it != assert_expr.depth_end();)
169+
{
170+
if(it->id() == ID_points_to_valid_memory)
171+
{
172+
// Build an expression that checks validity.
173+
exprt &valid_memory_expr = it.mutate();
174+
exprt &pointer_expr = valid_memory_expr.op0();
175+
exprt &size_expr = valid_memory_expr.op1();
176+
177+
exprt check_expr =
178+
points_to_valid_memory_def(pointer_expr, size_expr, ns);
179+
valid_memory_expr.swap(check_expr);
180+
it.next_sibling_or_parent();
181+
}
182+
else
183+
{
184+
++it;
185+
}
186+
}
187+
}
188+
189+
const symbolt &expand_pointer_predicatest::new_tmp_symbol(
190+
const typet &type,
191+
const source_locationt &source_location)
192+
{
193+
return get_fresh_aux_symbol(
194+
type,
195+
id2string(source_location.get_function()),
196+
"tmp_epp",
197+
source_location,
198+
ID_C,
199+
symbol_table);
200+
}
201+
202+
void expand_pointer_predicatest::operator()()
203+
{
204+
expand_pointer_predicates();
205+
}
206+
207+
void expand_pointer_predicates(goto_modelt &goto_model)
208+
{
209+
expand_pointer_predicatest(
210+
goto_model.symbol_table, goto_model.goto_functions)();
211+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*******************************************************************\
2+
3+
Module: Expand pointer predicates in assertions/assumptions.
4+
5+
Author: Klaas Pruiksma
6+
7+
Date: June 2018
8+
9+
\*******************************************************************/
10+
11+
/// \file
12+
/// Replace pointer predicates (e.g. __CPROVER_points_to_valid_memory)
13+
/// in assumptions and assertions with suitable expressions and additional
14+
/// instructions.
15+
16+
#ifndef CPROVER_GOTO_INSTRUMENT_EXPAND_POINTER_PREDICATES_H
17+
#define CPROVER_GOTO_INSTRUMENT_EXPAND_POINTER_PREDICATES_H
18+
19+
class goto_modelt;
20+
21+
void expand_pointer_predicates(goto_modelt &goto_model);
22+
23+
#endif // CPROVER_GOTO_INSTRUMENT_EXPAND_POINTER_PREDICATES_H

src/goto-instrument/goto_instrument_parse_options.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ Author: Daniel Kroening, [email protected]
8282
#include "document_properties.h"
8383
#include "dot.h"
8484
#include "dump_c.h"
85+
#include "expand_pointer_predicates.h"
8586
#include "full_slicer.h"
8687
#include "function.h"
8788
#include "havoc_loops.h"
@@ -1068,6 +1069,11 @@ void goto_instrument_parse_optionst::instrument_goto_program()
10681069
code_contracts(goto_model);
10691070
}
10701071

1072+
if(cmdline.isset("expand-pointer-predicates"))
1073+
{
1074+
expand_pointer_predicates(goto_model);
1075+
}
1076+
10711077
// replace function pointers, if explicitly requested
10721078
if(cmdline.isset("remove-function-pointers"))
10731079
{
@@ -1665,6 +1671,7 @@ void goto_instrument_parse_optionst::help()
16651671
" --no-caching disable caching of intermediate results during transitive function inlining\n" // NOLINT(*)
16661672
" --log <file> log in json format which code segments were inlined, use with --function-inline\n" // NOLINT(*)
16671673
" --remove-function-pointers replace function pointers by case statement over function calls\n" // NOLINT(*)
1674+
" --expand-pointer-predicates Expands predicates about pointers (e.g. __CPROVER_points_to_valid_memory) into a form useable by CBMC\n" // NOLINT(*)
16681675
HELP_REMOVE_CALLS_NO_BODY
16691676
HELP_REMOVE_CONST_FUNCTION_POINTERS
16701677
" --add-library add models of C library functions\n"

src/goto-instrument/goto_instrument_parse_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ Author: Daniel Kroening, [email protected]
9393
"(list-symbols)(list-undefined-functions)" \
9494
"(z3)(add-library)(show-dependence-graph)" \
9595
"(horn)(skip-loops):(apply-code-contracts)(model-argc-argv):" \
96+
"(expand-pointer-predicates)" \
9697
"(show-threaded)(list-calls-args)" \
9798
"(undefined-function-is-assume-false)" \
9899
"(remove-function-body):"\

0 commit comments

Comments
 (0)