|
ESBMC
Efficient SMT-based Context-Bounded Model Checker
|
#include <andersen.h>
Classes | |
| struct | constraintt |
| struct | indirect_callt |
| A call through a function pointer. More... | |
Public Types | |
| enum class | constraint_kindt { ADDRESS_OF , COPY , LOAD , STORE } |
| The four inclusion-constraint shapes of The four constraint kinds. More... | |
| typedef unsigned | node_id |
| A node is an abstract memory location: one program variable or one allocation site. | |
| Public Types inherited from value_setst | |
| typedef std::list< expr2tc > | valuest |
| typedef goto_programt::const_targett | locationt |
Public Member Functions | |
| andersent () | |
Core solver | |
Operates purely on node ids, so it is exercised by the unit tests from synthetic constraints without needing a GOTO program. | |
| node_id | get_node (const expr2tc &e) |
Interns e as a node, returning a stable id (creates one on first use). | |
| void | add_constraint (constraint_kindt kind, node_id lhs, node_id rhs) |
| Registers one constraint. Cheap; the real work happens in solve. | |
| void | points_to_top (node_id n) |
Marks n as possibly pointing anywhere by adding TOP to its set. | |
| void | solve () |
| Computes the least fixpoint of all registered constraints, populating pts. | |
| const std::unordered_set< node_id > & | points_to (node_id n) const |
The points-to set of n after solve (empty if never grown). | |
| bool | may_point_to (node_id a, node_id b) const |
True iff a may point to b (i.e. b ∈ pts[a]). | |
GOTO frontend | |
| void | collect_constraints (const goto_functionst &goto_functions) |
| Walks every function body and lowers pointer-relevant assignments, returns and calls to constraints via add_constraint. | |
Query / transparency layer | |
Lets whole-program consumers (GCSE, the k-induction pointer-write resolver) use Andersen wherever they used the value-set analysis. Because the analysis is flow-insensitive, the location argument | |
| void | operator() (const goto_functionst &goto_functions) |
| Runs the whole analysis: collect_constraints, then solve and resolve_indirect_calls alternately until a round adds nothing. | |
| void | get_values (locationtlocationt l, const expr2tc &expr, valuest &dest) override |
value_setst interface: values expr may hold, as a list of object_descriptor2t wrapping each pointee's program expression. | |
| void | get_reference_set (locationtlocationt l, const expr2tc &expr, valuest &dest) override |
| The set of objects a dereference-style l-value can refer to. | |
| bool | has_location (locationtlocationt) const override |
| Flow-insensitive: any location is "known" once the analysis has run. | |
| Public Member Functions inherited from value_setst | |
| value_setst ()=default | |
| virtual | ~value_setst ()=default |
Static Public Member Functions | |
| static bool | is_nondet_object (const expr2tc &object) |
True iff object is that sentinel. | |
Static Public Attributes | |
| static constexpr node_id | TOP = 0 |
| Reserved "may point anywhere". | |
| static constexpr const char * | nondet_object_name = "andersen::nondet" |
| The object an unconstrained pointer points at: symbolic execution resolves a dereference of one to an invalid object, so a consumer can skip it, while an empty set still means "nothing constrained this". | |
Protected Member Functions | |
| const node_id * | find_node (const expr2tc &e) const |
The node interned for e, or nullptr if it was never seen. | |
| void | ensure_node (node_id n) |
Grows all node-indexed vectors so n is a valid index. | |
| node_id | fresh_node () |
| A node with no program expression, used to hold the intermediate value of a compound right-hand side. | |
| bool | join (node_id dst, node_id src) |
| Unions pts[src] into pts[dst]; true iff pts[dst] grew. | |
| bool | add_copy_edge (node_id from, node_id to) |
| Adds the copy edge from -> to and propagates along it immediately. | |
| node_id | allocation (const type2tc &type, unsigned loc) |
A node pointing to the heap object allocated at loc. | |
| node_id | unconstrained () |
| A node pointing to the nondet sentinel: an unconstrained value. | |
| node_id | top_source () |
| A node whose points-to set is {TOP}. | |
| node_id | return_node (const irep_idt &fn) |
The node standing for fn's return value, shared by every call site (context insensitivity). | |
| node_id | eval_rhs (const expr2tc &rhs, unsigned location_number) |
A node whose points-to set is the value of rhs, introducing a fresh_node when the expression is not already a nameable object. | |
| node_id | eval_address_of (const address_of2t &rhs, unsigned location_number) |
| The parts of eval_rhs, each for one kind of expression. | |
| std::optional< node_id > | eval_sideeffect (const sideeffect2t &rhs, unsigned location_number) |
| node_id | eval_realloc (node_id fresh, const expr2tc &old, unsigned location_number) |
| std::optional< node_id > | eval_pointer_arithmetic (const expr2tc &rhs, unsigned location_number) |
| node_id | eval_branch (const if2t &rhs, unsigned location_number) |
| node_id | eval_value (const expr2tc &rhs, unsigned location_number) |
| A constant, an l-value read as a value, or an integer or aggregate that may carry addresses; TOP for anything else. | |
| node_id | eval_integer (const expr2tc &rhs, unsigned location_number) |
| node_id | eval_constant_aggregate (const expr2tc &rhs, unsigned location_number) |
| void | handle_assign (const expr2tc &lhs, const expr2tc &rhs, unsigned location_number) |
| Lowers lhs := rhs to whichever of the four constraints fits. | |
| void | assign_node (const expr2tc &lhs, node_id src, unsigned loc) |
Adds what src points to into every object lhs may designate: both arms of a conditional, and through the pointer of a dereference. | |
| void | assign_top (const expr2tc &lhs, unsigned loc) |
Records that the object lhs designates may hold any address, storing through the pointer when lhs is a dereference. | |
| void | handle_other (const expr2tc &code, unsigned location_number) |
| Lowers an OTHER instruction: the ones that only read are dropped, the rest may write through any pointer operand. | |
| void | handle_function_call (const code_function_call2t &call, const goto_functionst &goto_functions, unsigned location_number) |
| Binds arguments to formals and the return value to the LHS. | |
| bool | resolve_indirect_calls (const goto_functionst &goto_functions) |
| Lowers every indirect call whose target set is now known. | |
| void | bind_call (const irep_idt &callee_name, const goto_functiont &callee, const expr2tc &ret, const std::vector< expr2tc > &arguments, unsigned location_number) |
Binds arguments to callee's formals and its return value to ret. | |
| void | widen_call (const expr2tc &ret, const std::vector< expr2tc > &arguments, unsigned location_number) |
| The conservative treatment of a callee this analysis cannot name: it may return anything and write anything through the pointers it is handed. | |
| void | to_object_descriptors (node_id n, valuest &dest) const |
| Translates a solved points-to set into the object_descriptor2t list shape that value_setst consumers expect. | |
Protected Attributes | |
| std::vector< std::unordered_set< node_id > > | pts |
| node -> its points-to set. The analysis result. | |
| std::vector< constraintt > | constraints |
| All registered constraints (see add_constraint). | |
| std::vector< std::unordered_set< node_id > > | copy_edges |
| Copy edges from -> to built during solve (dynamic edges from load/store are appended here as they are discovered). | |
| std::unordered_map< irep_idt, node_id, irep_id_hash > | symbol_to_node |
| Node tracking. | |
| std::unordered_map< expr2tc, node_id, irep2_hash > | expr_to_node |
| std::vector< expr2tc > | node_to_expr |
| unsigned | tmp_count = 0 |
| Counter behind fresh_node. | |
| bool | solved = false |
| Set once solve has produced a valid fixpoint. | |
| bool | everything_escapes = false |
| Set when the program contains a statement whose effect on memory is not visible in the IR at all (inline asm: IREP2 keeps only its source string, not the operands it writes through). | |
| std::unordered_set< irep_idt, irep_id_hash > | allocators |
| Functions each call of which allocates an object of its own, so a call to one is an allocation site rather than a binding of its return value. | |
| std::unordered_map< irep_idt, bool, irep_id_hash > | reads_varargs |
| Whether each function bound so far reads its variadic arguments. | |
| std::vector< indirect_callt > | indirect_calls |
| typedef unsigned andersent::node_id |
A node is an abstract memory location: one program variable or one allocation site.
Nodes are dense integers so points-to sets can be cheap hashed-int sets (and, later, bitvectors).
|
strong |
| andersent::andersent | ( | ) |
| void andersent::add_constraint | ( | constraint_kindt | kind, |
| node_id | lhs, | ||
| node_id | rhs ) |
Registers one constraint. Cheap; the real work happens in solve.
Adds the copy edge from -> to and propagates along it immediately.
True iff pts[to] grew.
|
protected |
A node pointing to the heap object allocated at loc.
Adds what src points to into every object lhs may designate: both arms of a conditional, and through the pointer of a dereference.
|
protected |
Records that the object lhs designates may hold any address, storing through the pointer when lhs is a dereference.
The escape hatch for an assignment whose source this frontend cannot model.
|
protected |
Binds arguments to callee's formals and its return value to ret.
| void andersent::collect_constraints | ( | const goto_functionst & | goto_functions | ) |
Walks every function body and lowers pointer-relevant assignments, returns and calls to constraints via add_constraint.
Field-, index- and context-insensitive; see the implementation for the modelling choices. Independent of solve: it only builds constraints.
Calls through a function pointer cannot be lowered here — their targets are exactly what is being computed — so they are recorded and left to resolve_indirect_calls. Use operator(), which drives both.
|
protected |
Grows all node-indexed vectors so n is a valid index.
|
protected |
The parts of eval_rhs, each for one kind of expression.
Those that return an optional give nothing when rhs is not of their kind.
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
A node whose points-to set is the value of rhs, introducing a fresh_node when the expression is not already a nameable object.
It may be another object's own node, so callers only ever read it.
|
protected |
|
protected |
A constant, an l-value read as a value, or an integer or aggregate that may carry addresses; TOP for anything else.
|
protected |
The node interned for e, or nullptr if it was never seen.
|
protected |
A node with no program expression, used to hold the intermediate value of a compound right-hand side.
Never the target of an ADDRESS_OF, so it never appears inside a points-to set.
| andersent::node_id andersent::get_node | ( | const expr2tc & | e | ) |
Interns e as a node, returning a stable id (creates one on first use).
Symbols are interned by identifier alone: the same variable can reach the analysis with different-but-compatible types (a symbol_type at one use, the followed struct at another), and those must share a node.
|
overridevirtual |
The set of objects a dereference-style l-value can refer to.
Implements value_setst.
|
overridevirtual |
value_setst interface: values expr may hold, as a list of object_descriptor2t wrapping each pointee's program expression.
Implements value_setst.
|
protected |
Lowers lhs := rhs to whichever of the four constraints fits.
|
protected |
Binds arguments to formals and the return value to the LHS.
|
protected |
Lowers an OTHER instruction: the ones that only read are dropped, the rest may write through any pointer operand.
Flow-insensitive: any location is "known" once the analysis has run.
Implements value_setst.
True iff object is that sentinel.
True iff a may point to b (i.e. b ∈ pts[a]).
| void andersent::operator() | ( | const goto_functionst & | goto_functions | ) |
Runs the whole analysis: collect_constraints, then solve and resolve_indirect_calls alternately until a round adds nothing.
| const std::unordered_set< andersent::node_id > & andersent::points_to | ( | node_id | n | ) | const |
The points-to set of n after solve (empty if never grown).
| void andersent::points_to_top | ( | node_id | n | ) |
Marks n as possibly pointing anywhere by adding TOP to its set.
The conservative escape hatch for anything the frontend cannot model.
|
protected |
Lowers every indirect call whose target set is now known.
True iff new constraints were added, meaning another solve round is needed.
|
protected |
The node standing for fn's return value, shared by every call site (context insensitivity).
| void andersent::solve | ( | ) |
Computes the least fixpoint of all registered constraints, populating pts.
Idempotent: safe to call again after adding constraints.
Translates a solved points-to set into the object_descriptor2t list shape that value_setst consumers expect.
|
protected |
A node whose points-to set is {TOP}.
Used as the source of a STORE to express "an unmodelled callee may write anything through this pointer".
|
protected |
A node pointing to the nondet sentinel: an unconstrained value.
|
protected |
The conservative treatment of a callee this analysis cannot name: it may return anything and write anything through the pointers it is handed.
|
protected |
Functions each call of which allocates an object of its own, so a call to one is an allocation site rather than a binding of its return value.
|
protected |
All registered constraints (see add_constraint).
|
protected |
Copy edges from -> to built during solve (dynamic edges from load/store are appended here as they are discovered).
A set, because the load/store rules re-derive the same edge on every re-visit of a node and duplicates would otherwise accumulate without bound.
Set when the program contains a statement whose effect on memory is not visible in the IR at all (inline asm: IREP2 keeps only its source string, not the operands it writes through).
No per-object widening can be sound then, so every query answers TOP.
|
protected |
|
protected |
|
protected |
|
staticconstexpr |
The object an unconstrained pointer points at: symbolic execution resolves a dereference of one to an invalid object, so a consumer can skip it, while an empty set still means "nothing constrained this".
It is inert, as that invalid object is: a store into it is dropped and a load from it yields another unconstrained pointer. Sound relative to symex's memory model, not to C's.
|
protected |
node -> its points-to set. The analysis result.
|
protected |
Whether each function bound so far reads its variadic arguments.
|
protected |
Node tracking.
Symbols are keyed by identifier, everything else by the expression itself; see get_node.
|
protected |
Counter behind fresh_node.
|
staticconstexpr |
Reserved "may point anywhere".