cprover
symex_function_call.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Symbolic Execution of ANSI-C
4 
5 Author: Daniel Kroening, kroening@kroening.com
6 
7 \*******************************************************************/
8 
11 
12 #include "goto_symex.h"
13 
14 #include <util/arith_tools.h>
15 #include <util/byte_operators.h>
16 #include <util/c_types.h>
17 #include <util/exception_utils.h>
18 #include <util/fresh_symbol.h>
19 #include <util/invariant.h>
20 #include <util/prefix.h>
21 #include <util/range.h>
22 
23 #include "expr_skeleton.h"
24 #include "path_storage.h"
25 #include "symex_assign.h"
26 
27 static void locality(
28  const irep_idt &function_identifier,
29  goto_symext::statet &state,
30  path_storaget &path_storage,
31  const goto_functionst::goto_functiont &goto_function,
32  const namespacet &ns);
33 
34 bool goto_symext::get_unwind_recursion(const irep_idt &, unsigned, unsigned)
35 {
36  return false;
37 }
38 
40  const irep_idt &function_identifier,
41  const goto_functionst::goto_functiont &goto_function,
42  statet &state,
43  const exprt::operandst &arguments)
44 {
45  // iterates over the arguments
46  exprt::operandst::const_iterator it1=arguments.begin();
47 
48  // iterates over the types of the parameters
49  for(const auto &identifier : goto_function.parameter_identifiers)
50  {
51  INVARIANT(
52  !identifier.empty(), "function parameter must have an identifier");
53  state.call_stack().top().parameter_names.push_back(identifier);
54 
55  const symbolt &symbol=ns.lookup(identifier);
56  symbol_exprt lhs=symbol.symbol_expr();
57 
58  // this is the type that the n-th argument should have
59  const typet &parameter_type = symbol.type;
60 
61  exprt rhs;
62 
63  // if you run out of actual arguments there was a mismatch
64  if(it1==arguments.end())
65  {
66  log.warning() << state.source.pc->source_location.as_string()
67  << ": "
68  "call to '"
69  << id2string(function_identifier)
70  << "': "
71  "not enough arguments, inserting non-deterministic value"
72  << log.eom;
73 
75  parameter_type, state.source.pc->source_location);
76  }
77  else
78  rhs=*it1;
79 
80  if(rhs.is_nil())
81  {
82  // 'nil' argument doesn't get assigned
83  }
84  else
85  {
86  // It should be the same exact type.
87  if(parameter_type != rhs.type())
88  {
89  const typet &rhs_type = rhs.type();
90 
91  // But we are willing to do some limited conversion.
92  // This is highly dubious, obviously.
93  // clang-format off
94  if(
95  (parameter_type.id() == ID_signedbv ||
96  parameter_type.id() == ID_unsignedbv ||
97  parameter_type.id() == ID_c_enum_tag ||
98  parameter_type.id() == ID_bool ||
99  parameter_type.id() == ID_pointer ||
100  parameter_type.id() == ID_union ||
101  parameter_type.id() == ID_union_tag) &&
102  (rhs_type.id() == ID_signedbv ||
103  rhs_type.id() == ID_unsignedbv ||
104  rhs_type.id() == ID_c_bit_field ||
105  rhs_type.id() == ID_c_enum_tag ||
106  rhs_type.id() == ID_bool ||
107  rhs_type.id() == ID_pointer ||
108  rhs_type.id() == ID_union ||
109  rhs_type.id() == ID_union_tag))
110  // clang-format on
111  {
112  rhs = make_byte_extract(
113  rhs, from_integer(0, index_type()), parameter_type);
114  }
115  else
116  {
117  std::ostringstream error;
118  error << state.source.pc->source_location.as_string() << ": "
119  << "function call: parameter \"" << identifier
120  << "\" type mismatch:\ngot " << rhs.type().pretty()
121  << "\nexpected " << parameter_type.pretty();
122  throw unsupported_operation_exceptiont(error.str());
123  }
124  }
125 
126  assignment_typet assignment_type;
127 
128  // We hide if we are in a hidden function.
129  if(state.call_stack().top().hidden_function)
130  assignment_type =
132  else
133  assignment_type =
135 
136  lhs = to_symbol_expr(clean_expr(std::move(lhs), state, true));
137  rhs = clean_expr(std::move(rhs), state, false);
138 
139  exprt::operandst lhs_conditions;
140  symex_assignt{state, assignment_type, ns, symex_config, target}
141  .assign_rec(lhs, expr_skeletont{}, rhs, lhs_conditions);
142  }
143 
144  if(it1!=arguments.end())
145  it1++;
146  }
147 
148  if(to_code_type(ns.lookup(function_identifier).type).has_ellipsis())
149  {
150  // These are va_arg arguments; their types may differ from call to call
151  for(; it1 != arguments.end(); it1++)
152  {
153  symbolt &va_arg = get_fresh_aux_symbol(
154  it1->type(),
155  id2string(function_identifier),
156  "va_arg",
157  state.source.pc->source_location,
158  ns.lookup(function_identifier).mode,
159  state.symbol_table);
160  va_arg.is_parameter = true;
161 
162  state.call_stack().top().parameter_names.push_back(va_arg.name);
163 
164  symex_assign(state, va_arg.symbol_expr(), *it1);
165  }
166  }
167  else if(it1!=arguments.end())
168  {
169  // we got too many arguments, but we will just ignore them
170  }
171 }
172 
174  const get_goto_functiont &get_goto_function,
175  statet &state,
176  const code_function_callt &code)
177 {
178  const exprt &function=code.function();
179 
180  // If at some point symex_function_call can support more
181  // expression ids(), like ID_Dereference, please expand the
182  // precondition appropriately.
183  PRECONDITION(function.id() == ID_symbol);
185 }
186 
188  const get_goto_functiont &get_goto_function,
189  statet &state,
190  const code_function_callt &original_code)
191 {
192  code_function_callt code = original_code;
193 
194  if(code.lhs().is_not_nil())
195  code.lhs() = clean_expr(std::move(code.lhs()), state, true);
196 
197  code.function() = clean_expr(std::move(code.function()), state, false);
198 
199  for(auto &argument : code.arguments())
200  argument = clean_expr(std::move(argument), state, false);
201 
202  target.location(state.guard.as_expr(), state.source);
203 
204  PRECONDITION(code.function().id() == ID_symbol);
205 
206  const irep_idt &identifier=
208 
209  if(has_prefix(id2string(identifier), CPROVER_FKT_PREFIX))
210  {
211  symex_fkt(state, code);
212  }
213  else
215 }
216 
218  const get_goto_functiont &get_goto_function,
219  statet &state,
220  const code_function_callt &call)
221 {
222  const irep_idt &identifier=
224 
225  const goto_functionst::goto_functiont &goto_function =
226  get_goto_function(identifier);
227 
228  path_storage.dirty.populate_dirty_for_function(identifier, goto_function);
229 
230  auto emplace_safe_pointers_result =
231  path_storage.safe_pointers.emplace(identifier, local_safe_pointerst{});
232  if(emplace_safe_pointers_result.second)
233  emplace_safe_pointers_result.first->second(goto_function.body);
234 
235  const bool stop_recursing = get_unwind_recursion(
236  identifier,
237  state.source.thread_nr,
238  state.call_stack().top().loop_iterations[identifier].count);
239 
240  // see if it's too much
241  if(stop_recursing)
242  {
244  {
245  // it's ok, ignore
246  }
247  else
248  {
250  vcc(false_exprt(), "recursion unwinding assertion", state);
251 
252  // Rule out this path:
253  symex_assume_l2(state, false_exprt());
254  }
255 
256  symex_transition(state);
257  return;
258  }
259 
260  // read the arguments -- before the locality renaming
261  const exprt::operandst &arguments = call.arguments();
262  const std::vector<renamedt<exprt, L2>> renamed_arguments =
263  make_range(arguments).map(
264  [&](const exprt &a) { return state.rename(a, ns); });
265 
266  // we hide the call if the caller and callee are both hidden
267  const bool hidden =
268  state.call_stack().top().hidden_function && goto_function.is_hidden();
269 
270  // record the call
272  state.guard.as_expr(), identifier, renamed_arguments, state.source, hidden);
273 
274  if(!goto_function.body_available())
275  {
276  no_body(identifier);
277 
278  // record the return
280  state.guard.as_expr(), identifier, state.source, hidden);
281 
282  if(call.lhs().is_not_nil())
283  {
284  const auto rhs =
286  symex_assign(state, call.lhs(), rhs);
287  }
288 
290  {
291  // assign non det to function arguments if pointers
292  // are not const
293  for(const auto &arg : call.arguments())
294  {
295  if(
296  arg.type().id() == ID_pointer &&
297  !arg.type().subtype().get_bool(ID_C_constant) &&
298  arg.type().subtype().id() != ID_code)
299  {
300  exprt object = dereference_exprt(arg, arg.type().subtype());
301  exprt cleaned_object = clean_expr(object, state, true);
302  const guardt guard(true_exprt(), state.guard_manager);
303  havoc_rec(state, guard, cleaned_object);
304  }
305  }
306  }
307 
308  symex_transition(state);
309  return;
310  }
311 
312  // produce a new frame
313  PRECONDITION(!state.call_stack().empty());
314  framet &frame = state.call_stack().new_frame(state.source, state.guard);
315 
316  // Only enable loop analysis when complexity is enabled.
318  {
319  // Analyzes loops if required.
320  path_storage.add_function_loops(identifier, goto_function.body);
321  frame.loops_info = path_storage.get_loop_analysis(identifier);
322  }
323 
324  // preserve locality of local variables
325  locality(identifier, state, path_storage, goto_function, ns);
326 
327  // assign actuals to formal parameters
328  parameter_assignments(identifier, goto_function, state, arguments);
329 
330  frame.end_of_function=--goto_function.body.instructions.end();
331  frame.return_value=call.lhs();
332  frame.function_identifier=identifier;
333  frame.hidden_function = goto_function.is_hidden();
334 
335  const framet &p_frame = state.call_stack().previous_frame();
336  for(const auto &pair : p_frame.loop_iterations)
337  {
338  if(pair.second.is_recursion)
339  frame.loop_iterations.insert(pair);
340  }
341 
342  // increase unwinding counter
343  frame.loop_iterations[identifier].is_recursion=true;
344  frame.loop_iterations[identifier].count++;
345 
346  state.source.function_id = identifier;
347  symex_transition(state, goto_function.body.instructions.begin(), false);
348 }
349 
351 static void pop_frame(
352  goto_symext::statet &state,
353  const path_storaget &path_storage,
354  bool doing_path_exploration)
355 {
356  PRECONDITION(!state.call_stack().empty());
357 
358  const framet &frame = state.call_stack().top();
359 
360  // restore program counter
361  symex_transition(state, frame.calling_location.pc, false);
363 
364  // restore L1 renaming
365  state.level1.restore_from(frame.old_level1);
366 
367  // If the program is multi-threaded then the state guard is used to
368  // accumulate assumptions (in symex_assume_l2) and must be left alone.
369  // If however it is single-threaded then we should restore the guard, as the
370  // guard coming out of the function may be more complex (e.g. if the callee
371  // was { if(x) while(true) { } } then the guard may still be `!x`),
372  // but at this point all control-flow paths have either converged or been
373  // proven unviable, so we can stop specifying the callee's constraints when
374  // we generate an assumption or VCC.
375 
376  // If we're doing path exploration then we do tail-duplication, and we
377  // actually *are* in a more-restricted context than we were when the
378  // function began.
379  if(state.threads.size() == 1 && !doing_path_exploration)
380  {
381  state.guard = frame.guard_at_function_start;
382  }
383 
384  for(const irep_idt &l1_o_id : frame.local_objects)
385  {
386  const auto l2_entry_opt = state.get_level2().current_names.find(l1_o_id);
387 
388  if(
389  l2_entry_opt.has_value() &&
390  (state.threads.size() == 1 ||
391  !path_storage.dirty(l2_entry_opt->get().first.get_object_name())))
392  {
393  state.drop_existing_l1_name(l1_o_id);
394  }
395  }
396 
397  state.call_stack().pop();
398 }
399 
402 {
403  const bool hidden = state.call_stack().top().hidden_function;
404 
405  // first record the return
407  state.guard.as_expr(), state.source.function_id, state.source, hidden);
408 
409  // then get rid of the frame
411 }
412 
415 static void locality(
416  const irep_idt &function_identifier,
417  goto_symext::statet &state,
418  path_storaget &path_storage,
419  const goto_functionst::goto_functiont &goto_function,
420  const namespacet &ns)
421 {
422  unsigned &frame_nr=
423  state.threads[state.source.thread_nr].function_frame[function_identifier];
424  frame_nr++;
425 
426  for(const auto &param : goto_function.parameter_identifiers)
427  {
428  (void)state.add_object(
429  ns.lookup(param).symbol_expr(),
430  [&path_storage, &frame_nr](const irep_idt &l0_name) {
431  return path_storage.get_unique_l1_index(l0_name, frame_nr);
432  },
433  ns);
434  }
435 }
constant_exprt from_integer(const mp_integer &int_value, const typet &type)
Definition: arith_tools.cpp:99
Expression classes for byte-level operators.
bitvector_typet index_type()
Definition: c_types.cpp:16
void pop()
Definition: call_stack.h:36
framet & new_frame(symex_targett::sourcet calling_location, const guardt &guard)
Definition: call_stack.h:30
const framet & previous_frame()
Definition: call_stack.h:42
framet & top()
Definition: call_stack.h:17
codet representation of a function call statement.
Definition: std_code.h:1213
exprt & function()
Definition: std_code.h:1248
argumentst & arguments()
Definition: std_code.h:1258
bool has_ellipsis() const
Definition: std_types.h:611
Operator to dereference a pointer.
Definition: pointer_expr.h:628
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition: dstring.h:37
Expression in which some part is missing and can be substituted for another expression.
Definition: expr_skeleton.h:26
Base class for all expressions.
Definition: expr.h:54
std::vector< exprt > operandst
Definition: expr.h:56
const source_locationt & source_location() const
Definition: expr.h:230
typet & type()
Return the type of the expression.
Definition: expr.h:82
The Boolean constant false.
Definition: std_expr.h:2811
::goto_functiont goto_functiont
guardt guard
Definition: goto_state.h:58
const symex_level2t & get_level2() const
Definition: goto_state.h:45
Central data structure: state.
ssa_exprt add_object(const symbol_exprt &expr, std::function< std::size_t(const irep_idt &)> index_generator, const namespacet &ns)
Instantiate the object expr.
NODISCARD 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...
call_stackt & call_stack()
symex_level1t level1
guard_managert & guard_manager
symbol_tablet symbol_table
contains symbols that are minted during symbolic execution, such as dynamically created objects etc.
symex_targett::sourcet source
std::vector< threadt > threads
void drop_existing_l1_name(const irep_idt &l1_identifier)
Drops an L1 name from the local L2 map.
virtual void no_body(const irep_idt &identifier)
Log a warning that a function has no body.
Definition: goto_symex.h:424
virtual void vcc(const exprt &, const std::string &msg, statet &)
Definition: symex_main.cpp:185
static get_goto_functiont get_goto_function(abstract_goto_modelt &goto_model)
Return a function to get/load a goto function from the given goto model Create a default delegate to ...
Definition: symex_main.cpp:493
virtual void symex_function_call_symbol(const get_goto_functiont &get_goto_function, statet &state, const code_function_callt &code)
Symbolic execution of a call to a function call.
path_storaget & path_storage
Symbolic execution paths to be resumed later.
Definition: goto_symex.h:789
void havoc_rec(statet &state, const guardt &guard, const exprt &dest)
Definition: symex_other.cpp:19
symex_target_equationt & target
The equation that this execution is building up.
Definition: goto_symex.h:251
virtual bool get_unwind_recursion(const irep_idt &identifier, unsigned thread_nr, unsigned unwind)
void parameter_assignments(const irep_idt &function_identifier, const goto_functionst::goto_functiont &goto_function, statet &state, const exprt::operandst &arguments)
Iterates over arguments and assigns them to the parameters, which are symbols whose name and type are...
namespacet ns
Initialized just before symbolic execution begins, to point to both outer_symbol_table and the symbol...
Definition: goto_symex.h:243
virtual void symex_end_of_function(statet &)
Symbolically execute a END_FUNCTION instruction.
virtual void symex_fkt(statet &state, const code_function_callt &code)
Symbolically execute a FUNCTION_CALL instruction for a function whose name starts with CPROVER_FKT_PR...
exprt clean_expr(exprt expr, statet &state, bool write)
Clean up an expression.
void symex_assign(statet &state, const exprt &lhs, const exprt &rhs)
Symbolically execute an ASSIGN instruction or simulate such an execution for a synthetic assignment.
Definition: goto_symex.cpp:39
virtual void symex_function_call(const get_goto_functiont &get_goto_function, statet &state, const code_function_callt &code)
Symbolically execute a FUNCTION_CALL instruction.
std::function< const goto_functionst::goto_functiont &(const irep_idt &)> get_goto_functiont
The type of delegate functions that retrieve a goto_functiont for a particular function identifier.
Definition: goto_symex.h:82
messaget log
The messaget to write log messages to.
Definition: goto_symex.h:263
const symex_configt symex_config
The configuration to use for this symbolic execution.
Definition: goto_symex.h:170
void symex_assume_l2(statet &, const exprt &cond)
Definition: symex_main.cpp:222
virtual void symex_function_call_code(const get_goto_functiont &get_goto_function, statet &state, const code_function_callt &call)
Symbolic execution of a function call by inlining.
exprt as_expr() const
Definition: guard_expr.h:46
void populate_dirty_for_function(const irep_idt &id, const goto_functionst::goto_functiont &function)
Analyse the given function with dirtyt if it hasn't been seen before.
Definition: dirty.cpp:78
std::string pretty(unsigned indent=0, unsigned max_indent=0) const
Definition: irep.cpp:495
bool is_not_nil() const
Definition: irep.h:391
const irep_idt & id() const
Definition: irep.h:407
bool is_nil() const
Definition: irep.h:387
A very simple, cheap analysis to determine when dereference operations are trivially guarded by a che...
mstreamt & warning() const
Definition: message.h:404
static eomt eom
Definition: message.h:297
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition: namespace.h:91
bool lookup(const irep_idt &name, const symbolt *&symbol) const override
See documentation for namespace_baset::lookup().
Definition: namespace.cpp:138
Storage for symbolic execution paths to be resumed later.
Definition: path_storage.h:38
incremental_dirtyt dirty
Local variables are considered 'dirty' if they've had an address taken and therefore may be referred ...
Definition: path_storage.h:116
std::size_t get_unique_l1_index(const irep_idt &id, std::size_t minimum_index)
Provide a unique L1 index for a given id, starting from minimum_index.
Definition: path_storage.h:104
std::shared_ptr< lexical_loopst > get_loop_analysis(const irep_idt &function_id)
Definition: path_storage.h:131
std::unordered_map< irep_idt, local_safe_pointerst > safe_pointers
Map function identifiers to local_safe_pointerst instances.
Definition: path_storage.h:100
void add_function_loops(const irep_idt &identifier, const goto_programt &body)
Generates a loop analysis for the instructions in goto_programt and keys it against function ID.
Definition: path_storage.h:120
optionalt< std::reference_wrapper< const mapped_type > > find(const key_type &k) const
Find element.
Definition: sharing_map.h:1451
A side_effect_exprt that returns a non-deterministically chosen value.
Definition: std_code.h:1966
Expression to hold a symbol (variable)
Definition: std_expr.h:80
const irep_idt & get_identifier() const
Definition: std_expr.h:109
Symbol table entry.
Definition: symbol.h:28
bool is_parameter
Definition: symbol.h:67
class symbol_exprt symbol_expr() const
Produces a symbol_exprt for a symbol.
Definition: symbol.cpp:121
typet type
Type of symbol.
Definition: symbol.h:31
irep_idt name
The unique identifier.
Definition: symbol.h:40
Functor for symex assignment.
Definition: symex_assign.h:26
virtual void function_return(const exprt &guard, const irep_idt &function_id, const sourcet &source, bool hidden)
Record return from a function.
virtual void location(const exprt &guard, const sourcet &source)
Record a location.
virtual void function_call(const exprt &guard, const irep_idt &function_id, const std::vector< renamedt< exprt, L2 >> &ssa_function_arguments, const sourcet &source, bool hidden)
Record a function call.
The Boolean constant true.
Definition: std_expr.h:2802
The type of an expression, extends irept.
Definition: type.h:28
Thrown when we encounter an instruction, parameters to an instruction etc.
bool has_prefix(const std::string &s, const std::string &prefix)
Definition: converter.cpp:13
#define CPROVER_FKT_PREFIX
Expression skeleton.
symbolt & get_fresh_aux_symbol(const typet &type, const std::string &name_prefix, const std::string &basename_prefix, const source_locationt &source_location, const irep_idt &symbol_mode, const namespacet &ns, symbol_table_baset &symbol_table)
Installs a fresh-named symbol with respect to the given namespace ns with the requested name pattern ...
Fresh auxiliary symbol creation.
Symbolic Execution.
void symex_transition(goto_symext::statet &state)
Transition to the next instruction, which increments the internal program counter and initializes the...
Definition: symex_main.cpp:150
const std::string & id2string(const irep_idt &d)
Definition: irep.h:49
Storage of symbolic execution paths to resume.
Ranges: pair of begin and end iterators, which can be initialized from containers,...
ranget< iteratort > make_range(iteratort begin, iteratort end)
Definition: range.h:524
#define PRECONDITION(CONDITION)
Definition: invariant.h:463
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition: std_expr.h:189
const code_typet & to_code_type(const typet &type)
Cast a typet to a code_typet.
Definition: std_types.h:744
Stack frames – these are used for function calls and for exceptions.
Definition: frame.h:21
guardt guard_at_function_start
Definition: frame.h:31
exprt return_value
Definition: frame.h:33
symex_targett::sourcet calling_location
Definition: frame.h:29
std::unordered_map< irep_idt, loop_infot > loop_iterations
Definition: frame.h:71
bool hidden_function
Definition: frame.h:34
std::vector< irep_idt > parameter_names
Definition: frame.h:30
symex_level1t old_level1
Definition: frame.h:36
goto_programt::const_targett end_of_function
Definition: frame.h:32
std::set< irep_idt > local_objects
Definition: frame.h:38
irep_idt function_identifier
Definition: frame.h:27
std::shared_ptr< lexical_loopst > loops_info
Definition: frame.h:68
bool partial_loops
Definition: symex_config.h:35
bool complexity_limits_active
Whether this run of symex is under complexity limits.
Definition: symex_config.h:56
bool unwinding_assertions
Definition: symex_config.h:33
bool havoc_undefined_functions
Definition: symex_config.h:37
bool doing_path_exploration
Definition: symex_config.h:23
void restore_from(const symex_level1t &other)
Insert the content of other into this renaming.
symex_renaming_levelt current_names
goto_programt::const_targett pc
Definition: symex_target.h:42
Symbolic Execution of assignments.
static void locality(const irep_idt &function_identifier, goto_symext::statet &state, path_storaget &path_storage, const goto_functionst::goto_functiont &goto_function, const namespacet &ns)
Preserves locality of parameters of a given function by applying L1 renaming to them.
static void pop_frame(goto_symext::statet &state, const path_storaget &path_storage, bool doing_path_exploration)
pop one call frame
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.