CBMC
symex_assign.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Symbolic Execution
4 
5 Author: Daniel Kroening, kroening@kroening.com
6 
7 \*******************************************************************/
8 
11 
12 #include "symex_assign.h"
13 
14 #include <util/byte_operators.h>
15 #include <util/expr_util.h>
16 #include <util/pointer_expr.h>
17 #include <util/range.h>
18 
19 #include "expr_skeleton.h"
20 #include "goto_symex_state.h"
21 #include "symex_config.h"
22 
23 // We can either use with_exprt or update_exprt when building expressions that
24 // modify components of an array or a struct. Set USE_UPDATE to use
25 // update_exprt.
26 // #define USE_UPDATE
27 
28 constexpr bool use_update()
29 {
30 #ifdef USE_UPDATE
31  return true;
32 #else
33  return false;
34 #endif
35 }
36 
42 {
43  if(const auto &address_of = expr_try_dynamic_cast<address_of_exprt>(rhs))
44  {
45  if(
46  const auto &index =
47  expr_try_dynamic_cast<index_exprt>(address_of->object()))
48  {
49  if(index->array().id() == ID_string_constant && index->index().is_zero())
50  {
51  return true;
52  }
53  }
54  }
55  return false;
56 }
57 
59  const exprt &lhs,
60  const expr_skeletont &full_lhs,
61  const exprt &rhs,
63 {
64  if(is_ssa_expr(lhs))
65  {
66  assign_symbol(to_ssa_expr(lhs), full_lhs, rhs, guard);
67 
68  // Allocate shadow memory
69  if(shadow_memory.has_value())
70  {
71  bool is_string_constant_init = is_string_constant_initialization(rhs);
72  if(is_string_constant_init)
73  {
74  shadow_memory->symex_field_static_init_string_constant(
75  state, to_ssa_expr(lhs), rhs);
76  }
77  else
78  {
79  shadow_memory->symex_field_static_init(state, to_ssa_expr(lhs));
80  }
81  }
82  }
83  else if(lhs.id() == ID_index)
84  assign_array<use_update()>(to_index_expr(lhs), full_lhs, rhs, guard);
85  else if(lhs.id()==ID_member)
86  {
87  const typet &type = to_member_expr(lhs).struct_op().type();
88  if(type.id() == ID_struct || type.id() == ID_struct_tag)
89  {
90  assign_struct_member<use_update()>(
91  to_member_expr(lhs), full_lhs, rhs, guard);
92  }
93  else if(type.id() == ID_union || type.id() == ID_union_tag)
94  {
95  // should have been replaced by byte_extract
97  "assign_rec: unexpected assignment to union member");
98  }
99  else
101  "assign_rec: unexpected assignment to member of '" + type.id_string() +
102  "'");
103  }
104  else if(lhs.id()==ID_if)
105  assign_if(to_if_expr(lhs), full_lhs, rhs, guard);
106  else if(lhs.id()==ID_typecast)
107  assign_typecast(to_typecast_expr(lhs), full_lhs, rhs, guard);
109  {
110  // ignore
111  }
112  else if(lhs.id()==ID_byte_extract_little_endian ||
113  lhs.id()==ID_byte_extract_big_endian)
114  {
115  assign_byte_extract(to_byte_extract_expr(lhs), full_lhs, rhs, guard);
116  }
117  else if(lhs.id() == ID_complex_real)
118  {
119  // this is stuff like __real__ x = 1;
120  const complex_real_exprt &complex_real_expr = to_complex_real_expr(lhs);
121 
122  const complex_imag_exprt complex_imag_expr(complex_real_expr.op());
123 
124  complex_exprt new_rhs(
125  rhs, complex_imag_expr, to_complex_type(complex_real_expr.op().type()));
126 
127  assign_rec(complex_real_expr.op(), full_lhs, new_rhs, guard);
128  }
129  else if(lhs.id() == ID_complex_imag)
130  {
131  const complex_imag_exprt &complex_imag_expr = to_complex_imag_expr(lhs);
132 
133  const complex_real_exprt complex_real_expr(complex_imag_expr.op());
134 
135  complex_exprt new_rhs(
136  complex_real_expr, rhs, to_complex_type(complex_imag_expr.op().type()));
137 
138  assign_rec(complex_imag_expr.op(), full_lhs, new_rhs, guard);
139  }
140  else
142  "assignment to '" + lhs.id_string() + "' not handled");
143 }
144 
146 struct assignmentt final
147 {
152 };
153 
166  const ssa_exprt &lhs, // L1
167  const expr_skeletont &full_lhs,
168  const struct_exprt &rhs,
169  const exprt::operandst &guard)
170 {
171  const auto &components =
172  lhs.type().id() == ID_struct_tag
174  : to_struct_type(lhs.type()).components();
175  PRECONDITION(rhs.operands().size() == components.size());
176 
177  for(const auto &comp_rhs : make_range(components).zip(rhs.operands()))
178  {
179  const auto &comp = comp_rhs.first;
180  const exprt lhs_field = state.field_sensitivity.apply(
181  ns, state, member_exprt{lhs, comp.get_name(), comp.type()}, true);
182  INVARIANT(
183  lhs_field.id() == ID_symbol,
184  "member of symbol should be susceptible to field-sensitivity");
185 
186  assign_symbol(to_ssa_expr(lhs_field), full_lhs, comp_rhs.second, guard);
187  }
188 }
189 
191  const ssa_exprt &lhs, // L1
192  const expr_skeletont &full_lhs,
193  const exprt &rhs,
194  const exprt::operandst &guard)
195 {
196  exprt l2_rhs =
197  state
198  .rename(
199  // put assignment guard into the rhs
200  guard.empty()
201  ? rhs
202  : static_cast<exprt>(if_exprt{conjunction(guard), rhs, lhs}),
203  ns)
204  .get();
205 
206  assignmentt assignment{lhs, full_lhs, l2_rhs};
207 
209  assignment.rhs = simplify_expr(std::move(assignment.rhs), ns);
210 
211  const ssa_exprt l2_lhs = state
212  .assignment(
213  assignment.lhs,
214  assignment.rhs,
215  ns,
219  .get();
220 
221  state.record_events.push(false);
222  // Note any other symbols mentioned in the skeleton are rvalues -- for example
223  // array indices -- and were renamed to L2 at the start of
224  // goto_symext::symex_assign.
225  const exprt l2_full_lhs = assignment.original_lhs_skeleton.apply(l2_lhs);
227  {
228  INVARIANT(
229  !check_renaming(l2_full_lhs), "l2_full_lhs should be renamed to L2");
230  }
231  state.record_events.pop();
232 
233  auto current_assignment_type =
234  ns.lookup(l2_lhs.get_object_name()).is_auxiliary &&
236  std::string::npos
238  : assignment_type;
239 
242  l2_lhs,
243  l2_full_lhs,
244  get_original_name(l2_full_lhs),
245  assignment.rhs,
246  state.source,
247  current_assignment_type);
248 
249  const ssa_exprt &l1_lhs = assignment.lhs;
250  if(state.field_sensitivity.is_divisible(l1_lhs, false))
251  {
252  // Split composite symbol lhs into its components
254  ns,
255  state,
256  l1_lhs,
257  assignment.rhs,
258  target,
260  }
261 }
262 
264  const ssa_exprt &lhs, // L1
265  const expr_skeletont &full_lhs,
266  const exprt &rhs,
267  const exprt::operandst &guard)
268 {
269  // Shortcut the common case of a whole-struct initializer:
270  if(rhs.id() == ID_struct)
271  assign_from_struct(lhs, full_lhs, to_struct_expr(rhs), guard);
272  else
273  assign_non_struct_symbol(lhs, full_lhs, rhs, guard);
274 }
275 
277  const typecast_exprt &lhs,
278  const expr_skeletont &full_lhs,
279  const exprt &rhs,
281 {
282  // these may come from dereferencing on the lhs
283  exprt rhs_typecasted = typecast_exprt::conditional_cast(rhs, lhs.op().type());
284  expr_skeletont new_skeleton =
285  full_lhs.compose(expr_skeletont::remove_op0(lhs));
286  assign_rec(lhs.op(), new_skeleton, rhs_typecasted, guard);
287 }
288 
289 template <bool use_update>
291  const index_exprt &lhs,
292  const expr_skeletont &full_lhs,
293  const exprt &rhs,
295 {
296  const exprt &lhs_array=lhs.array();
297  const exprt &lhs_index=lhs.index();
298  const typet &lhs_index_type = lhs_array.type();
299 
300  PRECONDITION(lhs_index_type.id() == ID_array);
301 
302  if(use_update)
303  {
304  // turn
305  // a[i]=e
306  // into
307  // a'==UPDATE(a, [i], e)
308  const update_exprt new_rhs{lhs_array, index_designatort(lhs_index), rhs};
309  const expr_skeletont new_skeleton =
310  full_lhs.compose(expr_skeletont::remove_op0(lhs));
311  assign_rec(lhs, new_skeleton, new_rhs, guard);
312  }
313  else
314  {
315  // turn
316  // a[i]=e
317  // into
318  // a'==a WITH [i:=e]
319  const with_exprt new_rhs{lhs_array, lhs_index, rhs};
320  const expr_skeletont new_skeleton =
321  full_lhs.compose(expr_skeletont::remove_op0(lhs));
322  assign_rec(lhs_array, new_skeleton, new_rhs, guard);
323  }
324 }
325 
326 template <bool use_update>
328  const member_exprt &lhs,
329  const expr_skeletont &full_lhs,
330  const exprt &rhs,
332 {
333  // Symbolic execution of a struct member assignment.
334 
335  // lhs must be member operand, which
336  // takes one operand, which must be a structure.
337 
338  exprt lhs_struct = lhs.op();
339 
340  // typecasts involved? C++ does that for inheritance.
341  if(lhs_struct.id()==ID_typecast)
342  {
343  if(to_typecast_expr(lhs_struct).op().id() == ID_null_object)
344  {
345  // ignore, and give up
346  return;
347  }
348  else
349  {
350  // remove the type cast, we assume that the member is there
351  exprt tmp = to_typecast_expr(lhs_struct).op();
352 
353  if(tmp.type().id() == ID_struct || tmp.type().id() == ID_struct_tag)
354  lhs_struct=tmp;
355  else
356  return; // ignore and give up
357  }
358  }
359 
360  const irep_idt &component_name=lhs.get_component_name();
361 
362  if(use_update)
363  {
364  // turn
365  // a.c=e
366  // into
367  // a'==UPDATE(a, .c, e)
368  const update_exprt new_rhs{
369  lhs_struct, member_designatort(component_name), rhs};
370  const expr_skeletont new_skeleton =
371  full_lhs.compose(expr_skeletont::remove_op0(lhs));
372  assign_rec(lhs_struct, new_skeleton, new_rhs, guard);
373  }
374  else
375  {
376  // turn
377  // a.c=e
378  // into
379  // a'==a WITH [c:=e]
380 
381  with_exprt new_rhs(lhs_struct, exprt(ID_member_name), rhs);
382  new_rhs.where().set(ID_component_name, component_name);
383  const expr_skeletont new_skeleton =
384  full_lhs.compose(expr_skeletont::remove_op0(lhs));
385  assign_rec(lhs_struct, new_skeleton, new_rhs, guard);
386  }
387 }
388 
390  const if_exprt &lhs,
391  const expr_skeletont &full_lhs,
392  const exprt &rhs,
394 {
395  // we have (c?a:b)=e;
396 
397  guard.push_back(lhs.cond());
398  assign_rec(lhs.true_case(), full_lhs, rhs, guard);
399  guard.pop_back();
400 
401  guard.push_back(not_exprt(lhs.cond()));
402  assign_rec(lhs.false_case(), full_lhs, rhs, guard);
403  guard.pop_back();
404 }
405 
407  const byte_extract_exprt &lhs,
408  const expr_skeletont &full_lhs,
409  const exprt &rhs,
411 {
412  // we have byte_extract_X(object, offset)=value
413  // turn into object=byte_update_X(object, offset, value)
414 
416  if(lhs.id()==ID_byte_extract_little_endian)
417  byte_update_id = ID_byte_update_little_endian;
418  else if(lhs.id()==ID_byte_extract_big_endian)
419  byte_update_id = ID_byte_update_big_endian;
420  else
421  UNREACHABLE;
422 
423  const byte_update_exprt new_rhs{
424  byte_update_id, lhs.op(), lhs.offset(), rhs, lhs.get_bits_per_byte()};
425  const expr_skeletont new_skeleton =
426  full_lhs.compose(expr_skeletont::remove_op0(lhs));
427  assign_rec(lhs.op(), new_skeleton, new_rhs, guard);
428 }
static irep_idt byte_update_id()
Expression classes for byte-level operators.
const byte_extract_exprt & to_byte_extract_expr(const exprt &expr)
static exprt guard(const exprt::operandst &guards, exprt cond)
Expression of type type extracted from some object op starting at position offset (given in number of...
std::size_t get_bits_per_byte() const
Expression corresponding to op() where the bytes starting at position offset (given in number of byte...
Complex constructor from a pair of numbers.
Definition: std_expr.h:1916
Imaginary part of the expression describing a complex number.
Definition: std_expr.h:2027
Real part of the expression describing a complex number.
Definition: std_expr.h:1984
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition: dstring.h:38
Expression in which some part is missing and can be substituted for another expression.
Definition: expr_skeleton.h:26
expr_skeletont compose(expr_skeletont other) const
Replace the missing part of the current skeleton by another skeleton, ending in a bigger skeleton cor...
static expr_skeletont remove_op0(exprt e)
Create a skeleton by removing the first operand of e.
Base class for all expressions.
Definition: expr.h:56
std::vector< exprt > operandst
Definition: expr.h:58
typet & type()
Return the type of the expression.
Definition: expr.h:84
operandst & operands()
Definition: expr.h:94
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.
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
std::stack< bool > record_events
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...
static bool is_read_only_object(const exprt &lvalue)
Returns true if lvalue is a read-only object, such as the null object.
field_sensitivityt field_sensitivity
symex_targett::sourcet source
exprt as_expr() const
Definition: guard_expr.h:46
The trinary if-then-else operator.
Definition: std_expr.h:2380
exprt & true_case()
Definition: std_expr.h:2407
exprt & false_case()
Definition: std_expr.h:2417
exprt & cond()
Definition: std_expr.h:2397
Array index operator.
Definition: std_expr.h:1470
exprt & array()
Definition: std_expr.h:1500
exprt & index()
Definition: std_expr.h:1510
const std::string & id_string() const
Definition: irep.h:391
void set(const irep_idt &name, const irep_idt &value)
Definition: irep.h:412
const irep_idt & id() const
Definition: irep.h:388
Extract member of struct or union.
Definition: std_expr.h:2854
const exprt & struct_op() const
Definition: std_expr.h:2892
irep_idt get_component_name() const
Definition: std_expr.h:2876
const union_typet & follow_tag(const union_tag_typet &) const
Follow type tag of union type.
Definition: namespace.cpp:63
bool lookup(const irep_idt &name, const symbolt *&symbol) const override
See documentation for namespace_baset::lookup().
Definition: namespace.cpp:148
Boolean negation.
Definition: std_expr.h:2337
Expression providing an SSA-renamed symbol of expressions.
Definition: ssa_expr.h:17
irep_idt get_object_name() const
Struct constructor from list of elements.
Definition: std_expr.h:1877
const componentst & components() const
Definition: std_types.h:147
void assign_byte_extract(const byte_extract_exprt &lhs, const expr_skeletont &full_lhs, const exprt &rhs, exprt::operandst &guard)
void assign_if(const if_exprt &lhs, const expr_skeletont &full_lhs, const exprt &rhs, exprt::operandst &guard)
const symex_configt & symex_config
Definition: symex_assign.h:67
void assign_non_struct_symbol(const ssa_exprt &lhs, const expr_skeletont &full_lhs, const exprt &rhs, const exprt::operandst &guard)
goto_symex_statet & state
Definition: symex_assign.h:64
void assign_array(const index_exprt &lhs, const expr_skeletont &full_lhs, const exprt &rhs, exprt::operandst &guard)
symex_targett & target
Definition: symex_assign.h:68
void assign_rec(const exprt &lhs, const expr_skeletont &full_lhs, const exprt &rhs, exprt::operandst &guard)
void assign_symbol(const ssa_exprt &lhs, const expr_skeletont &full_lhs, const exprt &rhs, const exprt::operandst &guard)
Record the assignment of value rhs to variable lhs in state and add the equation to target: lhs#{n+1}...
void assign_struct_member(const member_exprt &lhs, const expr_skeletont &full_lhs, const exprt &rhs, exprt::operandst &guard)
symex_targett::assignment_typet assignment_type
Definition: symex_assign.h:65
const namespacet & ns
Definition: symex_assign.h:66
void assign_from_struct(const ssa_exprt &lhs, const expr_skeletont &full_lhs, const struct_exprt &rhs, const exprt::operandst &guard)
Assign a struct expression to a symbol.
void assign_typecast(const typecast_exprt &lhs, const expr_skeletont &full_lhs, const exprt &rhs, exprt::operandst &guard)
std::optional< shadow_memoryt > shadow_memory
Definition: symex_assign.h:63
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.
Semantic type conversion.
Definition: std_expr.h:2073
static exprt conditional_cast(const exprt &expr, const typet &type)
Definition: std_expr.h:2081
The type of an expression, extends irept.
Definition: type.h:29
const exprt & op() const
Definition: std_expr.h:391
Thrown when we encounter an instruction, parameters to an instruction etc.
Operator to update elements in structs and arrays.
Definition: std_expr.h:2665
Operator to update elements in structs and arrays.
Definition: std_expr.h:2481
exprt & where()
Definition: std_expr.h:2501
Expression skeleton.
exprt make_and(exprt a, exprt b)
Conjunction of two expressions.
Definition: expr_util.cpp:321
Deprecated expression utility functions.
Symbolic Execution.
const std::string & id2string(const irep_idt &d)
Definition: irep.h:44
API to expression classes for Pointers.
Ranges: pair of begin and end iterators, which can be initialized from containers,...
ranget< iteratort > make_range(iteratort begin, iteratort end)
Definition: range.h:522
exprt get_original_name(exprt expr)
Undo all levels of renaming.
bool check_renaming(const typet &type)
Check that type is correctly renamed to level 2 and return true in case an error is detected.
#define SHADOW_MEMORY_SYMBOL_PREFIX
Definition: shadow_memory.h:26
exprt simplify_expr(exprt src, const namespacet &ns)
#define UNREACHABLE
This should be used to mark dead code.
Definition: invariant.h:525
#define PRECONDITION(CONDITION)
Definition: invariant.h:463
const ssa_exprt & to_ssa_expr(const exprt &expr)
Cast a generic exprt to an ssa_exprt.
Definition: ssa_expr.h:145
bool is_ssa_expr(const exprt &expr)
Definition: ssa_expr.h:125
exprt conjunction(const exprt::operandst &op)
1) generates a conjunction for two or more operands 2) for one operand, returns the operand 3) return...
Definition: std_expr.cpp:83
const if_exprt & to_if_expr(const exprt &expr)
Cast an exprt to an if_exprt.
Definition: std_expr.h:2460
const typecast_exprt & to_typecast_expr(const exprt &expr)
Cast an exprt to a typecast_exprt.
Definition: std_expr.h:2107
const complex_real_exprt & to_complex_real_expr(const exprt &expr)
Cast an exprt to a complex_real_exprt.
Definition: std_expr.h:2010
const struct_exprt & to_struct_expr(const exprt &expr)
Cast an exprt to a struct_exprt.
Definition: std_expr.h:1900
const member_exprt & to_member_expr(const exprt &expr)
Cast an exprt to a member_exprt.
Definition: std_expr.h:2946
const complex_imag_exprt & to_complex_imag_expr(const exprt &expr)
Cast an exprt to a complex_imag_exprt.
Definition: std_expr.h:2053
const index_exprt & to_index_expr(const exprt &expr)
Cast an exprt to an index_exprt.
Definition: std_expr.h:1538
const struct_typet & to_struct_type(const typet &type)
Cast a typet to a struct_typet.
Definition: std_types.h:308
const complex_typet & to_complex_type(const typet &type)
Cast a typet to a complex_typet.
Definition: std_types.h:1155
const struct_tag_typet & to_struct_tag_type(const typet &type)
Cast a typet to a struct_tag_typet.
Definition: std_types.h:518
Assignment from the rhs value to the lhs variable.
ssa_exprt lhs
expr_skeletont original_lhs_skeleton
Skeleton to reconstruct the original lhs in the assignment.
bool allow_pointer_unsoundness
Definition: symex_config.h:27
bool constant_propagation
Definition: symex_config.h:29
bool run_validation_checks
Should the additional validation checks be run? If this flag is set the checks for renaming (both lev...
Definition: symex_config.h:42
constexpr bool use_update()
static bool is_string_constant_initialization(const exprt &rhs)
Determine whether the RHS expression is a string constant initialization.
Symbolic Execution of assignments.
Symbolic Execution.