CBMC
Loading...
Searching...
No Matches
field_sensitivity.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: Field-sensitive SSA
4
5Author: Michael Tautschnig
6
7\*******************************************************************/
8
9#include "field_sensitivity.h"
10
11#include <util/arith_tools.h>
12#include <util/byte_operators.h>
13#include <util/c_types.h>
15
16#include "goto_symex_state.h"
18#include "symex_target.h"
19
20#define ENABLE_ARRAY_FIELD_SENSITIVITY
21
23 const namespacet &ns,
24 goto_symex_statet &state,
26 bool write) const
27{
28 if(write)
29 return std::move(ssa_expr);
30 else
31 return get_fields(ns, state, ssa_expr, true);
32}
33
35 const namespacet &ns,
36 goto_symex_statet &state,
37 exprt expr,
38 bool write) const
39{
40 if(expr.id() != ID_address_of)
41 {
42 Forall_operands(it, expr)
43 *it = apply(ns, state, std::move(*it), write);
44 }
45
46 if(!write && is_ssa_expr(expr))
47 {
48 return get_fields(ns, state, to_ssa_expr(expr), true);
49 }
50 else if(
51 !write && expr.id() == ID_member &&
52 to_member_expr(expr).struct_op().id() == ID_struct)
53 {
54 return simplify_opt(std::move(expr), state.value_set, ns);
55 }
56#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
57 else if(
58 !write && expr.id() == ID_index &&
59 to_index_expr(expr).array().id() == ID_array)
60 {
61 return simplify_opt(std::move(expr), state.value_set, ns);
62 }
63#endif // ENABLE_ARRAY_FIELD_SENSITIVITY
64 else if(expr.id() == ID_member)
65 {
66 // turn a member-of-an-SSA-expression into a single SSA expression, thus
67 // encoding the member as an individual symbol rather than only the full
68 // struct
69 member_exprt &member = to_member_expr(expr);
70
71 if(
72 is_ssa_expr(member.struct_op()) &&
73 (member.struct_op().type().id() == ID_struct ||
74 member.struct_op().type().id() == ID_struct_tag ||
75 member.struct_op().type().id() == ID_union ||
76 member.struct_op().type().id() == ID_union_tag))
77 {
78 // place the entire member expression, not just the struct operand, in an
79 // SSA expression
81 bool was_l2 = !tmp.get_level_2().empty();
82
83 tmp.remove_level_2();
84 member.struct_op() = tmp.get_original_expr();
85 tmp.set_expression(member);
86 if(was_l2)
87 return apply(ns, state, state.rename(std::move(tmp), ns).get(), write);
88 else
89 return apply(ns, state, std::move(tmp), write);
90 }
91 }
92 else if(
93 !write && (expr.id() == ID_byte_extract_little_endian ||
95 {
97 if(
98 (be.op().type().id() == ID_union ||
99 be.op().type().id() == ID_union_tag) &&
100 is_ssa_expr(be.op()) && be.offset().is_constant())
101 {
102 const union_typet &type =
103 be.op().type().id() == ID_union_tag
104 ? ns.follow_tag(to_union_tag_type(be.op().type()))
105 : to_union_type(be.op().type());
106 for(const auto &comp : type.components())
107 {
108 ssa_exprt tmp = to_ssa_expr(be.op());
109 bool was_l2 = !tmp.get_level_2().empty();
110 tmp.remove_level_2();
111 const member_exprt member{tmp.get_original_expr(), comp};
112 auto recursive_member =
113 get_subexpression_at_offset(member, be.offset(), be.type(), ns);
114 if(
115 recursive_member.has_value() &&
116 (recursive_member->id() == ID_member ||
117 recursive_member->id() == ID_index))
118 {
119 tmp.type() = be.type();
120 tmp.set_expression(*recursive_member);
121 if(was_l2)
122 {
123 return apply(
124 ns, state, state.rename(std::move(tmp), ns).get(), write);
125 }
126 else
127 return apply(ns, state, std::move(tmp), write);
128 }
129 else if(
130 recursive_member.has_value() && recursive_member->id() == ID_typecast)
131 {
132 if(was_l2)
133 {
134 return apply(
135 ns,
136 state,
137 state.rename(std::move(*recursive_member), ns).get(),
138 write);
139 }
140 else
141 return apply(ns, state, std::move(*recursive_member), write);
142 }
143 }
144 }
145 }
146#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
147 else if(expr.id() == ID_index)
148 {
149 // turn a index-of-an-SSA-expression into a single SSA expression, thus
150 // encoding the index access into an array as an individual symbol rather
151 // than only the full array
152 index_exprt &index = to_index_expr(expr);
154 {
156 .simplify(index.index());
157 }
158
159 if(
160 is_ssa_expr(index.array()) && index.array().type().id() == ID_array &&
161 index.index().is_constant())
162 {
163 // place the entire index expression, not just the array operand, in an
164 // SSA expression
165 ssa_exprt tmp = to_ssa_expr(index.array());
166 auto l2_index = state.rename(index.index(), ns).get();
168 {
170 .simplify(l2_index);
171 }
172 bool was_l2 = !tmp.get_level_2().empty();
173 exprt l2_size =
174 state.rename(to_array_type(index.array().type()).size(), ns).get();
175 if(l2_size.is_nil() && index.array().id() == ID_symbol)
176 {
177 // In case the array type was incomplete, attempt to retrieve it from
178 // the symbol table.
180 to_symbol_expr(index.array()).get_identifier());
181 if(array_from_symbol_table != nullptr)
183 }
184
185 if(
186 l2_size.is_constant() &&
189 {
190 if(l2_index.is_constant())
191 {
192 // place the entire index expression, not just the array operand,
193 // in an SSA expression
195 ssa_array.remove_level_2();
196 index.array() = ssa_array.get_original_expr();
197 index.index() = l2_index;
198 tmp.set_expression(index);
199 if(was_l2)
200 {
201 return apply(
202 ns, state, state.rename(std::move(tmp), ns).get(), write);
203 }
204 else
205 return apply(ns, state, std::move(tmp), write);
206 }
207 else if(!write)
208 {
209 // Expand the array and return `{array[0]; array[1]; ...}[index]`
211 get_fields(ns, state, to_ssa_expr(index.array()), true);
212 return index_exprt{std::move(expanded_array), index.index()};
213 }
214 }
215 }
216 }
217#endif // ENABLE_ARRAY_FIELD_SENSITIVITY
218 return expr;
219}
220
222 const namespacet &ns,
223 goto_symex_statet &state,
224 const ssa_exprt &ssa_expr,
225 bool disjoined_fields_only) const
226{
227 if(
228 ssa_expr.type().id() == ID_struct ||
229 ssa_expr.type().id() == ID_struct_tag ||
230 (!disjoined_fields_only && (ssa_expr.type().id() == ID_union ||
231 ssa_expr.type().id() == ID_union_tag)))
232 {
233 const struct_union_typet::componentst &components =
234 (ssa_expr.type().id() == ID_struct_tag ||
235 ssa_expr.type().id() == ID_union_tag)
237 .components()
238 : to_struct_union_type(ssa_expr.type()).components();
239
240 exprt::operandst fields;
241 fields.reserve(components.size());
242
243 const exprt &compound_op = ssa_expr.get_original_expr();
244
245 for(const auto &comp : components)
246 {
248 bool was_l2 = !tmp.get_level_2().empty();
249 tmp.remove_level_2();
250 tmp.set_expression(
251 member_exprt{compound_op, comp.get_name(), comp.type()});
253 if(was_l2)
254 {
255 fields.emplace_back(state.rename(std::move(field), ns).get());
256 }
257 else
258 fields.emplace_back(std::move(field));
259 }
260
261 if(
262 disjoined_fields_only && (ssa_expr.type().id() == ID_struct ||
263 ssa_expr.type().id() == ID_struct_tag))
264 {
265 return struct_exprt{std::move(fields), ssa_expr.type()};
266 }
267 else
268 return field_sensitive_ssa_exprt{ssa_expr, std::move(fields)};
269 }
270#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
271 else if(
272 ssa_expr.type().id() == ID_array &&
273 to_array_type(ssa_expr.type()).size().is_constant())
274 {
276 to_constant_expr(to_array_type(ssa_expr.type()).size()));
278 return ssa_expr;
279
280 const array_typet &type = to_array_type(ssa_expr.type());
282
283 array_exprt::operandst elements;
284 elements.reserve(array_size);
285
286 const exprt &array = ssa_expr.get_original_expr();
287
288 for(std::size_t i = 0; i < array_size; ++i)
289 {
290 const index_exprt index(array, from_integer(i, type.index_type()));
292 bool was_l2 = !tmp.get_level_2().empty();
293 tmp.remove_level_2();
294 tmp.set_expression(index);
295 exprt element = get_fields(ns, state, tmp, disjoined_fields_only);
296 if(was_l2)
297 {
298 elements.emplace_back(state.rename(std::move(element), ns).get());
299 }
300 else
301 elements.emplace_back(std::move(element));
302 }
303
305 return array_exprt(std::move(elements), type);
306 else
307 return field_sensitive_ssa_exprt{ssa_expr, std::move(elements)};
308 }
309#endif // ENABLE_ARRAY_FIELD_SENSITIVITY
310 else
311 return ssa_expr;
312}
313
315 const namespacet &ns,
316 goto_symex_statet &state,
317 const ssa_exprt &lhs,
318 const exprt &rhs,
319 symex_targett &target,
320 bool allow_pointer_unsoundness) const
321{
322 const exprt lhs_fs = get_fields(ns, state, lhs, false);
323
324 if(lhs != lhs_fs)
325 {
327 ns, state, lhs_fs, rhs, target, allow_pointer_unsoundness);
328 // Erase the composite symbol from our working state. Note that we need to
329 // have it in the propagation table and the value set while doing the field
330 // assignments, thus we cannot skip putting it in there above.
331 if(is_divisible(lhs, true))
332 {
333 state.propagation.erase_if_exists(lhs.get_identifier());
334 state.value_set.erase_symbol(lhs, ns);
335 }
336 }
337}
338
350 const namespacet &ns,
351 goto_symex_statet &state,
352 const exprt &lhs_fs,
353 const exprt &ssa_rhs,
354 symex_targett &target,
355 bool allow_pointer_unsoundness) const
356{
358 {
360 const ssa_exprt ssa_lhs =
361 state
362 .assignment(l1_lhs, ssa_rhs, ns, true, true, allow_pointer_unsoundness)
363 .get();
364
365 // do the assignment
366 target.assignment(
367 state.guard.as_expr(),
368 ssa_lhs,
369 ssa_lhs,
370 ssa_lhs.get_original_expr(),
371 ssa_rhs,
372 state.source,
374 // Erase the composite symbol from our working state. Note that we need to
375 // have it in the propagation table and the value set while doing the field
376 // assignments, thus we cannot skip putting it in there above.
377 if(is_divisible(l1_lhs, true))
378 {
379 state.propagation.erase_if_exists(l1_lhs.get_identifier());
380 state.value_set.erase_symbol(l1_lhs, ns);
381 }
382 }
383 else if(
384 ssa_rhs.type().id() == ID_struct || ssa_rhs.type().id() == ID_struct_tag)
385 {
386 const struct_typet &type =
387 ssa_rhs.type().id() == ID_struct_tag
388 ? ns.follow_tag(to_struct_tag_type(ssa_rhs.type()))
389 : to_struct_type(ssa_rhs.type());
390 const struct_union_typet::componentst &components = type.components();
391
393 components.empty() ||
394 (lhs_fs.has_operands() && lhs_fs.operands().size() == components.size()));
395
396 exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
397 for(const auto &comp : components)
398 {
399 const exprt member_rhs = apply(
400 ns,
401 state,
403 member_exprt{ssa_rhs, comp.get_name(), comp.type()},
404 state.value_set,
405 ns),
406 false);
407
408 const exprt &member_lhs = *fs_it;
409 if(
410 auto fs_ssa =
412 {
414 ns,
415 state,
416 fs_ssa->get_object_ssa(),
418 target,
419 allow_pointer_unsoundness);
420 }
421
423 ns, state, member_lhs, member_rhs, target, allow_pointer_unsoundness);
424 ++fs_it;
425 }
426 }
427 else if(
428 ssa_rhs.type().id() == ID_union || ssa_rhs.type().id() == ID_union_tag)
429 {
430 const union_typet &type =
431 ssa_rhs.type().id() == ID_union_tag
432 ? ns.follow_tag(to_union_tag_type(ssa_rhs.type()))
433 : to_union_type(ssa_rhs.type());
434 const struct_union_typet::componentst &components = type.components();
435
437 components.empty() ||
438 (lhs_fs.has_operands() && lhs_fs.operands().size() == components.size()));
439
440 exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
441 for(const auto &comp : components)
442 {
443 const exprt member_rhs = apply(
444 ns,
445 state,
448 ssa_rhs, from_integer(0, c_index_type()), comp.type()),
449 state.value_set,
450 ns),
451 false);
452
453 const exprt &member_lhs = *fs_it;
454 if(
455 auto fs_ssa =
457 {
459 ns,
460 state,
461 fs_ssa->get_object_ssa(),
463 target,
464 allow_pointer_unsoundness);
465 }
466
468 ns, state, member_lhs, member_rhs, target, allow_pointer_unsoundness);
469 ++fs_it;
470 }
471 }
472#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
473 else if(const auto &type = type_try_dynamic_cast<array_typet>(ssa_rhs.type()))
474 {
475 const std::size_t array_size =
477 PRECONDITION(lhs_fs.operands().size() == array_size);
478
480 return;
481
482 exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
483 for(std::size_t i = 0; i < array_size; ++i)
484 {
485 const exprt index_rhs = apply(
486 ns,
487 state,
489 index_exprt{ssa_rhs, from_integer(i, type->index_type())},
490 state.value_set,
491 ns),
492 false);
493
494 const exprt &index_lhs = *fs_it;
495 if(
496 auto fs_ssa =
498 {
500 ns,
501 state,
502 fs_ssa->get_object_ssa(),
503 index_rhs,
504 target,
505 allow_pointer_unsoundness);
506 }
507
509 ns, state, index_lhs, index_rhs, target, allow_pointer_unsoundness);
510 ++fs_it;
511 }
512 }
513#endif // ENABLE_ARRAY_FIELD_SENSITIVITY
514 else if(lhs_fs.has_operands())
515 {
517 ssa_rhs.has_operands() &&
518 lhs_fs.operands().size() == ssa_rhs.operands().size());
519
520 exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
521 for(const exprt &op : ssa_rhs.operands())
522 {
524 {
526 ns,
527 state,
528 fs_ssa->get_object_ssa(),
529 op,
530 target,
531 allow_pointer_unsoundness);
532 }
533
535 ns, state, *fs_it, op, target, allow_pointer_unsoundness);
536 ++fs_it;
537 }
538 }
539 else
540 {
542 }
543}
544
546 const ssa_exprt &expr,
547 bool disjoined_fields_only) const
548{
549 if(expr.type().id() == ID_struct || expr.type().id() == ID_struct_tag)
550 return true;
551
552#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
553 if(
554 expr.type().id() == ID_array &&
555 to_array_type(expr.type()).size().is_constant() &&
558 {
559 return true;
560 }
561#endif
562
563 if(
565 (expr.type().id() == ID_union || expr.type().id() == ID_union_tag))
566 {
567 return true;
568 }
569
570 return false;
571}
572
574 exprt e,
575 const value_sett &value_set,
576 const namespacet &ns) const
577{
578 if(!should_simplify)
579 return e;
580
581 simplify_expr_with_value_sett{value_set, language_mode, ns}.simplify(e);
582 return e;
583}
constant_exprt from_integer(const mp_integer &int_value, const typet &type)
byte_extract_exprt make_byte_extract(const exprt &_op, const exprt &_offset, const typet &_type)
Construct a byte_extract_exprt with endianness and byte width matching the current configuration.
Expression classes for byte-level operators.
const byte_extract_exprt & to_byte_extract_expr(const exprt &expr)
bitvector_typet c_index_type()
Definition c_types.cpp:16
const union_typet & to_union_type(const typet &type)
Cast a typet to a union_typet.
Definition c_types.h:184
const union_tag_typet & to_union_tag_type(const typet &type)
Cast a typet to a union_tag_typet.
Definition c_types.h:224
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
Array constructor from list of elements.
Definition std_expr.h:1621
Arrays with given size.
Definition std_types.h:807
typet index_type() const
The type of the index expressions into any instance of this type.
Definition std_types.cpp:34
Expression of type type extracted from some object op starting at position offset (given in number of...
Base class for all expressions.
Definition expr.h:56
std::vector< exprt > operandst
Definition expr.h:58
bool has_operands() const
Return true if there is at least one operand.
Definition expr.h:91
bool is_constant() const
Return whether the expression is a constant.
Definition expr.h:212
typet & type()
Return the type of the expression.
Definition expr.h:84
operandst & operands()
Definition expr.h:94
exprt simplify_opt(exprt e, const value_sett &value_set, const namespacet &ns) const
const std::size_t max_field_sensitivity_array_size
exprt get_fields(const namespacet &ns, goto_symex_statet &state, const ssa_exprt &ssa_expr, bool disjoined_fields_only) const
Compute an expression representing the individual components of a field-sensitive SSA representation ...
void field_assignments_rec(const namespacet &ns, goto_symex_statet &state, const exprt &lhs_fs, const exprt &ssa_rhs, symex_targett &target, bool allow_pointer_unsoundness) const
Assign to the individual fields lhs_fs of a non-expanded symbol lhs.
exprt apply(const namespacet &ns, goto_symex_statet &state, exprt expr, bool write) const
Turn an expression expr into a field-sensitive SSA expression.
void field_assignments(const namespacet &ns, goto_symex_statet &state, const ssa_exprt &lhs, const exprt &rhs, symex_targett &target, bool allow_pointer_unsoundness) const
Assign to the individual fields of a non-expanded symbol lhs.
const irep_idt & language_mode
bool is_divisible(const ssa_exprt &expr, bool disjoined_fields_only) const
Determine whether expr would translate to an atomic SSA expression (returns false) or a composite obj...
guardt guard
Definition goto_state.h:58
sharing_mapt< irep_idt, exprt > propagation
Definition goto_state.h:71
value_sett value_set
Uses level 1 names, and is used to do dereferencing.
Definition goto_state.h:51
Central data structure: state.
renamedt< ssa_exprt, L2 > assignment(ssa_exprt lhs, const exprt &rhs, const namespacet &ns, bool rhs_is_simplified, bool record_value, bool allow_pointer_unsoundness=false)
renamedt< exprt, level > rename(exprt expr, const namespacet &ns)
Rewrites symbol expressions in exprt, applying a suffix to each symbol reflecting its most recent ver...
symex_targett::sourcet source
exprt as_expr() const
Definition guard_expr.h:46
Array index operator.
Definition std_expr.h:1470
exprt & index()
Definition std_expr.h:1510
exprt & array()
Definition std_expr.h:1500
const irep_idt & id() const
Definition irep.h:388
Extract member of struct or union.
Definition std_expr.h:2972
const exprt & struct_op() const
Definition std_expr.h:3010
const union_typet & follow_tag(const union_tag_typet &) const
Follow type tag of union type.
Definition namespace.cpp:49
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition namespace.h:91
const symbol_table_baset & get_symbol_table() const
Return first symbol table registered with the namespace.
Definition namespace.h:123
Expression providing an SSA-renamed symbol of expressions.
Definition ssa_expr.h:17
const exprt & get_original_expr() const
Definition ssa_expr.h:33
Struct constructor from list of elements.
Definition std_expr.h:1877
Structure type, corresponds to C style structs.
Definition std_types.h:231
const componentst & components() const
Definition std_types.h:147
std::vector< componentt > componentst
Definition std_types.h:140
const irep_idt & get_identifier() const
Definition std_expr.h:160
Symbol table entry.
Definition symbol.h:28
The interface of the target container for symbolic execution to record its symbolic steps into.
virtual void assignment(const exprt &guard, const ssa_exprt &ssa_lhs, const exprt &ssa_full_lhs, const exprt &original_full_lhs, const exprt &ssa_rhs, const sourcet &source, assignment_typet assignment_type)=0
Write to a local variable.
The union type.
Definition c_types.h:147
State type in value_set_domaint, used in value-set analysis and goto-symex.
Definition value_set.h:43
void erase_symbol(const symbol_exprt &symbol_expr, const namespacet &ns)
#define Forall_operands(it, expr)
Definition expr.h:27
Symbolic Execution.
std::optional< exprt > get_subexpression_at_offset(const exprt &expr, const mp_integer &offset_bytes, const typet &target_type_raw, const namespacet &ns)
Pointer Logic.
#define UNREACHABLE
This should be used to mark dead code.
Definition invariant.h:525
#define PRECONDITION(CONDITION)
Definition invariant.h:463
bool is_ssa_expr(const exprt &expr)
Definition ssa_expr.h:125
const ssa_exprt & to_ssa_expr(const exprt &expr)
Cast a generic exprt to an ssa_exprt.
Definition ssa_expr.h:145
const index_exprt & to_index_expr(const exprt &expr)
Cast an exprt to an index_exprt.
Definition std_expr.h:1538
const member_exprt & to_member_expr(const exprt &expr)
Cast an exprt to a member_exprt.
Definition std_expr.h:3064
const constant_exprt & to_constant_expr(const exprt &expr)
Cast an exprt to a constant_exprt.
Definition std_expr.h:3173
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition std_expr.h:272
const struct_typet & to_struct_type(const typet &type)
Cast a typet to a struct_typet.
Definition std_types.h:308
const struct_tag_typet & to_struct_tag_type(const typet &type)
Cast a typet to a struct_tag_typet.
Definition std_types.h:518
const struct_or_union_tag_typet & to_struct_or_union_tag_type(const typet &type)
Cast a typet to a struct_or_union_tag_typet.
Definition std_types.h:478
const array_typet & to_array_type(const typet &type)
Cast a typet to an array_typet.
Definition std_types.h:888
const struct_union_typet & to_struct_union_type(const typet &type)
Cast a typet to a struct_union_typet.
Definition std_types.h:214
Generate Equation using Symbolic Execution.
ssize_t write(int fildes, const void *buf, size_t nbyte)
Definition unistd.c:195