CBMC
Loading...
Searching...
No Matches
symex_function_call.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: Symbolic Execution of ANSI-C
4
5Author: 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>
18#include <util/fresh_symbol.h>
19#include <util/invariant.h>
20#include <util/range.h>
21#include <util/std_code.h>
22
23#include "expr_skeleton.h"
24#include "path_storage.h"
25#include "symex_assign.h"
26
27bool goto_symext::get_unwind_recursion(const irep_idt &, unsigned, unsigned)
28{
29 return false;
30}
31
33 const irep_idt &function_identifier,
34 const goto_functionst::goto_functiont &goto_function,
35 statet &state,
36 const exprt::operandst &arguments)
37{
38 // iterates over the arguments
39 exprt::operandst::const_iterator it1=arguments.begin();
40
41 // iterates over the types of the parameters
42 for(const auto &identifier : goto_function.parameter_identifiers)
43 {
45 !identifier.empty(), "function parameter must have an identifier");
46 state.call_stack().top().parameter_names.push_back(identifier);
47
48 const symbolt &symbol=ns.lookup(identifier);
49 symbol_exprt lhs=symbol.symbol_expr();
50
51 // this is the type that the n-th argument should have
52 const typet &parameter_type = symbol.type;
53
54 exprt rhs;
55
56 // if you run out of actual arguments there was a mismatch
57 if(it1==arguments.end())
58 {
59 log.warning() << state.source.pc->source_location().as_string()
60 << ": "
61 "call to '"
62 << id2string(function_identifier)
63 << "': "
64 "not enough arguments, inserting non-deterministic value"
65 << log.eom;
66
68 parameter_type, state.source.pc->source_location());
69 }
70 else
71 rhs=*it1;
72
73 if(rhs.is_nil())
74 {
75 // 'nil' argument doesn't get assigned
76 }
77 else
78 {
79 // It should be the same exact type.
80 if(parameter_type != rhs.type())
81 {
82 const typet &rhs_type = rhs.type();
83
84 // But we are willing to do some limited conversion.
85 // This is highly dubious, obviously.
86 // clang-format off
87 if(
88 (parameter_type.id() == ID_signedbv ||
91 parameter_type.id() == ID_bool ||
92 parameter_type.id() == ID_pointer ||
93 parameter_type.id() == ID_union ||
95 (rhs_type.id() == ID_signedbv ||
96 rhs_type.id() == ID_unsignedbv ||
97 rhs_type.id() == ID_c_bit_field ||
98 rhs_type.id() == ID_c_enum_tag ||
99 rhs_type.id() == ID_bool ||
100 rhs_type.id() == ID_pointer ||
101 rhs_type.id() == ID_union ||
102 rhs_type.id() == ID_union_tag))
103 // clang-format on
104 {
105 rhs = make_byte_extract(
107 }
108 else
109 {
110 std::ostringstream error;
111 error << state.source.pc->source_location().as_string() << ": "
112 << "function call: parameter \"" << identifier
113 << "\" type mismatch:\ngot " << rhs.type().pretty()
114 << "\nexpected " << parameter_type.pretty();
115 throw unsupported_operation_exceptiont(error.str());
116 }
117 }
118
119 assignment_typet assignment_type;
120
121 // We hide if we are in a hidden function.
122 if(state.call_stack().top().hidden_function)
123 assignment_type =
125 else
126 assignment_type =
128
129 lhs = to_symbol_expr(clean_expr(std::move(lhs), state, true));
130 rhs = clean_expr(std::move(rhs), state, false);
131
135 state,
136 assignment_type,
137 ns,
140 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 {
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 goto_programt::instructiont &instruction)
177{
178 const exprt &function = instruction.call_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);
184
187 state,
188 instruction.call_lhs(),
189 to_symbol_expr(instruction.call_function()),
190 instruction.call_arguments());
191}
192
194 const get_goto_functiont &get_goto_function,
195 statet &state,
196 const exprt &lhs,
197 const symbol_exprt &function,
198 const exprt::operandst &arguments)
199{
201
202 if(lhs.is_nil())
203 cleaned_lhs = lhs;
204 else
205 cleaned_lhs = clean_expr(lhs, state, true);
206
207 // no need to clean the function, which is a symbol only
208
210
211 for(auto &argument : arguments)
212 cleaned_arguments.push_back(clean_expr(argument, state, false));
213
214 target.location(state.guard.as_expr(), state.source);
215
216 PRECONDITION(function.id() == ID_symbol);
217 const irep_idt &identifier = to_symbol_expr(function).get_identifier();
218
220 {
222 symex_transition(state);
223 }
224 else if(identifier == CPROVER_PREFIX SHADOW_MEMORY_SET_FIELD)
225 {
227 symex_transition(state);
228 }
229 else
230 {
233 }
234}
235
237 const get_goto_functiont &get_goto_function,
238 statet &state,
239 const exprt &cleaned_lhs,
240 const symbol_exprt &function,
242{
243 const irep_idt &identifier = function.get_identifier();
244
245 const goto_functionst::goto_functiont &goto_function =
246 get_goto_function(identifier);
247
248 path_storage.dirty.populate_dirty_for_function(identifier, goto_function);
249
251 path_storage.safe_pointers.emplace(identifier, local_safe_pointerst{});
253 emplace_safe_pointers_result.first->second(goto_function.body);
254
256 identifier,
257 state.source.thread_nr,
258 state.call_stack().top().loop_iterations[identifier].count);
259
260 // see if it's too much
262 {
263 if(symex_config.unwinding_assertions)
264 {
265 vcc(
266 false_exprt(),
267 id2string(identifier) + ".recursion",
268 "recursion unwinding assertion",
269 state);
270 }
271 if(!symex_config.partial_loops)
272 {
273 // Rule out this path:
275 }
276
277 symex_transition(state);
278 return;
279 }
280
281 // read the arguments -- before the locality renaming
282 const std::vector<renamedt<exprt, L2>> renamed_arguments =
283 make_range(cleaned_arguments).map([&](const exprt &a) {
284 return state.rename(a, ns);
285 });
286
287 // we hide the call if the caller and callee are both hidden
288 const bool hidden =
289 state.call_stack().top().hidden_function && goto_function.is_hidden();
290
291 // record the call
293 state.guard.as_expr(), identifier, renamed_arguments, state.source, hidden);
294
295 if(!goto_function.body_available())
296 {
297 // create a fatal assertion
298 if(symex_config.unwinding_assertions)
299 {
300 const auto &symbol = ns.lookup(identifier);
301 const std::string property_id =
302 id2string(state.source.pc->source_location().get_function()) +
303 ".no-body." + id2string(identifier);
304 vcc(
305 false_exprt(),
306 property_id,
307 "no body for callee " + id2string(symbol.display_name()),
308 state);
309 }
310
311 // record the return
313 state.guard.as_expr(), identifier, state.source, hidden);
314
315 if(cleaned_lhs.is_not_nil())
316 {
317 const auto rhs = side_effect_expr_nondett(
318 cleaned_lhs.type(), state.source.pc->source_location());
319 symex_assign(state, cleaned_lhs, rhs);
320 }
321
322 symex_transition(state);
323 return;
324 }
325
326 // produce a new frame
327 PRECONDITION(!state.call_stack().empty());
328 framet &frame = state.call_stack().new_frame(state.source, state.guard);
329
330 // Only enable loop analysis when complexity is enabled.
331 if(symex_config.complexity_limits_active)
332 {
333 // Analyzes loops if required.
334 path_storage.add_function_loops(identifier, goto_function.body);
335 frame.loops_info = path_storage.get_loop_analysis(identifier);
336 }
337
338 // preserve locality of local variables
339 locality(identifier, state, goto_function);
340
341 // assign actuals to formal parameters
342 parameter_assignments(identifier, goto_function, state, cleaned_arguments);
343
344 frame.call_lhs = cleaned_lhs;
345 frame.end_of_function = --goto_function.body.instructions.end();
346 frame.function_identifier=identifier;
347 frame.hidden_function = goto_function.is_hidden();
348
349 // set up the 'return value symbol' when needed
350 if(frame.call_lhs.is_not_nil())
351 {
353 "goto_symex::return_value::" + id2string(identifier);
354
356 {
357 const symbolt &function_symbol = ns.lookup(identifier);
359 new_symbol; // these are thread-local and have dynamic lifetime
360 new_symbol.base_name = "return_value";
362 new_symbol.type = to_code_type(function_symbol.type).return_type();
363 new_symbol.mode = function_symbol.mode;
364 state.symbol_table.add(new_symbol);
365 }
366
367 frame.return_value_symbol =
369 }
370
371 const framet &p_frame = state.call_stack().previous_frame();
372 for(const auto &pair : p_frame.loop_iterations)
373 {
374 if(pair.second.is_recursion)
375 frame.loop_iterations.insert(pair);
376 }
377
378 // increase unwinding counter
379 frame.loop_iterations[identifier].is_recursion=true;
380 frame.loop_iterations[identifier].count++;
381
382 state.source.function_id = identifier;
383 symex_transition(state, goto_function.body.instructions.begin(), false);
384}
385
387static void pop_frame(
388 goto_symext::statet &state,
389 const path_storaget &path_storage,
390 bool doing_path_exploration)
391{
392 PRECONDITION(!state.call_stack().empty());
393
394 const framet &frame = state.call_stack().top();
395
396 // restore program counter
397 symex_transition(state, frame.calling_location.pc, false);
399
400 // restore L1 renaming
401 state.level1.restore_from(frame.old_level1);
402
403 // If the program is multi-threaded then the state guard is used to
404 // accumulate assumptions (in symex_assume_l2) and must be left alone.
405 // If however it is single-threaded then we should restore the guard, as the
406 // guard coming out of the function may be more complex (e.g. if the callee
407 // was { if(x) while(true) { } } then the guard may still be `!x`),
408 // but at this point all control-flow paths have either converged or been
409 // proven unviable, so we can stop specifying the callee's constraints when
410 // we generate an assumption or VCC.
411
412 // If we're doing path exploration then we do tail-duplication, and we
413 // actually *are* in a more-restricted context than we were when the
414 // function began.
415 if(state.threads.size() == 1 && !doing_path_exploration)
416 {
417 state.guard = frame.guard_at_function_start;
418 }
419
420 for(const irep_idt &l1_o_id : frame.local_objects)
421 {
422 const auto l2_entry_opt = state.get_level2().current_names.find(l1_o_id);
423
424 if(
425 l2_entry_opt.has_value() &&
426 (state.threads.size() == 1 ||
427 !path_storage.dirty(l2_entry_opt->get().first.get_object_name())))
428 {
430 }
431 }
432
433 state.call_stack().pop();
434}
435
438{
439 PRECONDITION(!state.call_stack().empty());
440
441 const bool hidden = state.call_stack().top().hidden_function;
442
443 // first record the return
445 state.guard.as_expr(), state.source.function_id, state.source, hidden);
446
447 // before we drop the frame, remember the call LHS
448 // and the return value symbol, if any
449 auto call_lhs = state.call_stack().top().call_lhs;
451
452 // now get rid of the frame
453 pop_frame(state, path_storage, symex_config.doing_path_exploration);
454
455 // after dropping the frame, assign the return value, if any
456 if(state.reachable && call_lhs.is_not_nil())
457 {
459 return_value_symbol.has_value(),
460 "must have return value symbol when assigning call lhs");
461 // the type of the call lhs and the return type might not match
463 return_value_symbol.value(), call_lhs.type());
464 symex_assign(state, call_lhs, casted_return_value);
465 }
466}
467
469 const irep_idt &function_identifier,
470 goto_symext::statet &state,
471 const goto_functionst::goto_functiont &goto_function)
472{
473 unsigned &frame_nr=
474 state.threads[state.source.thread_nr].function_frame[function_identifier];
475 frame_nr++;
476
477 for(const auto &param : goto_function.parameter_identifiers)
478 {
479 const ssa_exprt &renamed_param = state.add_object(
480 ns.lookup(param).symbol_expr(),
481 [this, &frame_nr](const irep_idt &l0_name) {
483 },
484 ns);
485
486 // Allocate shadow memory for parameters.
487 // They are like local variables.
489 }
490}
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.
bitvector_typet c_index_type()
Definition c_types.cpp:16
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
Internally generated symbol table entryThis is a symbol generated as part of translation to or modifi...
Definition symbol.h:153
void pop()
Definition call_stack.h:36
framet & top()
Definition call_stack.h:17
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
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.
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
The Boolean constant false.
Definition std_expr.h:3200
Stack frames – these are used for function calls and for exceptions.
guardt guard_at_function_start
Definition frame.h:36
std::optional< symbol_exprt > return_value_symbol
Definition frame.h:39
exprt call_lhs
Definition frame.h:38
symex_targett::sourcet calling_location
Definition frame.h:34
std::unordered_map< irep_idt, loop_infot > loop_iterations
Definition frame.h:77
bool hidden_function
Definition frame.h:40
std::vector< irep_idt > parameter_names
Definition frame.h:35
symex_level1t old_level1
Definition frame.h:42
goto_programt::const_targett end_of_function
Definition frame.h:37
std::set< irep_idt > local_objects
Definition frame.h:44
irep_idt function_identifier
Definition frame.h:28
std::shared_ptr< lexical_loopst > loops_info
Definition frame.h:74
::goto_functiont goto_functiont
This class represents an instruction in the GOTO intermediate representation.
const exprt & call_lhs() const
Get the lhs of a FUNCTION_CALL (may be nil)
const exprt::operandst & call_arguments() const
Get the arguments of a FUNCTION_CALL.
const exprt & call_function() const
Get the function that is called for FUNCTION_CALL.
guardt guard
Definition goto_state.h:58
bool reachable
Is this code reachable? If not we can take shortcuts such as not entering function calls,...
Definition goto_state.h:62
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.
call_stackt & call_stack()
symex_level1t level1
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...
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 symex_function_call_symbol(const get_goto_functiont &get_goto_function, statet &state, const exprt &lhs, const symbol_exprt &function, const exprt::operandst &arguments)
Symbolic execution of a call to a function call.
irep_idt language_mode
language_mode: ID_java, ID_C or another language identifier if we know the source language in use,...
Definition goto_symex.h:242
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 ...
path_storaget & path_storage
Symbolic execution paths to be resumed later.
Definition goto_symex.h:811
symex_target_equationt & target
The equation that this execution is building up.
Definition goto_symex.h:267
virtual bool get_unwind_recursion(const irep_idt &identifier, unsigned thread_nr, unsigned unwind)
exprt clean_expr(exprt expr, statet &state, bool write)
Clean up an expression.
shadow_memoryt shadow_memory
Shadow memory instrumentation API.
Definition goto_symex.h:840
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:259
virtual void vcc(const exprt &cond, const irep_idt &property_id, const std::string &msg, statet &state)
Symbolically execute a verification condition (assertion).
virtual void symex_end_of_function(statet &)
Symbolically execute a END_FUNCTION instruction.
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.
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:94
virtual void symex_function_call_post_clean(const get_goto_functiont &get_goto_function, statet &state, const exprt &cleaned_lhs, const symbol_exprt &function, const exprt::operandst &cleaned_arguments)
Symbolic execution of a function call by inlining.
messaget log
The messaget to write log messages to.
Definition goto_symex.h:279
const symex_configt symex_config
The configuration to use for this symbolic execution.
Definition goto_symex.h:186
void symex_assume_l2(statet &, const exprt &cond)
virtual void locality(const irep_idt &function_identifier, goto_symext::statet &state, const goto_functionst::goto_functiont &goto_function)
Preserves locality of parameters of a given function by applying L1 renaming to them.
virtual void symex_function_call(const get_goto_functiont &get_goto_function, statet &state, const goto_programt::instructiont &instruction)
Symbolically execute a FUNCTION_CALL instruction.
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:112
std::string pretty(unsigned indent=0, unsigned max_indent=0) const
Definition irep.cpp:482
bool is_not_nil() const
Definition irep.h:372
const irep_idt & id() const
Definition irep.h:388
bool is_nil() const
Definition irep.h:368
A very simple, cheap analysis to determine when dereference operations are trivially guarded by a che...
mstreamt & warning() const
Definition message.h:396
static eomt eom
Definition message.h:289
bool lookup(const irep_idt &name, const symbolt *&symbol) const override
See documentation for namespace_baset::lookup().
Storage for symbolic execution paths to be resumed later.
incremental_dirtyt dirty
Local variables are considered 'dirty' if they've had an address taken and therefore may be referred ...
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.
std::shared_ptr< lexical_loopst > get_loop_analysis(const irep_idt &function_id)
std::unordered_map< irep_idt, local_safe_pointerst > safe_pointers
Map function identifiers to local_safe_pointerst instances.
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.
void symex_get_field(goto_symex_statet &state, const exprt &lhs, const exprt::operandst &arguments)
Symbolically executes a __CPROVER_get_field call.
void symex_field_local_init(goto_symex_statet &state, const ssa_exprt &expr)
Initialize local-scope shadow memory for local variables and parameters.
void symex_set_field(goto_symex_statet &state, const exprt::operandst &arguments)
Symbolically executes a __CPROVER_set_field call.
A side_effect_exprt that returns a non-deterministically chosen value.
Definition std_code.h:1520
Expression providing an SSA-renamed symbol of expressions.
Definition ssa_expr.h:17
Expression to hold a symbol (variable)
Definition std_expr.h:131
const irep_idt & get_identifier() const
Definition std_expr.h:160
bool has_symbol(const irep_idt &name) const
Check whether a symbol exists in the symbol table.
bool add(const symbolt &symbol)
Add a new symbol to the symbol table.
Symbol table entry.
Definition symbol.h:28
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
irep_idt irep_idt base_name
Name of module the symbol belongs to.
Definition symbol.h:46
irep_idt mode
Language mode.
Definition symbol.h:49
Functor for symex assignment.
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.
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
Thrown when we encounter an instruction, parameters to an instruction etc.
#define CPROVER_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...
const std::string & id2string(const irep_idt &d)
Definition irep.h:44
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:522
symbol_exprt return_value_symbol(const irep_idt &identifier, const namespacet &ns)
produces the symbol that is used to store the return value of the function with the given identifier
#define SHADOW_MEMORY_SET_FIELD
#define SHADOW_MEMORY_GET_FIELD
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
Definition invariant.h:534
#define PRECONDITION(CONDITION)
Definition invariant.h:463
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition invariant.h:423
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition std_expr.h:272
const code_typet & to_code_type(const typet &type)
Cast a typet to a code_typet.
Definition std_types.h:788
void restore_from(const symex_level1t &other)
Insert the content of other into this renaming.
goto_programt::const_targett pc
Symbolic Execution of assignments.
static void pop_frame(goto_symext::statet &state, const path_storaget &path_storage, bool doing_path_exploration)
pop one call frame