Skip to content

Commit 5f9ce75

Browse files
author
klaas
committed
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 ed618d0 commit 5f9ce75

14 files changed

+464
-73
lines changed

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Author: Daniel Kroening, [email protected]
1818
#include <util/c_types.h>
1919
#include <util/config.h>
2020
#include <util/cprover_prefix.h>
21+
#include <util/expr_util.h>
2122
#include <util/ieee_float.h>
2223
#include <util/pointer_offset_size.h>
2324
#include <util/pointer_predicates.h>
@@ -32,6 +33,8 @@ Author: Daniel Kroening, [email protected]
3233
#include "anonymous_member.h"
3334
#include "padding.h"
3435

36+
bool is_lvalue(const exprt &expr);
37+
3538
void c_typecheck_baset::typecheck_expr(exprt &expr)
3639
{
3740
if(expr.id()==ID_already_typechecked)
@@ -2085,6 +2088,47 @@ exprt c_typecheck_baset::do_special_functions(
20852088

20862089
return same_object_expr;
20872090
}
2091+
else if(identifier == CPROVER_PREFIX "points_to_valid_memory")
2092+
{
2093+
if(expr.arguments().size()!=2)
2094+
{
2095+
err_location(f_op);
2096+
error() << "points_to_valid_memory expects two operands" << eom;
2097+
throw 0;
2098+
}
2099+
if(!is_lvalue(expr.arguments().front()))
2100+
{
2101+
err_location(f_op);
2102+
error() << "ptr argument to points_to_valid_memory"
2103+
<< " must be an lvalue" << eom;
2104+
throw 0;
2105+
}
2106+
2107+
exprt same_object_expr;
2108+
if(expr.arguments().size() == 2)
2109+
{
2110+
// TODO: We should add some way to enforce that the second argument
2111+
// has a reasonable type (coercible to __CPROVER_size_t).
2112+
#if 0
2113+
if(expr.arguments()[1].type() <[cannot be coerced to size_t]>)
2114+
{
2115+
err_location(f_op);
2116+
error() << "size argument to points_to_valid_memory"
2117+
<< " must be coercible to size_t" << eom;
2118+
throw 0;
2119+
}
2120+
#endif
2121+
same_object_expr =
2122+
points_to_valid_memory(expr.arguments()[0], expr.arguments()[1]);
2123+
}
2124+
else
2125+
{
2126+
UNREACHABLE;
2127+
}
2128+
same_object_expr.add_source_location()=source_location;
2129+
2130+
return same_object_expr;
2131+
}
20882132
else if(identifier==CPROVER_PREFIX "buffer_size")
20892133
{
20902134
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
@@ -3521,6 +3521,9 @@ std::string expr2ct::convert_with_precedence(
35213521
else if(src.id()==ID_good_pointer)
35223522
return convert_function(src, "GOOD_POINTER", precedence=16);
35233523

3524+
else if(src.id()==ID_points_to_valid_memory)
3525+
return convert_function(src, "POINTS_TO_VALID_MEMORY", precedence=16);
3526+
35243527
else if(src.id()==ID_object_size)
35253528
return convert_function(src, "OBJECT_SIZE", precedence=16);
35263529

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: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
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_programt &program,
56+
goto_programt::targett target,
57+
exprt &assume_expr);
58+
59+
void expand_assertion(exprt &assert_expr);
60+
61+
const symbolt &new_tmp_symbol(
62+
const typet &type,
63+
const source_locationt &source_location);
64+
};
65+
66+
bool is_lvalue(const exprt &expr);
67+
68+
void expand_pointer_predicatest::expand_pointer_predicates()
69+
{
70+
Forall_goto_functions(f_it, goto_functions)
71+
{
72+
goto_functionst::goto_functiont &goto_function = f_it->second;
73+
local_bitvector_analysist local_bitvector_analysis_obj(goto_function);
74+
local_bitvector_analysis = &local_bitvector_analysis_obj;
75+
Forall_goto_program_instructions(i_it, goto_function.body)
76+
{
77+
if(i_it->is_assert())
78+
{
79+
expand_assertion(i_it->guard);
80+
}
81+
else if(i_it->is_assume())
82+
{
83+
expand_assumption(
84+
goto_function.body,
85+
i_it,
86+
i_it->guard);
87+
local_bitvector_analysis_obj =
88+
local_bitvector_analysist(goto_function);
89+
local_bitvector_analysis = &local_bitvector_analysis_obj;
90+
}
91+
else
92+
{
93+
continue;
94+
}
95+
}
96+
}
97+
}
98+
99+
void expand_pointer_predicatest::expand_assumption(
100+
goto_programt &program,
101+
goto_programt::targett target,
102+
exprt &assume_expr)
103+
{
104+
goto_programt assume_code;
105+
for(depth_iteratort it=assume_expr.depth_begin();
106+
it != assume_expr.depth_end();)
107+
{
108+
if(it->id() == ID_points_to_valid_memory)
109+
{
110+
exprt &valid_memory_expr = it.mutate();
111+
exprt &pointer_expr = valid_memory_expr.op0();
112+
exprt size_expr = valid_memory_expr.op1();
113+
simplify(size_expr, ns);
114+
115+
// This should be forced by typechecking.
116+
INVARIANT(pointer_expr.type().id() == ID_pointer &&
117+
is_lvalue(pointer_expr),
118+
"Invalid argument to valid_pointer.");
119+
120+
local_bitvector_analysist::flagst flags =
121+
local_bitvector_analysis->get(target, pointer_expr);
122+
// Only create a new object if the pointer may be uninitialized.
123+
if(flags.is_uninitialized() || flags.is_unknown())
124+
{
125+
typet &base_type = pointer_expr.type().subtype();
126+
127+
typet object_type = type_from_size(size_expr, ns);
128+
129+
// Decl a new variable (which is therefore unconstrained)
130+
goto_programt::targett d = assume_code.add_instruction(DECL);
131+
d->function = target->function;
132+
d->source_location = assume_expr.source_location();
133+
const symbol_exprt obj =
134+
new_tmp_symbol(object_type, d->source_location).symbol_expr();
135+
d->code=code_declt(obj);
136+
137+
exprt rhs;
138+
if(object_type.id() == ID_array)
139+
{
140+
rhs = typecast_exprt(
141+
address_of_exprt(
142+
index_exprt(obj, from_integer(0, index_type()))),
143+
pointer_expr.type());
144+
}
145+
else
146+
{
147+
rhs = address_of_exprt(obj);
148+
}
149+
150+
// Point the relevant pointer to the new object
151+
goto_programt::targett a = assume_code.add_instruction(ASSIGN);
152+
a->function = target->function;
153+
a->source_location = assume_expr.source_location();
154+
a->code = code_assignt(pointer_expr, rhs);
155+
a->code.add_source_location() = assume_expr.source_location();
156+
}
157+
158+
// Because some uses of this occur before deallocated, dead, and malloc
159+
// objects are initialized, we need to make some additional assumptions
160+
// to clarify that our newly allocated object is not dead, deallocated,
161+
// or outside the bounds of a malloc region.
162+
163+
exprt check_expr =
164+
points_to_valid_memory_def(pointer_expr, size_expr, ns);
165+
valid_memory_expr.swap(check_expr);
166+
it.next_sibling_or_parent();
167+
}
168+
else {
169+
++it;
170+
}
171+
}
172+
173+
program.destructive_insert(target, assume_code);
174+
}
175+
176+
void expand_pointer_predicatest::expand_assertion(exprt &assert_expr)
177+
{
178+
for(depth_iteratort it = assert_expr.depth_begin();
179+
it != assert_expr.depth_end();)
180+
{
181+
if(it->id() == ID_points_to_valid_memory)
182+
{
183+
// Build an expression that checks validity.
184+
exprt &valid_memory_expr = it.mutate();
185+
exprt &pointer_expr = valid_memory_expr.op0();
186+
exprt &size_expr = valid_memory_expr.op1();
187+
188+
exprt check_expr =
189+
points_to_valid_memory_def(pointer_expr, size_expr, ns);
190+
valid_memory_expr.swap(check_expr);
191+
it.next_sibling_or_parent();
192+
}
193+
else
194+
{
195+
++it;
196+
}
197+
}
198+
}
199+
200+
const symbolt &expand_pointer_predicatest::new_tmp_symbol(
201+
const typet &type,
202+
const source_locationt &source_location)
203+
{
204+
return get_fresh_aux_symbol(
205+
type,
206+
id2string(source_location.get_function()),
207+
"tmp_epp",
208+
source_location,
209+
ID_C,
210+
symbol_table);
211+
}
212+
213+
void expand_pointer_predicatest::operator()()
214+
{
215+
expand_pointer_predicates();
216+
}
217+
218+
void expand_pointer_predicates(goto_modelt &goto_model)
219+
{
220+
expand_pointer_predicatest(goto_model.symbol_table,
221+
goto_model.goto_functions)();
222+
}
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
@@ -76,6 +76,7 @@ Author: Daniel Kroening, [email protected]
7676
#include "document_properties.h"
7777
#include "dot.h"
7878
#include "dump_c.h"
79+
#include "expand_pointer_predicates.h"
7980
#include "full_slicer.h"
8081
#include "function.h"
8182
#include "havoc_loops.h"
@@ -1053,6 +1054,11 @@ void goto_instrument_parse_optionst::instrument_goto_program()
10531054
code_contracts(goto_model);
10541055
}
10551056

