|
ESBMC
Efficient SMT-based Context-Bounded Model Checker
|
#include <value_set.h>
Classes | |
| class | objectt |
| Record for an object reference. More... | |
| class | object_map_dt |
| struct | entryt |
| Record for a particular value set: stores the identity of the variable that points at this set of objects, and the objects themselves (with associated offset data). More... | |
Public Types | |
| typedef std::set< expr2tc > | expr_sett |
| A type for a set of expressions. | |
| typedef std::unordered_map< unsigned, objectt > | object_mapt |
| Datatype for a value set: stores a mapping between some integers and additional reference data in an objectt object. | |
| typedef persistent_map< irep_idt, entryt, irep_id_hash > | valuest |
| Maps each variable to the entryt holding the set of objects it may point at. | |
Public Member Functions | |
| value_sett (const namespacet &_ns) | |
| Primary constructor. | |
| value_sett (const value_sett &ref) | |
| value_sett & | operator= (const value_sett &ref) |
| unsigned int | get_natural_alignment (const expr2tc &e) const |
| Get the natural alignment unit of a reference to e. | |
| unsigned int | offset2align (const expr2tc &e, const BigInt &m) const |
| expr2tc | to_expr (object_mapt::const_iterator it) const |
| Convert an object map element to an expression. | |
| void | set (object_mapt &dest, object_mapt::const_iterator it) const |
| Insert an object record element into an object map. | |
| bool | insert (object_mapt &dest, object_mapt::const_iterator it) const |
| bool | insert (object_mapt &dest, const expr2tc &src, const BigInt &offset) const |
| bool | insert (object_mapt &dest, unsigned n, const objectt &object) const |
| Insert an object record into the given object map. | |
| bool | insert (object_mapt &dest, const expr2tc &expr, const objectt &object) const |
| bool | erase (const std::string &name) |
| Remove the given pointer value set from the map. | |
| void | erase_object (const std::string &id, const type2tc &type, const std::string &suffix="") |
Remove the value sets of an object of type named id: its own and those of its members and elements, keyed by id plus a .member or [] suffix. | |
| void | get_value_set (const expr2tc &expr, value_setst::valuest &dest) const |
| Get the set of things that an expression might point at. | |
| void | clear () |
| Clear all value records from this value set. | |
| void | add_var (const std::string &id, const std::string &suffix) |
| Add a value set for the given variable name and suffix. | |
| void | add_var (const entryt &e) |
| void | del_var (const std::string &id, const std::string &suffix) |
| Delete the value set for the given variable name and suffix. | |
| void | touch_entry (const entryt &e) |
| Ensure a record exists for the given entry, without changing an existing one. | |
| bool | update_object_map (const entryt &e, const object_mapt &om, bool merge) |
| Overwrite (merge=false) or union-into (merge=true) the object map recorded for the given entry. | |
| void | add_vars (const std::list< entryt > &vars) |
| Add a value set for each variable in the given list. | |
| void | output (std::ostream &out) const |
| Dump the value set's textual representation to the given iostream. | |
| void | dump () const |
| Write a textual representation of the value set to stderr. | |
| bool | make_union (object_mapt &dest, const object_mapt &src) const |
| Join the two given object maps. | |
| bool | make_union (const valuest &new_values, bool keepnew=false) |
| Given another value set tracking object's storage, read all value set records out and merge them into this object's. | |
| bool | make_union (const value_sett &new_values, bool keepnew=false) |
| void | apply_code (const expr2tc &code) |
| When using value_sett for static analysis, takes a code statement and sends any assignments contained within to the assign method. | |
| void | assign (const expr2tc &lhs, const expr2tc &rhs, bool add_to_sets=false) |
| Interpret an assignment, and update value sets to reflect it. | |
| void | assign_struct_union (const expr2tc &lhs, const expr2tc &rhs, const type2tc &lhs_type, bool add_to_sets) |
| assign()'s struct/union arm, split out to keep assign() one dispatch. | |
| void | do_function_call (const symbolt &symbol, const std::vector< expr2tc > &arguments) |
| Interpret a function call during static analysis. | |
| void | do_end_function (const expr2tc &lhs) |
| During static analysis, simulate the return values assignment to the given lhs at the end of the function execution. | |
| void | get_reference_set (const expr2tc &expr, value_setst::valuest &dest) const |
| Determine the set of variables that expr refers to. | |
| void | get_value_set (const expr2tc &expr, object_mapt &dest) const |
| "Internal" get_value_set method. | |
| void | retire_objectless_operand (object_mapt &op0_set, object_mapt &op1_set, object_mapt &dest) const |
| Recursive body of get_value_set. | |
| void | get_value_set_rec_cached (const expr2tc &expr, object_mapt &dest, const std::string &suffix, const type2tc &original_type, bool under_deref=true) const |
| The entry point for the recursive value-set walk: memoises, then calls get_value_set_rec. | |
Static Public Member Functions | |
| static irep_idt | entry_key (const entryt &e) |
| The map key is identifier + suffix. | |
| static std::string | concat_key (const std::string &id, const std::string &suffix) |
| Compose the identifier + suffix map key into a single pre-sized buffer, avoiding the temporary that id2string(id) + suffix allocates for the (less common) array/struct-member pointer case. | |
Public Attributes | |
| unsigned | location_number |
| Location number of the instruction this value set is attached to; used to identify allocation sites for dynamic objects. | |
| valuest | values |
| Storage for all the value sets for all the variables in the program. | |
| const namespacet & | ns |
| Namespace for looking up types against. | |
| irep_idt | xchg_name |
| unsigned long | xchg_num |
Static Public Attributes | |
| static object_numberingt | object_numbering |
| Object to assign numbers to objects – i.e., the numbers in the map of a object_mapt. | |
| static object_number_numberingt | obj_numbering_refset |
Protected Member Functions | |
| std::optional< BigInt > | constant_pointer_arith_offset (const expr2tc &non_ptr_op, const type2tc &subtype, bool subtracting) const |
The byte offset a constant operand of pointer arithmetic contributes, already signed by subtracting; nullopt when the operand is not a compile-time constant or its element size is not statically known. | |
| void | offset_pointer_arith_objects (const object_mapt &pointer_expr_set, const expr2tc &ptr_op, const std::optional< BigInt > &total_offs, object_mapt &dest) const |
Fold total_offs into every object of pointer_expr_set, tracking alignment where the offset or the object's own offset is nondet, and store the results into dest. | |
| bool | get_symbol_value_set (const symbol2t &sym, const expr2tc &expr, const std::string &suffix, const type2tc &original_type, object_mapt &dest) const |
What sym points at: the values keyed under the path the read spells, falling back to the paths a union arm it crosses aliases. | |
| void | get_constant_value_set (const expr2tc &expr, object_mapt &dest, const std::string &suffix, const type2tc &original_type, bool under_deref) const |
| The constant cases of get_value_set_rec: what a value reaches this code as once constant propagation has substituted it. | |
| void | get_constant_struct_value_set (const expr2tc &expr, object_mapt &dest, const std::string &suffix, const type2tc &original_type, bool under_deref) const |
| The two aggregate literals of get_constant_value_set. | |
| void | get_constant_union_value_set (const expr2tc &expr, object_mapt &dest, const std::string &suffix, const type2tc &original_type) const |
| void | get_byte_stitching_value_set (const expr2tc &expr, object_mapt &dest, const std::string &suffix, const type2tc &original_type) const |
| void | get_reference_set (const expr2tc &expr, object_mapt &dest) const |
| Internal get_reference_set method. | |
| void | get_reference_set_rec (const expr2tc &expr, object_mapt &dest) const |
| Recursive implementation of get_reference_set. | |
| void | assign_rec (const expr2tc &lhs, const object_mapt &values_rhs, const std::string &suffix, bool add_to_sets) |
| Recursive assign method implementation – descends through the left hand side looking for symbols to assign values to. | |
| void | do_free (const expr2tc &op) |
| Mark dynamic objects as (possibly) deallocated, and thus invalid. | |
| expr2tc | make_member (const expr2tc &src, const irep_idt &component_name) |
| Attempt to extract the member of an expression statically. | |
Static Protected Member Functions | |
| static void | obj_numbering_ref (unsigned int num) |
| static void | obj_numbering_deref (unsigned int num) |
| typedef std::set<expr2tc> value_sett::expr_sett |
A type for a set of expressions.
| typedef std::unordered_map<unsigned, objectt> value_sett::object_mapt |
Datatype for a value set: stores a mapping between some integers and additional reference data in an objectt object.
The integers are indexes into value_sett::object_numbering, which identifies the l1 variable being referred to.
| typedef persistent_map<irep_idt, entryt, irep_id_hash> value_sett::valuest |
Maps each variable to the entryt holding the set of objects it may point at.
Persistent (structurally shared): symex snapshots the whole map at every goto for the later merge, so a std::unordered_map would copy O(N) per branch and make symex quadratic in the tracked-symbol count on branch-heavy inputs. immer's HAMT makes the snapshot O(1) — the same pattern level1 renaming and guard_seq use.
|
inlineexplicit |
Primary constructor.
Does approximately nothing non-standard.
|
inline |
|
inline |
|
inline |
Add a value set for the given variable name and suffix.
No effect if the given record already exists.
|
inline |
Add a value set for each variable in the given list.
| void value_sett::apply_code | ( | const expr2tc & | code | ) |
When using value_sett for static analysis, takes a code statement and sends any assignments contained within to the assign method.
| code | The statement to interpret. |
Interpret an assignment, and update value sets to reflect it.
| lhs | Assignment target expression. |
| rhs | Assignment expression, to be interpreted, and its pointer records assigned to lhs. |
| add_to_sets | If true, merge the pointer set from rhs into the pointer set for lhs. Otherwise, overwrite it. Used for the static analysis. |
|
protected |
Recursive assign method implementation – descends through the left hand side looking for symbols to assign values to.
| lhs | Left hand side expression that we're assigning value sets to. |
| values_rhs | The value set of the right hand side of the assignment, i.e. all the things the rhs points at. |
| suffix | Accumulated suffix of the lhs up to this point. See docs for entryt and @get_value_set_rec_cached. |
| add_to_sets | See assign. |
|
inline |
Clear all value records from this value set.
|
inlinestatic |
Compose the identifier + suffix map key into a single pre-sized buffer, avoiding the temporary that id2string(id) + suffix allocates for the (less common) array/struct-member pointer case.
|
protected |
The byte offset a constant operand of pointer arithmetic contributes, already signed by subtracting; nullopt when the operand is not a compile-time constant or its element size is not statically known.
|
inline |
Delete the value set for the given variable name and suffix.
| void value_sett::do_end_function | ( | const expr2tc & | lhs | ) |
During static analysis, simulate the return values assignment to the given lhs at the end of the function execution.
| lhs | Variable to take the (pointer) values of the returned value. |
|
protected |
Mark dynamic objects as (possibly) deallocated, and thus invalid.
Something to do with the black magic that allows the static analysis to deal with dynamically allocated memory.
| op | Operand evaluating to the pointer to free. |
| void value_sett::do_function_call | ( | const symbolt & | symbol, |
| const std::vector< expr2tc > & | arguments ) |
Interpret a function call during static analysis.
Looks up the given function, and simulates the assignment of all the arguments to the argument variables in the target function (for pointer tracking).
| function | Symbol of the function to bind arguments into. |
| arguments | Vector of argument expressions, will have their pointer tracking values merged into the corresponding argument variables in the target function. |
| void value_sett::dump | ( | ) | const |
Write a textual representation of the value set to stderr.
The map key is identifier + suffix.
The suffix is empty on the overwhelming majority of calls, so avoid building the concatenated temporary in that case and key directly off the identifier.
|
inline |
Remove the given pointer value set from the map.
| name | The name of the variable, including suffix, to erase. |
| void value_sett::erase_object | ( | const std::string & | id, |
| const type2tc & | type, | ||
| const std::string & | suffix = "" ) |
Remove the value sets of an object of type named id: its own and those of its members and elements, keyed by id plus a .member or [] suffix.
Linear in the type, where scanning the map for id would be linear in every tracked variable.
|
protected |
|
protected |
The two aggregate literals of get_constant_value_set.
Each consumes the one suffix component naming the member it holds, since a literal has no suffixed symbol name for the symbol case to look up.
|
protected |
|
protected |
The constant cases of get_value_set_rec: what a value reaches this code as once constant propagation has substituted it.
Get the natural alignment unit of a reference to e.
I don't know a more appropriate term, but if we were to have an offset into e, then what is the greatest alignment guarentee that would make sense? i.e., an offset of 404 into an array of integers gives an alignment guarentee of 4 bytes, not 404.
For arrays, this is the element size. For structs, I imagine it's the machine word size (?). Depends on padding. For integers / other things, it's the machine / word size (?).
|
inlineprotected |
Internal get_reference_set method.
Just the same as the other get_reference_set method, but collects into an object_mapt instead of a list of exprs.
| expr | The expression to evaluate the reference set of. |
| dest | Destination value set object map to store results into. |
| void value_sett::get_reference_set | ( | const expr2tc & | expr, |
| value_setst::valuest & | dest ) const |
Determine the set of variables that expr refers to.
The difference between this and get_value_set, is that this accumulates the set of variables /used/ in the expression, not the values they might point at. The primary use is, when one uses the address-of operator, and the operand may be a set of things, determine that set of things. An example: baz = &a->foo.bar[0] What does baz point at? It depends on what a points at, and a series of other expressions that must be interpreted. This method performs said interpretation, storing the results in a value set.
| expr | The expression to evaluate the reference set for. |
| dest | The expression list to store the results into. |
|
protected |
Recursive implementation of get_reference_set.
| expr | The (portion of the) expression we're evaluating the reference set of. |
| dest | Destination value set map to store results into. |
|
protected |
What sym points at: the values keyed under the path the read spells, falling back to the paths a union arm it crosses aliases.
Returns whether anything was found.
| void value_sett::get_value_set | ( | const expr2tc & | expr, |
| object_mapt & | dest ) const |
"Internal" get_value_set method.
Just the same as the other get_value_set method, but collects into an object_mapt instead of a list of exprs.
| expr | The expression to evaluate the value set of. |
| dest | Destination value set object map to store the result into. |
| void value_sett::get_value_set | ( | const expr2tc & | expr, |
| value_setst::valuest & | dest ) const |
Get the set of things that an expression might point at.
Interprets the given expression, making note of all the pointer variables used by it, and create a value set of what this expression points at. For example, the expression: (foo + 2) would return the set of all the things 'foo' points at, with the offset records updated to reflect the offset added by the expression. (Although not for records where the offset is nondeterministic.
To be clear: the value set returned reflects the values that the entire expression may evaluate to. That means if you have ((nondet_bool()) ? foo : bar) then you'll get everything both foo and bar point at.
This method also looks up values through dereferences and addresses-of.
| expr | The expression to evaluate and determine the set of values it points to. |
| dest | A list to store pointed-at object expressions into. |
| void value_sett::get_value_set_rec_cached | ( | const expr2tc & | expr, |
| object_mapt & | dest, | ||
| const std::string & | suffix, | ||
| const type2tc & | original_type, | ||
| bool | under_deref = true ) const |
The entry point for the recursive value-set walk: memoises, then calls get_value_set_rec.
Call this rather than the recursion itself – a propagated multi-dimensional array reaches here as a DAG, and walking it unmemoised costs paths exponential in the number of stores it carries. The recursion is private so that cannot be bypassed.
|
inline |
|
inline |
|
inline |
|
inline |
Insert an object record into the given object map.
This method has various overloaded instances, that all descend to this particular method. The essential elements are a) an object map, b) an l1 data object or the index number (in value_sett::object_numbering) that identifies it, and c) the offset data for this record.
Rather than just adding this pointer record to the object map, this method attempts to merge the data in. That is, if the variable might already point at the same object, attempt to merge the two offset records. If that fails, then record the offset as being nondeterministic, and let the SMT solver work it out.
| dest | The object map to insert this record into. |
| n | The identifier for the object being referrred to, as indexed by the value_set::object_numbering mapping. |
| object | The offset data for the pointer record being inserted. |
Attempt to extract the member of an expression statically.
If it's a chain of with's, or a constant struct, then pick out the actual expression value of the given struct component. If not, just formulate a member2t expression.
| src | Structure type'd expr to extract a member from. |
| component_name | Name of the component to extract from src. |
|
inline |
Given another value set tracking object's storage, read all value set records out and merge them into this object's.
| new_values | Stored set of value sets to merge into this object. |
| keepnew | If true, add new pointer records in new_values into this object's tracking map; if not, discard them. |
| bool value_sett::make_union | ( | object_mapt & | dest, |
| const object_mapt & | src ) const |
Join the two given object maps.
Takes all the pointer records from src and stores them into the dest object map.
| dest | Destination object map to join records into. |
| src | Object map to merge into dest. |
|
staticprotected |
|
staticprotected |
|
protected |
Fold total_offs into every object of pointer_expr_set, tracking alignment where the offset or the object's own offset is nondet, and store the results into dest.
|
inline |
| void value_sett::output | ( | std::ostream & | out | ) | const |
Dump the value set's textual representation to the given iostream.
| out | Output stream to write the textual representation too. |
| void value_sett::retire_objectless_operand | ( | object_mapt & | op0_set, |
| object_mapt & | op1_set, | ||
| object_mapt & | dest ) const |
Recursive body of get_value_set.
An operand whose set holds nothing but unknown carries no object information, so it must not veto the other operand's.
| expr | Expression to interpret and fetch value set for |
| dest | Destination object map to store pointed-at records in. |
| suffix | Cumulative suffix to attach to referred-to variables. See the documentation on entryt for what the suffix means. As higher level expressions determine the suffix that a variable name gets, this must be passed down from higher levels (through this parameter). |
| original_type | Type of the top level expression. If any part of the interpreted expression isn't recognized, then an unknown2t expr is put in the value tracking set to represent the fact that interpretation failed, and it might point at something crazy. |
| under_deref | If true, treat the expression as occuring under a dereference operation. Otherwise treat it at free-standing. This only affects how constants are interpreted: if enabled, the expression 0 becomes a null_object2t reference in the value-set, while other constants are invalid2t/unknown2t; when disabled, constant expressions generate no references, except that a member of a constant aggregate is still selected, since navigating to it inserts nothing by itself. Stop an operand whose set is only unknown from vetoing the other's; the unknown moves into dest. See docs/design/pointer-integer-provenance.md. |
Letting it count as non-empty dropped the whole expression to unknown, which is how an address round-tripped through uintptr_t arithmetic lost the object it still pointed at once a multiply had made one side unknown (#6545).
The unknown is carried into dest rather than discarded: the arithmetic may equally land outside every known object, and dropping that alternative would delete the invalid pointer property and hide a wild dereference. See docs/design/pointer-integer-provenance.md.
|
inline |
Insert an object record element into an object map.
| dest | The map to insert this record into. |
| it | Iterator of existing object record to insert into dest. |
| expr2tc value_sett::to_expr | ( | object_mapt::const_iterator | it | ) | const |
Convert an object map element to an expression.
Formulates either an object_descriptor irep, or unknown / invalid expr's as appropriate.
|
inline |
Ensure a record exists for the given entry, without changing an existing one.
The persistent map's values are immutable, so a mutation is a read-modify-set via update_object_map().
|
inline |
Overwrite (merge=false) or union-into (merge=true) the object map recorded for the given entry.
Only a genuine change writes the map, so a no-op update never breaks the record's structural sharing.
| unsigned value_sett::location_number |
Location number of the instruction this value set is attached to; used to identify allocation sites for dynamic objects.
| const namespacet& value_sett::ns |
Namespace for looking up types against.
|
thread_localstatic |
|
thread_localstatic |
Object to assign numbers to objects – i.e., the numbers in the map of a object_mapt.
thread_local so concurrent symex threads (–k-induction-parallel) don't race on the same hash_numbering. Numbers only need to be consistent within a single goto_symex state's lifetime, and each symex thread owns its own state.
| valuest value_sett::values |
Storage for all the value sets for all the variables in the program.
See entryt for the format of the string used as an index.
| irep_idt value_sett::xchg_name |
| unsigned long value_sett::xchg_num |