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,
38 bool write) const
39{
40 if(
41 (be.op().type().id() != ID_union && be.op().type().id() != ID_union_tag) ||
42 !is_ssa_expr(be.op()) || !be.offset().is_constant())
43 {
44 return be;
45 }
46
47 const union_typet &type = be.op().type().id() == ID_union_tag
48 ? ns.follow_tag(to_union_tag_type(be.op().type()))
49 : to_union_type(be.op().type());
50 for(const auto &comp : type.components())
51 {
53 bool was_l2 = !tmp.get_level_2().empty();
54 tmp.remove_level_2();
55 const member_exprt member{tmp.get_original_expr(), comp};
56 auto recursive_member =
57 get_subexpression_at_offset(member, be.offset(), be.type(), ns);
58 if(!recursive_member.has_value())
59 continue;
60
61 // We need to inspect the access path as the resulting expression may
62 // involve index expressions. When array field sensitivity is disabled
63 // or the size of the array that is indexed into is larger than
64 // max_field_sensitivity_array_size then only the expression up to (but
65 // excluding) said index expression can be turned into an ssa_exprt.
68 exprt *parent = for_ssa;
69 while(parent->id() == ID_typecast)
70 parent = &to_typecast_expr(*parent).op();
71 while(parent->id() == ID_member || parent->id() == ID_index)
72 {
73 if(parent->id() == ID_member)
74 {
75 parent = &to_member_expr(*parent).compound();
76 }
77 else
78 {
79 parent = &to_index_expr(*parent).array();
80#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
81 if(
82 !to_array_type(parent->type()).size().is_constant() ||
84 to_constant_expr(to_array_type(parent->type()).size())) >
86 {
87 for_ssa = parent;
88 }
89#else
90 for_ssa = parent;
91#endif // ENABLE_ARRAY_FIELD_SENSITIVITY
92 }
93 }
94
95 if(for_ssa->id() == ID_index || for_ssa->id() == ID_member)
96 {
97 tmp.type() = for_ssa->type();
98 tmp.set_expression(*for_ssa);
99 if(was_l2)
100 {
101 *for_ssa =
102 apply(ns, state, state.rename(std::move(tmp), ns).get(), write);
103 }
104 else
105 *for_ssa = apply(ns, state, std::move(tmp), write);
106
107 return full_exprt;
108 }
109 else if(for_ssa->id() == ID_typecast)
110 {
111 if(was_l2)
112 {
113 *for_ssa = apply(ns, state, state.rename(*for_ssa, ns).get(), write);
114 }
115 else
116 *for_ssa = apply(ns, state, std::move(*for_ssa), write);
117
118 return full_exprt;
119 }
120 }
121
122 return be;
123}
124
126 const namespacet &ns,
127 goto_symex_statet &state,
128 exprt expr,
129 bool write) const
130{
131 if(expr.id() != ID_address_of)
132 {
133 Forall_operands(it, expr)
134 *it = apply(ns, state, std::move(*it), write);
135 }
136
137 if(!write && is_ssa_expr(expr))
138 {
139 return get_fields(ns, state, to_ssa_expr(expr), true);
140 }
141 else if(
142 !write && expr.id() == ID_member &&
143 to_member_expr(expr).struct_op().id() == ID_struct)
144 {
145 return simplify_opt(std::move(expr), state.value_set, ns);
146 }
147#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
148 else if(
149 !write && expr.id() == ID_index &&
150 to_index_expr(expr).array().id() == ID_array)
151 {
152 return simplify_opt(std::move(expr), state.value_set, ns);
153 }
154#endif // ENABLE_ARRAY_FIELD_SENSITIVITY
155 else if(expr.id() == ID_member)
156 {
157 // turn a member-of-an-SSA-expression into a single SSA expression, thus
158 // encoding the member as an individual symbol rather than only the full
159 // struct
160 member_exprt &member = to_member_expr(expr);
161
162 if(
163 is_ssa_expr(member.struct_op()) &&
164 (member.struct_op().type().id() == ID_struct ||
165 member.struct_op().type().id() == ID_struct_tag ||
166 member.struct_op().type().id() == ID_union ||
167 member.struct_op().type().id() == ID_union_tag))
168 {
169 // place the entire member expression, not just the struct operand, in an
170 // SSA expression
172 bool was_l2 = !tmp.get_level_2().empty();
173
174 tmp.remove_level_2();
175 member.struct_op() = tmp.get_original_expr();
176 tmp.set_expression(member);
177 if(was_l2)
178 return apply(ns, state, state.rename(std::move(tmp), ns).get(), write);
179 else
180 return apply(ns, state, std::move(tmp), write);
181 }
182 }
183 else if(
184 !write && (expr.id() == ID_byte_extract_little_endian ||
186 {
187 return apply_byte_extract(ns, state, to_byte_extract_expr(expr), write);
188 }
189#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
190 else if(expr.id() == ID_index)
191 {
192 // turn a index-of-an-SSA-expression into a single SSA expression, thus
193 // encoding the index access into an array as an individual symbol rather
194 // than only the full array
195 index_exprt &index = to_index_expr(expr);
197 {
199 .simplify(index.index());
200 }
201
202 if(
203 is_ssa_expr(index.array()) && index.array().type().id() == ID_array &&
204 index.index().is_constant())
205 {
206 // place the entire index expression, not just the array operand, in an
207 // SSA expression
208 ssa_exprt tmp = to_ssa_expr(index.array());
209 auto l2_index = state.rename(index.index(), ns).get();
211 {
213 .simplify(l2_index);
214 }
215 bool was_l2 = !tmp.get_level_2().empty();
216 exprt l2_size =
217 state.rename(to_array_type(index.array().type()).size(), ns).get();
218 if(l2_size.is_nil() && index.array().id() == ID_symbol)
219 {
220 // In case the array type was incomplete, attempt to retrieve it from
221 // the symbol table.
223 to_symbol_expr(index.array()).get_identifier());
224 if(array_from_symbol_table != nullptr)
226 }
227
228 if(
229 l2_size.is_constant() &&
232 {
233 if(l2_index.is_constant())
234 {
235 // place the entire index expression, not just the array operand,
236 // in an SSA expression
238 ssa_array.remove_level_2();
239 index.array() = ssa_array.get_original_expr();
240 index.index() = l2_index;
241 tmp.set_expression(index);
242 if(was_l2)
243 {
244 return apply(
245 ns, state, state.rename(std::move(tmp), ns).get(), write);
246 }
247 else
248 return apply(ns, state, std::move(tmp), write);
249 }
250 else if(!write)
251 {
252 // Expand the array and return `{array[0]; array[1]; ...}[index]`
254 get_fields(ns, state, to_ssa_expr(index.array()), true);
255 return index_exprt{std::move(expanded_array), index.index()};
256 }
257 }
258 }
259 }
260#endif // ENABLE_ARRAY_FIELD_SENSITIVITY
261 return expr;
262}
263
265 const namespacet &ns,
266 goto_symex_statet &state,
267 const ssa_exprt &ssa_expr,
268 bool disjoined_fields_only) const
269{
270 if(
271 ssa_expr.type().id() == ID_struct ||
272 ssa_expr.type().id() == ID_struct_tag ||
273 (!disjoined_fields_only && (ssa_expr.type().id() == ID_union ||
274 ssa_expr.type().id() == ID_union_tag)))
275 {
276 const struct_union_typet::componentst &components =
277 (ssa_expr.type().id() == ID_struct_tag ||
278 ssa_expr.type().id() == ID_union_tag)
280 .components()
281 : to_struct_union_type(ssa_expr.type()).components();
282
283 exprt::operandst fields;
284 fields.reserve(components.size());
285
286 const exprt &compound_op = ssa_expr.get_original_expr();
287
288 for(const auto &comp : components)
289 {
291 bool was_l2 = !tmp.get_level_2().empty();
292 tmp.remove_level_2();
293 tmp.set_expression(
294 member_exprt{compound_op, comp.get_name(), comp.type()});
296 if(was_l2)
297 {
298 fields.emplace_back(state.rename(std::move(field), ns).get());
299 }
300 else
301 fields.emplace_back(std::move(field));
302 }
303
304 if(
305 disjoined_fields_only && (ssa_expr.type().id() == ID_struct ||
306 ssa_expr.type().id() == ID_struct_tag))
307 {
308 return struct_exprt{std::move(fields), ssa_expr.type()};
309 }
310 else
311 return field_sensitive_ssa_exprt{ssa_expr, std::move(fields)};
312 }
313#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
314 else if(
315 ssa_expr.type().id() == ID_array &&
316 to_array_type(ssa_expr.type()).size().is_constant())
317 {
319 to_constant_expr(to_array_type(ssa_expr.type()).size()));
321 return ssa_expr;
322
323 const array_typet &type = to_array_type(ssa_expr.type());
325
326 array_exprt::operandst elements;
327 elements.reserve(array_size);
328
329 const exprt &array = ssa_expr.get_original_expr();
330
331 for(std::size_t i = 0; i < array_size; ++i)
332 {
333 const index_exprt index(array, from_integer(i, type.index_type()));
335 bool was_l2 = !tmp.get_level_2().empty();
336 tmp.remove_level_2();
337 tmp.set_expression(index);
338 exprt element = get_fields(ns, state, tmp, disjoined_fields_only);
339 if(was_l2)
340 {
341 elements.emplace_back(state.rename(std::move(element), ns).get());
342 }
343 else
344 elements.emplace_back(std::move(element));
345 }
346
348 return array_exprt(std::move(elements), type);
349 else
350 return field_sensitive_ssa_exprt{ssa_expr, std::move(elements)};
351 }
352#endif // ENABLE_ARRAY_FIELD_SENSITIVITY
353 else
354 return ssa_expr;
355}
356
358 const namespacet &ns,
359 goto_symex_statet &state,
360 const ssa_exprt &lhs,
361 const exprt &rhs,
362 symex_targett &target,
363 bool allow_pointer_unsoundness) const
364{
365 const exprt lhs_fs = get_fields(ns, state, lhs, false);
366
367 if(lhs != lhs_fs)
368 {
370 ns, state, lhs_fs, rhs, target, allow_pointer_unsoundness);
371 // Erase the composite symbol from our working state. Note that we need to
372 // have it in the propagation table and the value set while doing the field
373 // assignments, thus we cannot skip putting it in there above.
374 if(is_divisible(lhs, true))
375 {
376 state.propagation.erase_if_exists(lhs.get_identifier());
377 state.value_set.erase_symbol(lhs, ns);
378 }
379 }
380}
381
393 const namespacet &ns,
394 goto_symex_statet &state,
395 const exprt &lhs_fs,
396 const exprt &ssa_rhs,
397 symex_targett &target,
398 bool allow_pointer_unsoundness) const
399{
401 {
403 const ssa_exprt ssa_lhs =
404 state
405 .assignment(l1_lhs, ssa_rhs, ns, true, true, allow_pointer_unsoundness)
406 .get();
407
408 // do the assignment
409 target.assignment(
410 state.guard.as_expr(),
411 ssa_lhs,
412 ssa_lhs,
413 ssa_lhs.get_original_expr(),
414 ssa_rhs,
415 state.source,
417 // Erase the composite symbol from our working state. Note that we need to
418 // have it in the propagation table and the value set while doing the field
419 // assignments, thus we cannot skip putting it in there above.
420 if(is_divisible(l1_lhs, true))
421 {
422 state.propagation.erase_if_exists(l1_lhs.get_identifier());
423 state.value_set.erase_symbol(l1_lhs, ns);
424 }
425 }
426 else if(
427 ssa_rhs.type().id() == ID_struct || ssa_rhs.type().id() == ID_struct_tag)
428 {
429 const struct_typet &type =
430 ssa_rhs.type().id() == ID_struct_tag
431 ? ns.follow_tag(to_struct_tag_type(ssa_rhs.type()))
432 : to_struct_type(ssa_rhs.type());
433 const struct_union_typet::componentst &components = type.components();
434
436 components.empty() ||
437 (lhs_fs.has_operands() && lhs_fs.operands().size() == components.size()));
438
439 exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
440 for(const auto &comp : components)
441 {
442 const exprt member_rhs = apply(
443 ns,
444 state,
446 member_exprt{ssa_rhs, comp.get_name(), comp.type()},
447 state.value_set,
448 ns),
449 false);
450
451 const exprt &member_lhs = *fs_it;
452 if(
453 auto fs_ssa =
455 {
457 ns,
458 state,
459 fs_ssa->get_object_ssa(),
461 target,
462 allow_pointer_unsoundness);
463 }
464
466 ns, state, member_lhs, member_rhs, target, allow_pointer_unsoundness);
467 ++fs_it;
468 }
469 }
470 else if(
471 ssa_rhs.type().id() == ID_union || ssa_rhs.type().id() == ID_union_tag)
472 {
473 const union_typet &type =
474 ssa_rhs.type().id() == ID_union_tag
475 ? ns.follow_tag(to_union_tag_type(ssa_rhs.type()))
476 : to_union_type(ssa_rhs.type());
477 const struct_union_typet::componentst &components = type.components();
478
480 components.empty() ||
481 (lhs_fs.has_operands() && lhs_fs.operands().size() == components.size()));
482
483 exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
484 for(const auto &comp : components)
485 {
486 const exprt member_rhs = apply(
487 ns,
488 state,
491 ssa_rhs, from_integer(0, c_index_type()), comp.type()),
492 state.value_set,
493 ns),
494 false);
495
496 const exprt &member_lhs = *fs_it;
497 if(
498 auto fs_ssa =
500 {
502 ns,
503 state,
504 fs_ssa->get_object_ssa(),
506 target,
507 allow_pointer_unsoundness);
508 }
509
511 ns, state, member_lhs, member_rhs, target, allow_pointer_unsoundness);
512 ++fs_it;
513 }
514 }
515#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
516 else if(const auto &type = type_try_dynamic_cast<array_typet>(ssa_rhs.type()))
517 {
518 const std::size_t array_size =
520 PRECONDITION(lhs_fs.operands().size() == array_size);
521
523 return;
524
525 exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
526 for(std::size_t i = 0; i < array_size; ++i)
527 {
528 const exprt index_rhs = apply(
529 ns,
530 state,
532 index_exprt{ssa_rhs, from_integer(i, type->index_type())},
533 state.value_set,
534 ns),
535 false);
536
537 const exprt &index_lhs = *fs_it;
538 if(
539 auto fs_ssa =
541 {
543 ns,
544 state,
545 fs_ssa->get_object_ssa(),
546 index_rhs,
547 target,
548 allow_pointer_unsoundness);
549 }
550
552 ns, state, index_lhs, index_rhs, target, allow_pointer_unsoundness);
553 ++fs_it;
554 }
555 }
556#endif // ENABLE_ARRAY_FIELD_SENSITIVITY
557 else if(lhs_fs.has_operands())
558 {
560 ssa_rhs.has_operands() &&
561 lhs_fs.operands().size() == ssa_rhs.operands().size());
562
563 exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
564 for(const exprt &op : ssa_rhs.operands())
565 {
567 {
569 ns,
570 state,
571 fs_ssa->get_object_ssa(),
572 op,
573 target,
574 allow_pointer_unsoundness);
575 }
576
578 ns, state, *fs_it, op, target, allow_pointer_unsoundness);
579 ++fs_it;
580 }
581 }
582 else
583 {
585 }
586}
587
589 const ssa_exprt &expr,
590 bool disjoined_fields_only) const
591{
592 if(expr.type().id() == ID_struct || expr.type().id() == ID_struct_tag)
593 return true;
594
595#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
596 if(
597 expr.type().id() == ID_array &&
598 to_array_type(expr.type()).size().is_constant() &&
601 {
602 return true;
603 }
604#endif
605
606 if(
608 (expr.type().id() == ID_union || expr.type().id() == ID_union_tag))
609 {
610 return true;
611 }
612
613 return false;
614}
615
617 exprt e,
618 const value_sett &value_set,
619 const namespacet &ns) const
620{
621 if(!should_simplify)
622 return e;
623
624 simplify_expr_with_value_sett{value_set, language_mode, ns}.simplify(e);
625 return e;
626}
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:57
std::vector< exprt > operandst
Definition expr.h:59
bool has_operands() const
Return true if there is at least one operand.
Definition expr.h:92
bool is_constant() const
Return whether the expression is a constant.
Definition expr.h:213
typet & type()
Return the type of the expression.
Definition expr.h:85
operandst & operands()
Definition expr.h:95
exprt simplify_opt(exprt e, const value_sett &value_set, const namespacet &ns) const
exprt apply_byte_extract(const namespacet &ns, goto_symex_statet &state, const byte_extract_exprt &expr, bool write) const
Turn an expression expr into a field-sensitive SSA expression.
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:28
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 typecast_exprt & to_typecast_expr(const exprt &expr)
Cast an exprt to a typecast_exprt.
Definition std_expr.h:2107
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:3189
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