-
Notifications
You must be signed in to change notification settings - Fork 273
/
Copy pathgoto_symex_state.h
276 lines (226 loc) · 9.21 KB
/
goto_symex_state.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
/*******************************************************************\
Module: Symbolic Execution
Author: Daniel Kroening, [email protected]
\*******************************************************************/
/// \file
/// Symbolic Execution
#ifndef CPROVER_GOTO_SYMEX_GOTO_SYMEX_STATE_H
#define CPROVER_GOTO_SYMEX_GOTO_SYMEX_STATE_H
#include <util/invariant.h>
#include <util/ssa_expr.h>
#include <util/std_expr.h>
#include <util/symbol_table.h>
#include <analyses/guard.h>
#include "call_stack.h"
#include "field_sensitivity.h"
#include "goto_state.h"
#include "renaming_level.h"
#include "shadow_memory_state.h"
#include <functional>
#include <memory>
class incremental_dirtyt;
class symex_target_equationt;
/// \brief Central data structure: state.
///
/// The state is a persistent data structure that symex maintains as it
/// executes. As we walk over each instruction, state will be updated reflecting
/// their effects until a branch occurs (such as an if), where parts of the
/// state will be copied into a \ref goto_statet, stored in a map for later
/// reference and then merged again (via merge_goto) once it reaches a
/// control-flow graph convergence.
class goto_symex_statet final : public goto_statet
{
public:
goto_symex_statet(
const symex_targett::sourcet &,
std::size_t max_field_sensitive_array_size,
bool should_simplify,
guard_managert &manager,
std::function<std::size_t(const irep_idt &)> fresh_l2_name_provider);
~goto_symex_statet();
/// \brief Fake "copy constructor" that initializes the `symex_target` member
explicit goto_symex_statet(
const goto_symex_statet &other,
symex_target_equationt *const target)
: goto_symex_statet(other) // NOLINT
{
symex_target = target;
}
symex_targett::sourcet source;
/// contains symbols that are minted during symbolic execution, such as
/// dynamically created objects etc. The names in this table are needed
/// for error traces even after symbolic execution has finished.
symbol_tablet symbol_table;
// Manager is required to be able to resize the thread vector
guard_managert &guard_manager;
symex_target_equationt *symex_target;
/// Rewrites symbol expressions in \ref exprt, applying a suffix to each
/// symbol reflecting its most recent version, which differs depending on
/// which level you requested. Each level also updates its predecessors, so
/// a L1 rename will update L1 and L0. A L2 will update L2, L1 and L0.
///
/// What happens at each level:
/// L0. Applies a suffix giving the current thread number. (Excludes
/// guards, dynamic objects and anything not considered thread-local)
/// L1. Applies a suffix giving the current loop iteration or recursive
/// function invocation.
/// L2. Applies a suffix giving the generation of this variable.
///
/// Renaming will not increment any of these values, just update the
/// expression with them. Levels matter when reading a variable, for
/// example: reading the value of x really means reading the particular x
/// symbol for this thread (L0 renaming, if applicable), the most recent
/// instance of x (L1 renaming), and the most recent write to x (L2 renaming).
///
/// The above example after being renamed could look like this: 'x!0@0#42'.
/// That states it's the 42nd generation of this variable, on the first
/// thread, in the first frame.
///
/// A full explanation of SSA (which is why we do this renaming) is in
/// the SSA section of background-concepts.md.
template <levelt level = L2>
[[nodiscard]] renamedt<exprt, level> rename(exprt expr, const namespacet &ns);
/// Version of rename which is specialized for SSA exprt.
/// Implementation only exists for level L0 and L1.
template <levelt level>
[[nodiscard]] renamedt<ssa_exprt, level>
rename_ssa(ssa_exprt ssa, const namespacet &ns);
template <levelt level = L2>
void rename(typet &type, const irep_idt &l1_identifier, const namespacet &ns);
[[nodiscard]] exprt l2_rename_rvalues(exprt lvalue, const namespacet &ns);
/// \return lhs renamed to level 2
[[nodiscard]] renamedt<ssa_exprt, L2> assignment(
ssa_exprt lhs, // L0/L1
const exprt &rhs, // L2
const namespacet &ns,
bool rhs_is_simplified,
bool record_value,
bool allow_pointer_unsoundness = false);
field_sensitivityt field_sensitivity;
shadow_memory_statet shadow_memory;
protected:
template <levelt>
void rename_address(exprt &expr, const namespacet &ns);
/// Update values up to \c level.
template <levelt level>
[[nodiscard]] renamedt<ssa_exprt, level>
set_indices(ssa_exprt expr, const namespacet &ns);
// this maps L1 names to (L2) types
typedef std::unordered_map<irep_idt, typet> l1_typest;
l1_typest l1_types;
public:
// guards
static irep_idt guard_identifier()
{
static irep_idt id = "goto_symex::\\guard";
return id;
}
call_stackt &call_stack()
{
PRECONDITION(source.thread_nr < threads.size());
return threads[source.thread_nr].call_stack;
}
const call_stackt &call_stack() const
{
PRECONDITION(source.thread_nr < threads.size());
return threads[source.thread_nr].call_stack;
}
/// Instantiate the object \p expr. An instance of an object is an L1-renamed
/// SSA expression such that its L1-index has not previously been used.
/// \param expr: Symbol to be instantiated
/// \param index_generator: A function to produce a new index for a given name
/// \param ns: A namespace
/// \return L1-renamed SSA expression
ssa_exprt add_object(
const symbol_exprt &expr,
std::function<std::size_t(const irep_idt &)> index_generator,
const namespacet &ns);
/// Add `invalid` (or a failed symbol) to the value_set if ssa is a pointer,
/// ensure that level2 index of symbols in fields of ssa are at 1,
/// and rename ssa to level 2
/// \return ssa renamed to level 2
ssa_exprt declare(ssa_exprt ssa, const namespacet &ns);
void print_backtrace(std::ostream &) const;
// threads
typedef std::pair<unsigned, std::list<guardt> > a_s_r_entryt;
typedef std::list<guardt> a_s_w_entryt;
std::unordered_map<ssa_exprt, a_s_r_entryt, irep_hash> read_in_atomic_section;
std::unordered_map<ssa_exprt, a_s_w_entryt, irep_hash>
written_in_atomic_section;
struct threadt
{
goto_programt::const_targett pc;
irep_idt function_id;
guardt guard;
call_stackt call_stack;
symex_level1t level1;
std::map<irep_idt, unsigned> function_frame;
unsigned atomic_section_id = 0;
explicit threadt(guard_managert &guard_manager)
: guard(true_exprt(), guard_manager)
{
}
};
std::vector<threadt> threads;
enum class write_is_shared_resultt
{
NOT_SHARED,
IN_ATOMIC_SECTION,
SHARED
};
bool l2_thread_read_encoding(ssa_exprt &expr, const namespacet &ns);
bool l2_thread_write_encoding(const ssa_exprt &expr, const namespacet &ns);
write_is_shared_resultt
write_is_shared(const ssa_exprt &expr, const namespacet &ns) const;
std::stack<bool> record_events;
const incremental_dirtyt *dirty = nullptr;
goto_programt::const_targett saved_target;
/// \brief This state is saved, with the PC pointing to the target of a GOTO
bool has_saved_jump_target;
/// \brief This state is saved, with the PC pointing to the next instruction
/// of a GOTO
bool has_saved_next_instruction;
/// \brief Should the additional validation checks be run?
bool run_validation_checks;
unsigned total_vccs = 0;
unsigned remaining_vccs = 0;
/// Drops an L1 name from the local L2 map
void drop_existing_l1_name(const irep_idt &l1_identifier)
{
level2.current_names.erase(l1_identifier);
}
/// Drops an L1 name from the local L2 map
void drop_l1_name(const irep_idt &l1_identifier)
{
level2.current_names.erase_if_exists(l1_identifier);
}
std::function<std::size_t(const irep_idt &)> get_l2_name_provider() const
{
return fresh_l2_name_provider;
}
/// Returns true if \p lvalue is a read-only object, such as the null object
static bool is_read_only_object(const exprt &lvalue)
{
// Note ID_constant can occur due to a partial write to a string constant,
// (i.e. something like byte_extract int from "hello" offset 2), which
// simplifies to a plain constant.
return lvalue.id() == ID_string_constant || lvalue.id() == ID_null_object ||
lvalue.id() == "zero_string" || lvalue.id() == "is_zero_string" ||
lvalue.id() == "zero_string_length" || lvalue.is_constant() ||
lvalue.id() == ID_array;
}
private:
std::function<std::size_t(const irep_idt &)> fresh_l2_name_provider;
/// \brief Dangerous, do not use
///
/// Copying a state S1 to S2 risks S2 pointing to a deallocated
/// symex_target_equationt if S1 (and the symex_target_equationt that its
/// `symex_target` member points to) go out of scope. If your class has a
/// goto_symex_statet member and needs a copy constructor, copy instances
/// of this class using the public two-argument copy constructor
/// constructor to ensure that the copy points to an allocated
/// symex_target_equationt. The two-argument copy constructor uses this
/// private copy constructor as a delegate.
goto_symex_statet(const goto_symex_statet &other) = default;
};
#endif // CPROVER_GOTO_SYMEX_GOTO_SYMEX_STATE_H