@@ -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,99 @@ 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 typecast wrapping the expression, the guard expresion
90
+ // / and the NULL|0 expression.
91
+ // / \param expr the candidate expression
92
+ // / \returns A pair`(guard,target)` if successful.
93
+ if_match_resultt match_if (const exprt &expr)
94
+ {
95
+ if (expr.id () == ID_typecast)
96
+ {
97
+ return match_if (expr.operands ().front ());
98
+ }
99
+ else if (expr.id () == ID_if)
100
+ {
101
+ if (!match_NULL_or_zero (expr.operands ().at (2 )))
102
+ return {};
103
+
104
+ return {{expr.operands ().at (0 ), expr.operands ().at (1 )}};
105
+ }
106
+ else
107
+ return {};
108
+ }
109
+
110
+ // / Pattern match the given expression for a guarded pattern
111
+ // / and builds guarded_slice expression as follows:
112
+ // /
113
+ // / ```
114
+ // / case expr of
115
+ // / | guard ? target : NULL ->
116
+ // / {guard,
117
+ // / target,
118
+ // / address_of{target},
119
+ // / size_of_expr{target}}
120
+ // / | __CPROVER_POINTER_OBJECT(guard ? target : NULL) ->
121
+ // / {guard,
122
+ // / __CPROVER_POINTER_OBJECT(target),
123
+ // / address_of{target-offset(target)},
124
+ // / object_size{target}}
125
+ // / | other ->
126
+ // / {true,
127
+ // / expr,
128
+ // / address_of{expr},
129
+ // / object_size{expr}}
130
+ // / ```
131
+ static const guarded_slicet
132
+ normalize_to_guarded_slice (const exprt &expr, const namespacet &ns)
133
+ {
134
+ if (expr.id () == ID_pointer_object)
135
+ {
136
+ if_match_resultt match_opt = match_if (expr.operands ().at (0 ));
137
+ if (match_opt.has_value ())
138
+ {
139
+ const auto &match = match_opt.value ();
140
+ const auto &new_expr = pointer_object (match.second );
141
+ const auto &slice = normalize_to_slice (new_expr, ns);
142
+ return {match.first , new_expr, slice.first , slice.second };
143
+ }
144
+ else
145
+ {
146
+ const auto &slice = normalize_to_slice (expr, ns);
147
+ goto_programt empty_program;
148
+ return {true_exprt{}, expr, slice.first , slice.second };
149
+ }
150
+ }
151
+
152
+ if_match_resultt match_opt = match_if (expr);
153
+ if (match_opt.has_value ())
154
+ {
155
+ const auto &match = match_opt.value ();
156
+ const auto &slice = normalize_to_slice (match.second , ns);
157
+ return {match.first , match.second , slice.first , slice.second };
158
+ }
159
+ else
160
+ {
161
+ const auto &slice = normalize_to_slice (expr, ns);
162
+ return {true_exprt{}, expr, slice.first , slice.second };
163
+ }
164
+ }
165
+
55
166
const symbolt assigns_clauset::conditional_address_ranget::generate_new_symbol (
56
167
const std::string &prefix,
57
168
const typet &type,
@@ -71,15 +182,19 @@ assigns_clauset::conditional_address_ranget::conditional_address_ranget(
71
182
: source_expr(expr),
72
183
location(expr.source_location()),
73
184
parent(parent),
74
- slice(normalize_to_slice (expr, parent.ns)),
185
+ guarded_slice(normalize_to_guarded_slice (expr, parent.ns)),
75
186
validity_condition_var(
76
187
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())
188
+ lower_bound_address_var(generate_new_symbol(
189
+ " __car_lb" ,
190
+ guarded_slice.start_adress.type(),
191
+ location)
192
+ .symbol_expr()),
193
+ upper_bound_address_var(generate_new_symbol(
194
+ " __car_ub" ,
195
+ guarded_slice.start_adress.type(),
196
+ location)
197
+ .symbol_expr())
83
198
{
84
199
}
85
200
@@ -93,6 +208,17 @@ assigns_clauset::conditional_address_ranget::generate_snapshot_instructions()
93
208
source_locationt location_no_checks = location;
94
209
disable_pointer_checks (location_no_checks);
95
210
211
+ null_message_handlert null_handler;
212
+
213
+ // clean up side effects from the guard expression
214
+ // we want checks on the guard evaluation instructions because it is user code
215
+ cleanert cleaner (parent.symbol_table , null_handler);
216
+ exprt clean_guard (guarded_slice.guard );
217
+ const auto &mode = parent.symbol_table .lookup_ref (parent.function_name ).mode ;
218
+ cleaner.clean (clean_guard, instructions, mode);
219
+ for (auto &inst : instructions.instructions )
220
+ inst.source_location_nonconst () = location;
221
+
96
222
instructions.add (
97
223
goto_programt::make_decl (validity_condition_var, location_no_checks));
98
224
instructions.add (
@@ -102,28 +228,30 @@ assigns_clauset::conditional_address_ranget::generate_snapshot_instructions()
102
228
103
229
instructions.add (goto_programt::make_assignment (
104
230
lower_bound_address_var,
105
- null_pointer_exprt{to_pointer_type (slice. first .type ())},
231
+ null_pointer_exprt{to_pointer_type (guarded_slice. start_adress .type ())},
106
232
location_no_checks));
107
233
instructions.add (goto_programt::make_assignment (
108
234
upper_bound_address_var,
109
- null_pointer_exprt{to_pointer_type (slice. first .type ())},
235
+ null_pointer_exprt{to_pointer_type (guarded_slice. start_adress .type ())},
110
236
location_no_checks));
111
237
112
238
goto_programt skip_program;
113
239
const auto skip_target =
114
240
skip_program.add (goto_programt::make_skip (location_no_checks));
115
241
116
242
const auto validity_check_expr =
117
- and_exprt{all_dereferences_are_valid (source_expr, parent.ns ),
118
- w_ok_exprt{slice.first , slice.second }};
243
+ and_exprt{clean_guard,
244
+ all_dereferences_are_valid (guarded_slice.expr , parent.ns ),
245
+ w_ok_exprt{guarded_slice.start_adress , guarded_slice.size }};
246
+
119
247
instructions.add (goto_programt::make_assignment (
120
248
validity_condition_var, validity_check_expr, location_no_checks));
121
249
122
250
instructions.add (goto_programt::make_goto (
123
251
skip_target, not_exprt{validity_condition_var}, location_no_checks));
124
252
125
253
instructions.add (goto_programt::make_assignment (
126
- lower_bound_address_var, slice. first , location_no_checks));
254
+ lower_bound_address_var, guarded_slice. start_adress , location_no_checks));
127
255
128
256
// the computation of the CAR upper bound can overflow into the object ID bits
129
257
// of the pointer with very large allocation sizes.
@@ -133,7 +261,7 @@ assigns_clauset::conditional_address_ranget::generate_snapshot_instructions()
133
261
134
262
instructions.add (goto_programt::make_assignment (
135
263
upper_bound_address_var,
136
- plus_exprt{slice. first , slice. second },
264
+ plus_exprt{guarded_slice. start_adress , guarded_slice. size },
137
265
location_overflow_check));
138
266
instructions.destructive_append (skip_program);
139
267
0 commit comments