1057+
if(cmdline.isset("expand-pointer-predicates"))
1058+
{
1059+
expand_pointer_predicates(goto_model);
1060+
}
1061+
10561062
// replace function pointers, if explicitly requested
10571063
if(cmdline.isset("remove-function-pointers"))
10581064
{
@@ -1622,6 +1628,7 @@ void goto_instrument_parse_optionst::help()
16221628
" --no-caching disable caching of intermediate results during transitive function inlining\n" // NOLINT(*)
16231629
" --log <file> log in json format which code segments were inlined, use with --function-inline\n" // NOLINT(*)
16241630
" --remove-function-pointers replace function pointers by case statement over function calls\n" // NOLINT(*)
1631+
" --expand-pointer-predicates Expands predicates about pointers (e.g. __CPROVER_points_to_valid_memory) into a form useable by CBMC\n" // NOLINT(*)
16251632
HELP_REMOVE_CALLS_NO_BODY
16261633
HELP_REMOVE_CONST_FUNCTION_POINTERS
16271634
" --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
@@ -88,6 +88,7 @@ Author: Daniel Kroening, [email protected]
8888
"(list-symbols)(list-undefined-functions)" \
8989
"(z3)(add-library)(show-dependence-graph)" \
9090
"(horn)(skip-loops):(apply-code-contracts)(model-argc-argv):" \
91+
"(expand-pointer-predicates)" \
9192
"(show-threaded)(list-calls-args)" \
9293
"(undefined-function-is-assume-false)" \
9394
"(remove-function-body):"\

0 commit comments

Comments
 (0)