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