@@ -18,11 +18,29 @@ Date: July 2021
18
18
19
19
#include < langapi/language_util.h>
20
20
21
+ #include < goto-programs/goto_convert_class.h>
21
22
#include < util/arith_tools.h>
22
23
#include < util/c_types.h>
23
24
#include < util/pointer_offset_size.h>
24
25
#include < util/pointer_predicates.h>
25
26
27
+ // / Allows to clean expressions of side effects.
28
+ class cleanert : public goto_convertt
29
+ {
30
+ public:
31
+ cleanert (
32
+ symbol_table_baset &_symbol_table,
33
+ message_handlert &_message_handler)
34
+ : goto_convertt(_symbol_table, _message_handler)
35
+ {
36
+ }
37
+
38
+ void clean (exprt &guard, goto_programt &dest, const irep_idt &mode)
39
+ {
40
+ goto_convertt::clean_expr (guard, dest, mode, true );
41
+ }
42
+ };
43
+
26
44
static const slicet normalize_to_slice (const exprt &expr, const namespacet &ns)
27
45
{
28
46
// FIXME: Refactor these checks out to a common function that can be
@@ -52,6 +70,98 @@ static const slicet normalize_to_slice(const exprt &expr, const namespacet &ns)
52
70
UNREACHABLE;
53
71
}
54
72
73
+ // / Result type for guarded assigns target pattern matching
74
+ using if_match_resultt = optionalt<std::pair<exprt, exprt>>;
75
+
76
+ // / Matches an expression with ID_NULL or ID_constant and value "0",
77
+ // / modulo some intermediate ID_typecasts
78
+ bool match_NULL_or_zero (const exprt &expr)
79
+ {
80
+ if (expr.id () == ID_typecast)
81
+ return match_NULL_or_zero (expr.operands ().front ());
82
+ else
83
+ return expr.id () == ID_NULL ||
84
+ (expr.id () == ID_constant &&
85
+ id2string (to_constant_expr (expr).get_value ()) == " 0" );
86
+ }
87
+
88
+ // / Matches an expression of the form `guard ? target : (NULL|0)`
89
+ // / modulo intermediary typecasts.
90
+ // / \param expr the candidate expression
91
+ // / \returns A pair `(guard,target)` if successful.
92
+ if_match_resultt match_if (const exprt &expr)
93
+ {
94
+ if (expr.id () == ID_typecast)
95
+ {
96
+ return match_if (expr.operands ().front ());
97
+ }
98
+ else if (expr.id () == ID_if)
99
+ {
100
+ if (!match_NULL_or_zero (expr.operands ().at (2 )))
101
+ return {};
102
+
103
+ return {{expr.operands ().at (0 ), expr.operands ().at (1 )}};
104
+ }
105
+ else
106
+ return {};
107
+ }
108
+
109
+ // / Pattern match the given expression for a guarded pattern
110
+ // / and builds guarded_slice expression as follows:
111
+ // /
112
+ // / ```
113
+ // / case expr of
114
+ // / | guard ? target : NULL ->
115
+ // / {guard,
116
+ // / target,
117
+ // / address_of{target},
118
+ // / size_of_expr{target}}
119
+ // / | __CPROVER_POINTER_OBJECT(guard ? target : NULL) ->
120
+ // / {guard,
121
+ // / __CPROVER_POINTER_OBJECT(target),
122
+ // / address_of{target-offset(target)},
123
+ // / object_size{target}}
124
+ // / | other ->
125
+ // / {true,
126
+ // / expr,
127
+ // / address_of{expr},
128
+ // / object_size{expr}}
129
+ // / ```
130
+ static const guarded_slicet
131
+ normalize_to_guarded_slice (const exprt &expr, const namespacet &ns)
132
+ {
133
+ if (expr.id () == ID_pointer_object)
134
+ {
135
+ if_match_resultt match_opt = match_if (expr.operands ().at (0 ));
136
+ if (match_opt.has_value ())
137
+ {
138
+ const auto &match = match_opt.value ();
139
+ const auto &new_expr = pointer_object (match.second );
140
+ const auto &slice = normalize_to_slice (new_expr, ns);
141
+ return {match.first , new_expr, slice.first , slice.second };
142
+ }
143
+ else
144
+ {
145
+ const auto &slice = normalize_to_slice (expr, ns);
146
+ goto_programt empty_program;
147
+ return {true_exprt{}, expr, slice.first , slice.second };
148
+ }
149
+ }
150
+
151
+ if_match_resultt match_opt = match_if (expr);
152
+ if (match_opt.has_value ())
153
+ {
154
+ const auto &match = match_opt.value ();
155
+ const auto &slice = normalize_to_slice (match.second , ns);
156
+ return {match.first , match.second , slice.first , slice.second };
157
+ }
158
+ else
159
+ {
160
+ const auto &slice = normalize_to_slice (expr, ns);
161
+ return {true_exprt{}, expr, slice.first , slice.second };
162
+ }
163
+ }
164
+
55
165
const symbolt assigns_clauset::conditional_address_ranget::generate_new_symbol (
56
166
const std::string &prefix,
57
167
const typet &type,
@@ -71,15 +181,19 @@ assigns_clauset::conditional_address_ranget::conditional_address_ranget(
71
181
: source_expr(expr),
72
182
location(expr.source_location()),
73
183
parent(parent),
74
- slice(normalize_to_slice (expr, parent.ns)),
184
+ guarded_slice(normalize_to_guarded_slice (expr, parent.ns)),
75
185
validity_condition_var(
76
186
generate_new_symbol (" __car_valid" , bool_typet(), location).symbol_expr()),
77
- lower_bound_address_var(
78
- generate_new_symbol (" __car_lb" , slice.first.type(), location)
79
- .symbol_expr()),
80
- upper_bound_address_var(
81
- generate_new_symbol (" __car_ub" , slice.first.type(), location)
82
- .symbol_expr())
187
+ lower_bound_address_var(generate_new_symbol(
188
+ " __car_lb" ,
189
+ guarded_slice.start_adress.type(),
190
+ location)
191
+ .symbol_expr()),
192
+ upper_bound_address_var(generate_new_symbol(
193
+ " __car_ub" ,
194
+ guarded_slice.start_adress.type(),
195
+ location)
196
+ .symbol_expr())
83
197
{
84
198
}
85
199
@@ -93,6 +207,17 @@ assigns_clauset::conditional_address_ranget::generate_snapshot_instructions()
93
207
source_locationt location_no_checks = location;
94
208
disable_pointer_checks (location_no_checks);
95
209
210
+ null_message_handlert null_handler;
211
+
212
+ // clean up side effects from the guard expression
213
+ // we want checks on the guard evaluation instructions because it is user code
214
+ cleanert cleaner (parent.symbol_table , null_handler);
215
+ exprt clean_guard (guarded_slice.guard );
216
+ const auto &mode = parent.symbol_table .lookup_ref (parent.function_name ).mode ;
217
+ cleaner.clean (clean_guard, instructions, mode);
218
+ for (auto &inst : instructions.instructions )
219
+ inst.source_location_nonconst () = location;
220
+
96
221
instructions.add (
97
222
goto_programt::make_decl (validity_condition_var, location_no_checks));
98
223
instructions.add (
@@ -102,28 +227,30 @@ assigns_clauset::conditional_address_ranget::generate_snapshot_instructions()
102
227
103
228
instructions.add (goto_programt::make_assignment (
104
229
lower_bound_address_var,
105
- null_pointer_exprt{to_pointer_type (slice. first .type ())},
230
+ null_pointer_exprt{to_pointer_type (guarded_slice. start_adress .type ())},
106
231
location_no_checks));
107
232
instructions.add (goto_programt::make_assignment (
108
233
upper_bound_address_var,
109
- null_pointer_exprt{to_pointer_type (slice. first .type ())},
234
+ null_pointer_exprt{to_pointer_type (guarded_slice. start_adress .type ())},
110
235
location_no_checks));
111
236
112
237
goto_programt skip_program;
113
238
const auto skip_target =
114
239
skip_program.add (goto_programt::make_skip (location_no_checks));
115
240
116
241
const auto validity_check_expr =
117
- and_exprt{all_dereferences_are_valid (source_expr, parent.ns ),
118
- w_ok_exprt{slice.first , slice.second }};
242
+ and_exprt{clean_guard,
243
+ all_dereferences_are_valid (guarded_slice.expr , parent.ns ),
244
+ w_ok_exprt{guarded_slice.start_adress , guarded_slice.size }};
245
+
119
246
instructions.add (goto_programt::make_assignment (
120
247
validity_condition_var, validity_check_expr, location_no_checks));
121
248
122
249
instructions.add (goto_programt::make_goto (
123
250
skip_target, not_exprt{validity_condition_var}, location_no_checks));
124
251
125
252
instructions.add (goto_programt::make_assignment (
126
- lower_bound_address_var, slice. first , location_no_checks));
253
+ lower_bound_address_var, guarded_slice. start_adress , location_no_checks));
127
254
128
255
// the computation of the CAR upper bound can overflow into the object ID bits
129
256
// of the pointer with very large allocation sizes.
@@ -133,7 +260,7 @@ assigns_clauset::conditional_address_ranget::generate_snapshot_instructions()
133
260
134
261
instructions.add (goto_programt::make_assignment (
135
262
upper_bound_address_var,
136
- plus_exprt{slice. first , slice. second },
263
+ plus_exprt{guarded_slice. start_adress , guarded_slice. size },
137
264
location_overflow_check));
138
265
instructions.destructive_append (skip_program);
139
266
0 commit comments