Static Value-Flow Analysis
Loading...
Searching...
No Matches
Public Types | Public Member Functions | Static Public Member Functions | Protected Member Functions | Protected Attributes | Private Member Functions | Private Attributes | Friends | List of all members
SVF::AbstractInterpretation Class Reference

#include <AbstractInterpretation.h>

Inheritance diagram for SVF::AbstractInterpretation:
SVF::SemiSparseAbstractInterpretation SVF::FullSparseAbstractInterpretation

Public Types

enum  AESparsity { Dense , SemiSparse , Sparse }
 
enum  HandleRecur { TOP , WIDEN_ONLY , WIDEN_NARROW }
 
enum  AEFunEntryMode { MAIN , NO_MAIN }
 

Public Member Functions

virtual void runOnModule ()
 
virtual ~AbstractInterpretation ()
 Destructor.
 
void analyse ()
 Program entry.
 
void analyzeFromAllProgEntries ()
 Analyze all entry points (functions without callers)
 
FIFOWorkList< const FunObjVar * > collectProgEntryFuns ()
 Get all entry point functions (functions without callers)
 
void addDetector (std::unique_ptr< AEDetector > detector)
 
const SVFVargetSVFVar (NodeID varId) const
 Retrieve SVFVar given its ID; asserts if no such variable exists.
 
virtual const AbstractValuegetAbsValue (const ValVar *var, const ICFGNode *node)
 
virtual const AbstractValuegetAbsValue (const ObjVar *var, const ICFGNode *node)
 
virtual const AbstractValuegetAbsValue (const SVFVar *var, const ICFGNode *node)
 
virtual bool hasAbsValue (const ValVar *var, const ICFGNode *node) const
 Side-effect-free existence check.
 
virtual bool hasAbsValue (const ObjVar *var, const ICFGNode *node) const
 
virtual bool hasAbsValue (const SVFVar *var, const ICFGNode *node) const
 
virtual void updateAbsValue (const ValVar *var, const AbstractValue &val, const ICFGNode *node)
 
virtual void updateAbsValue (const ObjVar *var, const AbstractValue &val, const ICFGNode *node)
 
virtual void updateAbsValue (const SVFVar *var, const AbstractValue &val, const ICFGNode *node)
 
AbstractStategetAbsState (const ICFGNode *node)
 
virtual void updateAbsState (const ICFGNode *node, const AbstractState &state)
 
virtual void joinStates (AbstractState &dst, const AbstractState &src)
 
bool hasAbsState (const ICFGNode *node)
 
void getAbsState (const Set< const ValVar * > &vars, AbstractState &result, const ICFGNode *node)
 
void getAbsState (const Set< const ObjVar * > &vars, AbstractState &result, const ICFGNode *node)
 
void getAbsState (const Set< const SVFVar * > &vars, AbstractState &result, const ICFGNode *node)
 
IntervalValue getGepElementIndex (const GepStmt *gep)
 
IntervalValue getGepByteOffset (const GepStmt *gep)
 
AddressValue getGepObjAddrs (const ValVar *pointer, IntervalValue offset)
 
virtual AbstractValue loadValue (const ValVar *pointer, const ICFGNode *node)
 Virtual so full-sparse can layer the GepObj overlay on top.
 
virtual void storeValue (const ValVar *pointer, const AbstractValue &val, const ICFGNode *node)
 
const SVFTypegetPointeeElement (const ObjVar *var, const ICFGNode *node)
 
u32_t getAllocaInstByteSize (const AddrStmt *addr)
 
Map< const ICFGNode *, AbstractState > & getTrace ()
 
AbstractStateoperator[] (const ICFGNode *node)
 

Static Public Member Functions

static AbstractInterpretationgetAEInstance ()
 

Protected Member Functions

 AbstractInterpretation ()
 
virtual AbstractState getFullCycleHeadState (const ICFGCycleWTO *cycle)
 
virtual bool widenCycleState (const AbstractState &prev, const AbstractState &cur, const ICFGCycleWTO *cycle)
 
virtual bool narrowCycleState (const AbstractState &prev, const AbstractState &cur, const ICFGCycleWTO *cycle)
 
virtual bool mergeStatesFromPredecessors (const ICFGNode *node)
 
bool isBranchEdgeFeasible (const IntraCFGEdge *edge, AbstractState &as)
 
void collectBranchRefinement (const IntraCFGEdge *edge, AbstractState &as)
 
virtual void recordBranchRefinement (NodeID objId, const IntervalValue &narrowed, AbstractState &as, const ICFGNode *loadIcfg, const ICFGNode *succ)
 
bool shouldApplyNarrowing (const FunObjVar *fun)
 Check if narrowing should be applied: always for regular loops, mode-dependent for recursion.
 

Protected Attributes

SVFIRsvfir {nullptr}
 Data and helpers reachable from SparseAbstractInterpretation.
 
AEWTOpreAnalysis {nullptr}
 
Map< const ICFGNode *, AbstractStateabstractTrace
 per-node trace; owned here
 

Private Member Functions

virtual void handleGlobalNode ()
 
virtual void handleCallSite (const ICFGNode *node)
 Handle a call site node: dispatch to ext-call, direct-call, or indirect-call handling.
 
virtual void handleLoopOrRecursion (const ICFGCycleWTO *cycle, const CallICFGNode *caller)
 Handle a WTO cycle (loop or recursive function) using widening/narrowing iteration.
 
void handleFunction (const ICFGNode *funEntry, const CallICFGNode *caller)
 Handle a function body via worklist-driven WTO traversal starting from funEntry.
 
bool handleICFGNode (const ICFGNode *node)
 Handle an ICFG node: execute statements; return true if state changed.
 
virtual void handleSVFStatement (const SVFStmt *stmt)
 Dispatch an SVF statement (Addr/Binary/Cmp/Load/Store/Copy/Gep/Select/Phi/Call/Ret) to its handler.
 
bool isCmpBranchEdgeFeasible (const IntraCFGEdge *edge, AbstractState &as)
 Returns true if the cmp-conditional branch is feasible.
 
bool isSwitchBranchEdgeFeasible (const IntraCFGEdge *edge, AbstractState &as)
 Returns true if the switch branch is feasible.
 
void updateStateOnAddr (const AddrStmt *addr)
 
void updateStateOnBinary (const BinaryOPStmt *binary)
 
void updateStateOnCmp (const CmpStmt *cmp)
 
void updateStateOnLoad (const LoadStmt *load)
 
void updateStateOnStore (const StoreStmt *store)
 
void updateStateOnCopy (const CopyStmt *copy)
 
void updateStateOnCall (const CallPE *callPE)
 
void updateStateOnRet (const RetPE *retPE)
 
void updateStateOnGep (const GepStmt *gep)
 
void updateStateOnSelect (const SelectStmt *select)
 
void updateStateOnPhi (const PhiStmt *phi)
 
AbsExtAPIgetUtils ()
 
virtual bool isExtCall (const CallICFGNode *callNode)
 
virtual void handleExtCall (const CallICFGNode *callNode)
 
virtual bool isRecursiveFun (const FunObjVar *fun)
 Check if a function is recursive (part of a call graph SCC)
 
virtual void skipRecursionWithTop (const CallICFGNode *callNode)
 
virtual bool isRecursiveCallSite (const CallICFGNode *callNode, const FunObjVar *)
 Check if caller and callee are in the same CallGraph SCC (i.e. a recursive callsite)
 
virtual void handleFunCall (const CallICFGNode *callNode)
 
bool skipRecursiveCall (const CallICFGNode *callNode)
 Skip recursive callsites (within SCC); entry calls from outside SCC are not skipped.
 
const FunObjVargetCallee (const CallICFGNode *callNode)
 Get callee function: directly for direct calls, via pointer analysis for indirect calls.
 

Private Attributes

AEAPIapi {nullptr}
 Execution State, used to store the Interval Value of every SVF variable.
 
ICFGicfg
 
CallGraphcallGraph
 
AEStatstat
 
Map< std::string, std::function< void(const CallICFGNode *)> > func_map
 
Set< const ICFGNode * > allAnalyzedNodes
 
std::string moduleName
 
std::vector< std::unique_ptr< AEDetector > > detectors
 
AbsExtAPIutils
 

Friends

class AEStat
 
class AEAPI
 
class BufOverflowDetector
 
class NullptrDerefDetector
 

Detailed Description

AbstractInterpretation is same as Abstract Execution.

Owns the per-node abstract trace and exposes the read/write API directly (no separate state-manager indirection). Sparse modes are implemented as subclasses that override the virtual hooks below (cycle helpers, ValVar accessors, joinStates, def/use queries).

Definition at line 60 of file AbstractInterpretation.h.

Member Enumeration Documentation

◆ AEFunEntryMode

Enumerator
MAIN 
NO_MAIN 

Definition at line 97 of file AbstractInterpretation.h.

◆ AESparsity

Enumerator
Dense 
SemiSparse 
Sparse 

Definition at line 83 of file AbstractInterpretation.h.

◆ HandleRecur

Enumerator
TOP 
WIDEN_ONLY 
WIDEN_NARROW 

Definition at line 90 of file AbstractInterpretation.h.

Constructor & Destructor Documentation

◆ ~AbstractInterpretation()

AbstractInterpretation::~AbstractInterpretation ( )
virtual

Destructor.

Definition at line 112 of file AbstractInterpretation.cpp.

◆ AbstractInterpretation()

AbstractInterpretation::AbstractInterpretation ( )
protected

Factory-only construction. External callers must use getAEInstance(); SparseAbstractInterpretation reaches this via its own ctor.

Definition at line 61 of file AbstractInterpretation.cpp.

62{
63 stat = new AEStat(this);
64 // Run Andersen's pointer analysis and build WTO
66 icfg = svfir->getICFG();
71}
CallGraph * getCallGraph() const
Definition AEWTO.h:60
void initWTO()
Build WTO for each function using call graph SCC.
Definition AEWTO.cpp:51
SVFIR * svfir
Data and helpers reachable from SparseAbstractInterpretation.
void updateCallGraph(CallGraph *callgraph)
update ICFG for indirect calls
Definition ICFG.cpp:427
ICFG * getICFG() const
Definition SVFIR.h:229
static SVFIR * getPAG(bool buildFromFile=false)
Singleton design here to make sure we only have one instance during any analysis.
Definition SVFIR.h:118

Member Function Documentation

◆ addDetector()

void SVF::AbstractInterpretation::addDetector ( std::unique_ptr< AEDetector detector)
inline

Definition at line 124 of file AbstractInterpretation.h.

125 {
126 detectors.push_back(std::move(detector));
127 }
std::vector< std::unique_ptr< AEDetector > > detectors
llvm::IRBuilder IRBuilder
Definition BasicTypes.h:76

◆ analyse()

void AbstractInterpretation::analyse ( )

Program entry.

Program entry - entry policy is selected by -ae-fun-entry.

Definition at line 200 of file AbstractInterpretation.cpp.

201{
203}
void analyzeFromAllProgEntries()
Analyze all entry points (functions without callers)

◆ analyzeFromAllProgEntries()

void AbstractInterpretation::analyzeFromAllProgEntries ( )

Analyze all entry points (functions without callers)

Analyze the entry functions selected by collectProgEntryFuns(). Abstract state is shared across entry points so that functions analyzed from earlier entries are not re-analyzed from scratch.

Definition at line 208 of file AbstractInterpretation.cpp.

209{
210 // Collect all entry point functions
212
213 if (entryFunctions.empty())
214 {
215 assert(false && "No entry functions found for analysis");
216 return;
217 }
218 // handle Global ICFGNode of SVFModule
221 while (!entryFunctions.empty())
222 {
223 const FunObjVar* entryFun = entryFunctions.pop();
226 handleFunction(funEntry, nullptr);
227 }
228}
void handleFunction(const ICFGNode *funEntry, const CallICFGNode *caller)
Handle a function body via worklist-driven WTO traversal starting from funEntry.
AbstractState & getAbsState(const ICFGNode *node)
FIFOWorkList< const FunObjVar * > collectProgEntryFuns()
Get all entry point functions (functions without callers)
virtual void updateAbsState(const ICFGNode *node, const AbstractState &state)
FunEntryICFGNode * getFunEntryICFGNode(const FunObjVar *fun)
Add a function entry node.
Definition ICFG.cpp:242
GlobalICFGNode * getGlobalICFGNode() const
Definition ICFG.h:244

◆ collectBranchRefinement()

void AbstractInterpretation::collectBranchRefinement ( const IntraCFGEdge edge,
AbstractState as 
)
protected

Collect branch-induced interval refinement after a feasible edge has been selected for normal CFG-state merging.

Definition at line 537 of file AbstractInterpretation.cpp.

539{
540 const SVFVar* cond = edge->getCondition();
541 const ICFGNode* pred = edge->getSrcNode();
542 const ICFGNode* succNode = edge->getDstNode();
543 s64_t succ = edge->getSuccessorCondValue();
544
545 assert(!cond->getInEdges().empty() &&
546 "branch condition has no defining edge?");
547 const SVFStmt* condDef = *cond->getInEdges().begin();
548
549 if (const CmpStmt* cmpStmt = SVFUtil::dyn_cast<CmpStmt>(condDef))
550 {
551 s32_t predicate = cmpStmt->getPredicate();
552
553 if (cmpStmt->getOpVarID(0) == IRGraph::NullPtr ||
554 cmpStmt->getOpVarID(1) == IRGraph::NullPtr)
555 {
556 // p == NULL / p != NULL: no interval obj to refine.
557 }
558 else
559 {
560 AbstractValue opVal[2] = {getAbsValue(cmpStmt->getOpVar(0), pred),
561 getAbsValue(cmpStmt->getOpVar(1), pred)
562 };
563
564 const bool hasIntervalCmp =
565 opVal[0].isInterval() && opVal[1].isInterval();
566 if (!hasIntervalCmp && (opVal[0].isAddr() || opVal[1].isAddr()))
567 {
568 // Pointer-valued cmp: branch feasibility only.
569 }
570 else
571 {
572 for (int i = 0; i < 2; i++)
573 {
574 const int other = 1 - i;
575 const LoadStmt* load =
576 findBackingLoad(cmpStmt->getOpVar(i));
577
578 if (opVal[i].getInterval().is_numeral())
579 {
580 // Example: in x < 5, operand 5 is not refined.
581 }
582 else if (!opVal[other].getInterval().is_numeral())
583 {
584 // Example: x < y, neither side has a fixed bound.
585 }
586 else if (!load)
587 {
588 // Example: cmp uses a computed temporary, not load p.
589 }
590 else
591 {
593 predicate, succ, i == 0, opVal[i].getInterval(),
594 opVal[other].getInterval());
595
596 if (narrowed.isTop())
597 {
598 // != and unsupported predicates reach here.
599 }
600 else
601 {
602 const ICFGNode* loadIcfg = load->getICFGNode();
603 const AbstractValue& ptrVal =
605 if (!ptrVal.isAddr())
606 {
607 // Cannot map load p back to concrete ObjVars.
608 }
609 else
610 {
611 for (const auto& addr : ptrVal.getAddrs())
612 {
613 NodeID objId = as.getIDFromAddr(addr);
616 }
617 }
618 }
619 }
620 }
621 }
622 }
623 }
624 else
625 {
626 const SVFVar* var = cond;
627
629 IntervalValue switch_cond = condVal.getInterval();
631 if (switch_cond.isBottom())
632 {
633 // This case label is not reachable from cond's interval.
634 }
635 else
636 {
637 as[var->getId()] = AbstractValue(switch_cond);
638
640 for (SVFStmt* stmt : var->getInEdges())
641 stmtList.push(stmt);
642 while (!stmtList.empty())
643 {
644 const SVFStmt* stmt = stmtList.pop();
645 const LoadStmt* load = SVFUtil::dyn_cast<LoadStmt>(stmt);
646 if (!load)
647 {
648 // Skip non-load definitions of the switch condition.
649 }
650 else
651 {
652 const ICFGNode* loadIcfg = load->getICFGNode();
653 const AbstractValue& ptrVal =
655 if (!ptrVal.isAddr())
656 {
657 // Cannot map load p back to concrete ObjVars.
658 }
659 else
660 {
661 for (const auto& addr : ptrVal.getAddrs())
662 {
663 NodeID objId = as.getIDFromAddr(addr);
666 }
667 }
668 }
669 }
670 }
671 }
672}
static const LoadStmt * findBackingLoad(const SVFVar *var)
static IntervalValue computeCmpConstraint(s32_t predicate, s64_t succ, bool isLHS, const IntervalValue &self, const IntervalValue &other)
virtual const AbstractValue & getAbsValue(const ValVar *var, const ICFGNode *node)
virtual void recordBranchRefinement(NodeID objId, const IntervalValue &narrowed, AbstractState &as, const ICFGNode *loadIcfg, const ICFGNode *succ)
bool isInterval() const
const GEdgeSetTy & getInEdges() const
void meet_with(const IntervalValue &other)
Return a intersected IntervalValue.
const ValVar * getRHSVar() const
ICFGNode * getICFGNode() const
u32_t NodeID
Definition GeneralType.h:56
signed s32_t
Definition GeneralType.h:48
signed long long s64_t
Definition GeneralType.h:50

◆ collectProgEntryFuns()

FIFOWorkList< const FunObjVar * > AbstractInterpretation::collectProgEntryFuns ( )

Get all entry point functions (functions without callers)

Collect entry point functions for analysis. In main mode, entry is main/svf.main. In no-main mode, entries are SCCs with no external caller in the Andersen-resolved CallGraph.

Definition at line 122 of file AbstractInterpretation.cpp.

123{
127 auto* callGraphSCC = preAnalysis->getCallGraphSCC();
128
129 for (auto it = callGraph->begin(); it != callGraph->end(); ++it)
130 {
131 const CallGraphNode* cgNode = it->second;
132 const FunObjVar* fun = cgNode->getFunction();
133
134 // Skip declarations
135 if (fun->isDeclaration())
136 continue;
137
138 if (mainEntry)
139 {
141 {
142 entryFunctions.push(fun);
143 break;
144 }
145 }
146 else
147 {
148 NodeID repNodeId = callGraphSCC->repNode(cgNode->getId());
149 if (visitedEntrySCCs.count(repNodeId))
150 continue;
151
152 const NodeBS& cgSCCNodes = callGraphSCC->subNodes(repNodeId);
153 bool hasExternalCaller = false;
154 for (NodeID nodeId : cgSCCNodes)
155 {
157 for (auto inEdge : sccNode->getInEdges())
158 {
159 if (!cgSCCNodes.test(inEdge->getSrcID()))
160 {
161 hasExternalCaller = true;
162 break;
163 }
164 }
166 break;
167 }
168
170 continue;
171
173 const FunObjVar* entryFun = fun;
174 for (NodeID nodeId : cgSCCNodes)
175 {
176 const FunObjVar* sccFun = callGraph->getGNode(nodeId)->getFunction();
178 {
180 break;
181 }
182 }
184 }
185 }
186
187 if (mainEntry && entryFunctions.empty())
188 {
190 "AE -ae-fun-entry=main requires a program entry function, but main/svf.main was not found.\n");
191 assert(false && "No program entry function found for -ae-fun-entry=main");
192 abort();
193 }
194
195 return entryFunctions;
196}
CallGraphSCC * getCallGraphSCC() const
Definition AEWTO.h:64
const FunObjVar * getFunction() const
Get function of this call node.
Definition CallGraph.h:191
bool isDeclaration() const
iterator begin()
Iterators.
NodeType * getGNode(NodeID id) const
Get a node.
static const OptionMap< u32_t > AEFunEntry
Definition Options.h:244
NodeID getId() const
Get ID.
Definition SVFValue.h:163
bool isProgEntryFunction(const FunObjVar *)
Program entry function e.g. main.
Definition SVFUtil.cpp:442
std::string errMsg(const std::string &msg)
Print error message by converting a string into red string output.
Definition SVFUtil.cpp:78
std::ostream & errs()
Overwrite llvm::errs()
Definition SVFUtil.h:58

◆ getAbsState() [1/4]

AbstractState & AbstractInterpretation::getAbsState ( const ICFGNode node)

Definition at line 44 of file AbstractStateManager.cpp.

45{
46 if (abstractTrace.count(node) == 0)
47 {
48 assert(false && "No preAbsTrace for this node");
49 abort();
50 }
51 return abstractTrace[node];
52}
Map< const ICFGNode *, AbstractState > abstractTrace
per-node trace; owned here

◆ getAbsState() [2/4]

void AbstractInterpretation::getAbsState ( const Set< const ObjVar * > &  vars,
AbstractState result,
const ICFGNode node 
)

Definition at line 168 of file AbstractStateManager.cpp.

169{
171 for (const ObjVar* var : vars)
172 {
174 result.store(addr, as.load(addr));
175 }
176}
unsigned u32_t
Definition CommandLine.h:18
static u32_t getVirtualMemAddress(u32_t idx)
The physical address starts with 0x7f...... + idx.

◆ getAbsState() [3/4]

void AbstractInterpretation::getAbsState ( const Set< const SVFVar * > &  vars,
AbstractState result,
const ICFGNode node 
)

Definition at line 178 of file AbstractStateManager.cpp.

179{
181 for (const SVFVar* var : vars)
182 {
183 if (const ValVar* valVar = SVFUtil::dyn_cast<ValVar>(var))
184 {
185 u32_t id = valVar->getId();
186 result[id] = as[id];
187 }
188 else if (const ObjVar* objVar = SVFUtil::dyn_cast<ObjVar>(var))
189 {
191 result.store(addr, as.load(addr));
192 }
193 }
194}

◆ getAbsState() [4/4]

void AbstractInterpretation::getAbsState ( const Set< const ValVar * > &  vars,
AbstractState result,
const ICFGNode node 
)

Definition at line 158 of file AbstractStateManager.cpp.

159{
161 for (const ValVar* var : vars)
162 {
163 u32_t id = var->getId();
164 result[id] = as[id];
165 }
166}

◆ getAbsValue() [1/3]

const AbstractValue & AbstractInterpretation::getAbsValue ( const ObjVar var,
const ICFGNode node 
)
virtual

Reimplemented in SVF::SemiSparseAbstractInterpretation.

Definition at line 90 of file AbstractStateManager.cpp.

91{
94 return as.load(addr);
95}

◆ getAbsValue() [2/3]

const AbstractValue & AbstractInterpretation::getAbsValue ( const SVFVar var,
const ICFGNode node 
)
virtual

Reimplemented in SVF::SemiSparseAbstractInterpretation.

Definition at line 97 of file AbstractStateManager.cpp.

98{
99 if (const ObjVar* objVar = SVFUtil::dyn_cast<ObjVar>(var))
100 return getAbsValue(objVar, node);
101 if (const ValVar* valVar = SVFUtil::dyn_cast<ValVar>(var))
102 return getAbsValue(valVar, node);
103 assert(false && "Unknown SVFVar kind");
104 abort();
105}

◆ getAbsValue() [3/3]

const AbstractValue & AbstractInterpretation::getAbsValue ( const ValVar var,
const ICFGNode node 
)
virtual

Read a top-level variable's abstract value. Dense base does a direct trace lookup; sparse subclasses override with their own resolution chain (def-site walk, call-result fallback, etc.). All three overloads are virtual so full-sparse can route ObjVar reads through the SVFG.

Dense base: direct trace lookup, with a top sentinel for genuinely missing entries (e.g. function parameters like argc, never written before first read). Sparse subclasses override with a def-site resolution chain.

The "in map" check is a raw map.count — NOT inVarToValTable / inVarToAddrsTable, which gate on isInterval / isAddr. SVF canonically represents uninit and null-pointer shapes as (interval=bottom ∧ addrs=∅); those predicates would falsely report such an entry as "not present", and the top fallback below would then clobber the very signal NullptrDerefDetector::isUninit keys off.

Reimplemented in SVF::SemiSparseAbstractInterpretation, and SVF::SemiSparseAbstractInterpretation.

Definition at line 80 of file AbstractStateManager.cpp.

81{
82 u32_t id = var->getId();
84 if (as.getVarToVal().count(id))
85 return as[id];
86 as[id] = IntervalValue::top();
87 return as[id];
88}
static IntervalValue top()
Create the IntervalValue [-inf, +inf].

◆ getAEInstance()

AbstractInterpretation & AbstractInterpretation::getAEInstance ( )
static

Factory: returns the singleton instance. The concrete class is chosen once, on first call, from Options::AESparsity(): SemiSparseAbstractInterpretation for SemiSparse, FullSparseAbstractInterpretation for Sparse, otherwise the dense base. Must be called only after the option parser has run.

Factory: first call allocates the concrete subclass based on Options::AESparsity(); all subsequent calls return the same instance. Must only be called after the option parser has populated AESparsity.

Definition at line 76 of file AbstractInterpretation.cpp.

77{
78 // Leak the singleton on purpose. AbstractInterpretation owns a
79 // Map<std::string, std::function<void(const CallICFGNode*)>> func_map
80 // whose lambda closures back-reference state owned by other globals
81 // (preAnalysis's WTO, the call graph, ...). Letting the static
82 // unique_ptr's atexit-time destructor run hits a static-destruction-
83 // order issue: the func_map hashtable's destructor calls into
84 // std::function destroyers whose closures touch already-destroyed
85 // state, and ~_Hashtable() segfaults during normal program shutdown.
86 //
87 // Reliably reproducible from any downstream tool that drives a full
88 // AE analysis to completion and then exits normally:
89 // - SSA's ass3 binary (Software-Security-Analysis/Assignment-3)
90 // - pysvf via Python interpreter shutdown
91 //
92 // A process-lifetime singleton has no observable lifecycle past
93 // program exit, so leaking is benign and avoids the use-after-destroy.
95 {
96 switch (Options::AESparsity())
97 {
103 default:
104 return new AbstractInterpretation();
105 }
106 }();
107 return *instance;
108}
static const OptionMap< u32_t > AESparsity
Definition Options.h:243

◆ getAllocaInstByteSize()

u32_t AbstractInterpretation::getAllocaInstByteSize ( const AddrStmt addr)

Definition at line 373 of file AbstractStateManager.cpp.

374{
375 const ICFGNode* node = addr->getICFGNode();
376 if (const ObjVar* objvar = SVFUtil::dyn_cast<ObjVar>(addr->getRHSVar()))
377 {
379 {
380 return svfir->getBaseObject(objvar->getId())->getByteSizeOfObj();
381 }
382 else
383 {
384 const std::vector<SVFVar*>& sizes = addr->getArrSize();
385 u32_t elementSize = 1;
386 u64_t res = elementSize;
387 for (const SVFVar* value : sizes)
388 {
389 const AbstractValue& sizeVal = getAbsValue(value, node);
390 IntervalValue itv = sizeVal.getInterval();
391 if (itv.isBottom())
393 res = res * itv.ub().getIntNumeral() > Options::MaxFieldLimit()
394 ? Options::MaxFieldLimit() : res * itv.ub().getIntNumeral();
395 }
396 return (u32_t)res;
397 }
398 }
399 assert(false && "Addr rhs value is not ObjVar");
400 abort();
401}
bool isConstantByteSize() const
Check if byte size is a const value.
u32_t getByteSizeOfObj() const
Get the byte size of this object.
static const Option< u32_t > MaxFieldLimit
Maximum number of field derivations for an object.
Definition Options.h:35
const BaseObjVar * getBaseObject(NodeID id) const
Definition SVFIR.h:496
unsigned long long u64_t
Definition GeneralType.h:49

◆ getCallee()

const FunObjVar * AbstractInterpretation::getCallee ( const CallICFGNode callNode)
private

Get callee function: directly for direct calls, via pointer analysis for indirect calls.

Definition at line 842 of file AbstractInterpretation.cpp.

843{
844 // Direct call: get callee directly from call node
845 if (const FunObjVar* callee = callNode->getCalledFunction())
846 return callee;
847
848 // Indirect call: resolve callee through pointer analysis
850 auto it = callsiteMaps.find(callNode);
851 if (it == callsiteMaps.end())
852 return nullptr;
853
854 NodeID call_id = it->second;
855 if (!hasAbsState(callNode))
856 return nullptr;
857
859 if (!Addrs.isAddr() || Addrs.getAddrs().empty())
860 return nullptr;
861
862 NodeID addr = *Addrs.getAddrs().begin();
863 const SVFVar* func_var = getSVFVar(getAbsState(callNode).getIDFromAddr(addr));
864 return SVFUtil::dyn_cast<FunObjVar>(func_var);
865}
bool hasAbsState(const ICFGNode *node)
const SVFVar * getSVFVar(NodeID varId) const
Retrieve SVFVar given its ID; asserts if no such variable exists.
const CallSiteToFunPtrMap & getIndirectCallsites() const
Add/get indirect callsites.
Definition SVFIR.h:451
const SVFVar * getSVFVar(NodeID id) const
ObjVar/GepObjVar/BaseObjVar.
Definition SVFIR.h:133

◆ getFullCycleHeadState()

AbstractState AbstractInterpretation::getFullCycleHeadState ( const ICFGCycleWTO cycle)
protectedvirtual

Build a full cycle-head AbstractState. Dense default: trace[cycle_head] as-is. Semi-sparse subclass: also pull cycle ValVars from def-sites.

Reimplemented in SVF::SemiSparseAbstractInterpretation.

Definition at line 162 of file AELoopRecursion.cpp.

163{
164 const ICFGNode* cycle_head = cycle->head()->getICFGNode();
168 return snap;
169}

◆ getGepByteOffset()

IntervalValue AbstractInterpretation::getGepByteOffset ( const GepStmt gep)

Definition at line 253 of file AbstractStateManager.cpp.

254{
255 const ICFGNode* node = gep->getICFGNode();
256 if (gep->isConstantOffset())
257 return IntervalValue((s64_t)gep->accumulateConstantByteOffset());
258
259 IntervalValue res(0);
260 for (int i = gep->getOffsetVarAndGepTypePairVec().size() - 1; i >= 0; i--)
261 {
262 const ValVar* idxOperandVar = gep->getOffsetVarAndGepTypePairVec()[i].first;
263 const SVFType* idxOperandType = gep->getOffsetVarAndGepTypePairVec()[i].second;
264
265 if (SVFUtil::isa<SVFArrayType>(idxOperandType) || SVFUtil::isa<SVFPointerType>(idxOperandType))
266 {
268 if (const SVFArrayType* arrOperandType = SVFUtil::dyn_cast<SVFArrayType>(idxOperandType))
269 elemByteSize = arrOperandType->getTypeOfElement()->getByteSize();
270 else if (SVFUtil::isa<SVFPointerType>(idxOperandType))
271 elemByteSize = gep->getAccessPath().gepSrcPointeeType()->getByteSize();
272 else
273 assert(false && "idxOperandType must be ArrType or PtrType");
274
275 if (const ConstIntValVar* op = SVFUtil::dyn_cast<ConstIntValVar>(idxOperandVar))
276 {
277 s64_t lb = (double)Options::MaxFieldLimit() / elemByteSize >= op->getSExtValue()
278 ? op->getSExtValue() * elemByteSize
280 res = res + IntervalValue(lb, lb);
281 }
282 else
283 {
285 if (idxVal.isBottom())
286 res = res + IntervalValue(0, 0);
287 else
288 {
289 s64_t ub = (idxVal.ub().getIntNumeral() < 0) ? 0
290 : (double)Options::MaxFieldLimit() / elemByteSize >= idxVal.ub().getIntNumeral()
291 ? elemByteSize * idxVal.ub().getIntNumeral()
292 : Options::MaxFieldLimit();
293 s64_t lb = (idxVal.lb().getIntNumeral() < 0) ? 0
294 : (double)Options::MaxFieldLimit() / elemByteSize >= idxVal.lb().getIntNumeral()
295 ? elemByteSize * idxVal.lb().getIntNumeral()
296 : Options::MaxFieldLimit();
297 res = res + IntervalValue(lb, ub);
298 }
299 }
300 }
301 else if (const SVFStructType* structOperandType = SVFUtil::dyn_cast<SVFStructType>(idxOperandType))
302 {
303 res = res + IntervalValue(gep->getAccessPath().getStructFieldOffset(idxOperandVar, structOperandType));
304 }
305 else
306 {
307 assert(false && "gep type pair only support arr/ptr/struct");
308 }
309 }
310 return res;
311}
IntervalValue & getInterval()
Carries around command line options.
Definition Options.h:17
u32_t getByteSize() const
Definition SVFType.h:289

◆ getGepElementIndex()

IntervalValue AbstractInterpretation::getGepElementIndex ( const GepStmt gep)

Definition at line 196 of file AbstractStateManager.cpp.

197{
198 const ICFGNode* node = gep->getICFGNode();
199 if (gep->isConstantOffset())
200 return IntervalValue((s64_t)gep->accumulateConstantOffset());
201
202 IntervalValue res(0);
203 for (int i = gep->getOffsetVarAndGepTypePairVec().size() - 1; i >= 0; i--)
204 {
205 const ValVar* var = gep->getOffsetVarAndGepTypePairVec()[i].first;
206 const SVFType* type = gep->getOffsetVarAndGepTypePairVec()[i].second;
207
209 if (const ConstIntValVar* constInt = SVFUtil::dyn_cast<ConstIntValVar>(var))
210 idxLb = idxUb = constInt->getSExtValue();
211 else
212 {
214 if (idxItv.isBottom())
215 idxLb = idxUb = 0;
216 else
217 {
219 idxUb = idxItv.ub().getIntNumeral();
220 }
221 }
222
223 if (SVFUtil::isa<SVFPointerType>(type))
224 {
225 u32_t elemNum = gep->getAccessPath().getElementNum(gep->getAccessPath().gepSrcPointeeType());
226 idxLb = (double)Options::MaxFieldLimit() / elemNum < idxLb ? Options::MaxFieldLimit() : idxLb * elemNum;
227 idxUb = (double)Options::MaxFieldLimit() / elemNum < idxUb ? Options::MaxFieldLimit() : idxUb * elemNum;
228 }
229 else
230 {
232 {
233 const std::vector<u32_t>& so = PAG::getPAG()->getTypeInfo(type)->getFlattenedElemIdxVec();
234 if (so.empty() || idxUb >= (APOffset)so.size() || idxLb < 0)
235 idxLb = idxUb = 0;
236 else
237 {
240 }
241 }
242 else
243 idxLb = idxUb = 0;
244 }
245 res = res + IntervalValue(idxLb, idxUb);
246 }
247 res.meet_with(IntervalValue((s64_t)0, (s64_t)Options::MaxFieldLimit()));
248 if (res.isBottom())
249 res = IntervalValue(0);
250 return res;
251}
newitem type
Definition cJSON.cpp:2739
s64_t getIntNumeral() const
u32_t getFlattenedElemIdx(const SVFType *T, u32_t origId)
Flattened element idx of an array or struct by considering stride.
Definition IRGraph.cpp:144
const StInfo * getTypeInfo(const SVFType *T) const
Get struct info.
Definition IRGraph.cpp:242
const BoundedInt & lb() const
Return the lower bound.
static Option< bool > ModelArrays
Definition Options.h:185
std::vector< u32_t > & getFlattenedElemIdxVec()
Definition SVFType.h:125
s64_t APOffset
Definition GeneralType.h:60

◆ getGepObjAddrs()

AddressValue AbstractInterpretation::getGepObjAddrs ( const ValVar pointer,
IntervalValue  offset 
)

Definition at line 313 of file AbstractStateManager.cpp.

314{
315 const ICFGNode* node = pointer->getICFGNode();
318 APOffset lb = offset.lb().getIntNumeral() < Options::MaxFieldLimit() ? offset.lb().getIntNumeral()
320 APOffset ub = offset.ub().getIntNumeral() < Options::MaxFieldLimit() ? offset.ub().getIntNumeral()
322 for (APOffset i = lb; i <= ub; i++)
323 {
324 const AbstractValue& addrs = getAbsValue(pointer, node);
325 for (const auto& addr : addrs.getAddrs())
326 {
327 s64_t baseObj = as.getIDFromAddr(addr);
328 assert(SVFUtil::isa<ObjVar>(svfir->getSVFVar(baseObj)) && "Fail to get the base object address!");
332 }
333 }
334 return gepAddrs;
335}
buffer offset
Definition cJSON.cpp:1113
const GepObjVar * getGepObjVar(NodeID id) const
Definition SVFIR.h:167
const ICFGNode * getICFGNode() const

◆ getPointeeElement()

const SVFType * AbstractInterpretation::getPointeeElement ( const ObjVar var,
const ICFGNode node 
)

Definition at line 358 of file AbstractStateManager.cpp.

359{
360 const AbstractValue& ptrVal = getAbsValue(var, node);
361 if (!ptrVal.isAddr())
362 return nullptr;
363 for (auto addr : ptrVal.getAddrs())
364 {
366 if (objId == 0)
367 continue;
368 return svfir->getBaseObject(objId)->getType();
369 }
370 return nullptr;
371}
u32_t getIDFromAddr(u32_t addr) const
Return the internal index if addr is an address otherwise return the value of idx.
const SVFType * getType() const
Get obj type.

◆ getSVFVar()

const SVFVar * SVF::AbstractInterpretation::getSVFVar ( NodeID  varId) const
inline

Retrieve SVFVar given its ID; asserts if no such variable exists.

Definition at line 130 of file AbstractInterpretation.h.

131 {
132 return svfir->getSVFVar(varId);
133 }

◆ getTrace()

Map< const ICFGNode *, AbstractState > & SVF::AbstractInterpretation::getTrace ( )
inline

Definition at line 192 of file AbstractInterpretation.h.

193 {
194 return abstractTrace;
195 }

◆ getUtils()

AbsExtAPI * SVF::AbstractInterpretation::getUtils ( )
inlineprivate

Definition at line 310 of file AbstractInterpretation.h.

311 {
312 return utils;
313 }

◆ handleCallSite()

void AbstractInterpretation::handleCallSite ( const ICFGNode node)
privatevirtual

Handle a call site node: dispatch to ext-call, direct-call, or indirect-call handling.

Definition at line 809 of file AbstractInterpretation.cpp.

810{
811 if (const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(node))
812 {
813 if (isExtCall(callNode))
814 {
816 }
817 else
818 {
819 // Handle both direct and indirect calls uniformly
821 }
822 }
823 else
824 assert (false && "it is not call node");
825}
virtual void handleFunCall(const CallICFGNode *callNode)
virtual bool isExtCall(const CallICFGNode *callNode)
virtual void handleExtCall(const CallICFGNode *callNode)

◆ handleExtCall()

void AbstractInterpretation::handleExtCall ( const CallICFGNode callNode)
privatevirtual

Definition at line 832 of file AbstractInterpretation.cpp.

833{
835 for (auto& detector : detectors)
836 {
837 detector->handleStubFunctions(callNode);
838 }
839}
void handleExtAPI(const CallICFGNode *call)
Handles an external API call.

◆ handleFunCall()

void AbstractInterpretation::handleFunCall ( const CallICFGNode callNode)
privatevirtual

Handle direct or indirect call: get callee(s), process function body, set return state.

For direct calls, the callee is known statically. For indirect calls, the previous implementation resolved callees from the abstract state's address domain, which only picked the first address and missed other targets. Since the abstract state's address domain is not an over-approximation for function pointers (it may be uninitialized or incomplete), we now use Andersen's pointer analysis results from the pre-computed call graph, which soundly resolves all possible indirect call targets.

Definition at line 876 of file AbstractInterpretation.cpp.

877{
879 return;
880
881 // Direct call: callee is known
882 if (const FunObjVar* callee = callNode->getCalledFunction())
883 {
886 const RetICFGNode* retNode = callNode->getRetICFGNode();
888 return;
889 }
890
891 // Indirect call: use Andersen's call graph to get all resolved callees.
892 const RetICFGNode* retNode = callNode->getRetICFGNode();
894 {
896 for (const FunObjVar* callee : callees)
897 {
898 if (callee->isDeclaration())
899 continue;
902 }
903 }
904 // Resume return node from caller's state (context-insensitive)
906}
bool skipRecursiveCall(const CallICFGNode *callNode)
Skip recursive callsites (within SCC); entry calls from outside SCC are not skipped.
bool hasIndCSCallees(const CallICFGNode *cs) const
Definition CallGraph.h:335
const FunctionSet & getIndCSCallees(const CallICFGNode *cs) const
Definition CallGraph.h:339

◆ handleFunction()

void AbstractInterpretation::handleFunction ( const ICFGNode funEntry,
const CallICFGNode caller 
)
private

Handle a function body via worklist-driven WTO traversal starting from funEntry.

Handle a function using worklist algorithm guided by WTO order. All top-level WTO components are pushed into the worklist upfront, so the traversal order is exactly the WTO order — each node is visited once, and cycles are handled as whole components.

Definition at line 782 of file AbstractInterpretation.cpp.

783{
784 auto it = preAnalysis->getFuncToWTO().find(funEntry->getFun());
785 assert(it != preAnalysis->getFuncToWTO().end() && "Missing WTO for function");
786
787 // Push all top-level WTO components into the worklist in WTO order
788 FIFOWorkList<const ICFGWTOComp*> worklist(it->second->getWTOComponents());
789
790 while (!worklist.empty())
791 {
792 const ICFGWTOComp* comp = worklist.pop();
793
794 if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
795 {
796 const ICFGNode* node = singleton->getICFGNode();
798 handleICFGNode(node);
799 }
800 else if (const ICFGCycleWTO* cycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
801 {
802 if (mergeStatesFromPredecessors(cycle->head()->getICFGNode()))
804 }
805 }
806}
const Map< const FunObjVar *, const ICFGWTO * > & getFuncToWTO() const
Accessors for WTO data.
Definition AEWTO.h:72
bool handleICFGNode(const ICFGNode *node)
Handle an ICFG node: execute statements; return true if state changed.
virtual bool mergeStatesFromPredecessors(const ICFGNode *node)
virtual void handleLoopOrRecursion(const ICFGCycleWTO *cycle, const CallICFGNode *caller)
Handle a WTO cycle (loop or recursive function) using widening/narrowing iteration.
WTONode< ICFG > ICFGSingletonWTO
Definition ICFGWTO.h:45
WTOComponent< ICFG > ICFGWTOComp
Definition ICFGWTO.h:44

◆ handleGlobalNode()

void AbstractInterpretation::handleGlobalNode ( )
privatevirtual

Initialize abstract state for the global ICFG node and process global statements

handle global node Initializes the abstract state for the global ICFG node and processes all global statements. This includes setting up the null pointer and black hole pointer (blkPtr). BlkPtr is initialized to point to the BlackHole object, representing an unknown memory location that cannot be statically resolved.

Definition at line 235 of file AbstractInterpretation.cpp.

236{
237 const ICFGNode* node = icfg->getGlobalICFGNode();
238 // Global init is one of the few legitimate direct-mutation sites:
239 // updateAbsState filters out ValVars in semi-sparse mode, but NullPtr/
240 // BlkPtr have no SVFVar so we cannot route them through updateAbsValue.
241 // Use the manager's operator[] (auto-creates the entry if absent).
242 AbstractState& init = abstractTrace[node];
243 init = AbstractState();
244 // TODO: we cannot find right SVFVar for NullPtr, so we use init[NullPtr]
245 // directly. Same for BlkPtr below.
247
248 // Global Node, we just need to handle addr, load, store, copy and gep
249 for (const SVFStmt *stmt: node->getSVFStmts())
250 {
252 }
253
254 // BlkPtr is the canonical unknown value. Keep its address-domain meaning
255 // for pointer uses, and also give it numeric top so external-input stores
256 // can flow through ordinary store/load state as [-inf, +inf].
258 blkPtrValue.getAddrs().insert(BlackHoleObjAddr);
260}
#define BlackHoleObjAddr
virtual void handleSVFStatement(const SVFStmt *stmt)
Dispatch an SVF statement (Addr/Binary/Cmp/Load/Store/Copy/Gep/Select/Phi/Call/Ret) to its handler.
NodeID getBlkPtr() const
Definition IRGraph.h:255

◆ handleICFGNode()

bool AbstractInterpretation::handleICFGNode ( const ICFGNode node)
private

Handle an ICFG node: execute statements; return true if state changed.

Handle an ICFG node: execute statements on the current abstract state. The node's pre-state must already be in getAbsState(node) (set by mergeStatesFromPredecessors, or by handleGlobalNode for the global node). Returns true if the abstract state has changed, false if fixpoint reached or unreachable.

Definition at line 723 of file AbstractInterpretation.cpp.

724{
725 // Check reachability: pre-state must have been propagated by predecessors
726 bool isFunEntry = SVFUtil::isa<FunEntryICFGNode>(node);
727 if (!hasAbsState(node))
728 {
729 if (isFunEntry)
730 {
731 // Entry point with no callers: inherit from global node
735 else
737 }
738 else
739 {
740 return false; // unreachable node
741 }
742 }
743
744 // Store the previous state for fixpoint detection
746
747 stat->getBlockTrace()++;
749
750 // Handle SVF statements
751 for (const SVFStmt *stmt: node->getSVFStmts())
752 {
754 }
755
756 // Handle call sites
757 if (const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(node))
758 {
760 }
761
762 // Run detectors
763 for (auto& detector: detectors)
764 detector->detect(node);
766
767 // Track this node as analyzed (for coverage statistics across all entry points)
768 allAnalyzedNodes.insert(node);
769
770 if (getAbsState(node) == prevState)
771 return false;
772
773 return true;
774}
u32_t & getBlockTrace()
Definition AEStat.h:70
u32_t & getICFGNodeTrace()
Definition AEStat.h:78
void countStateSize()
Definition AEStat.cpp:31
virtual void handleCallSite(const ICFGNode *node)
Handle a call site node: dispatch to ext-call, direct-call, or indirect-call handling.
Set< const ICFGNode * > allAnalyzedNodes

◆ handleLoopOrRecursion()

void AbstractInterpretation::handleLoopOrRecursion ( const ICFGCycleWTO cycle,
const CallICFGNode caller 
)
privatevirtual

Handle a WTO cycle (loop or recursive function) using widening/narrowing iteration.

Definition at line 235 of file AELoopRecursion.cpp.

236{
237 const ICFGNode* cycle_head = cycle->head()->getICFGNode();
238
239 // TOP mode for recursive function cycles: set all stores and return value to TOP
240 if (Options::HandleRecur() == TOP && isRecursiveFun(cycle_head->getFun()))
241 {
242 if (caller)
244 return;
245 }
246
247 // Iterate until fixpoint with widening/narrowing on the cycle head.
248 bool increasing = true;
250 for (u32_t cur_iter = 0;; cur_iter++)
251 {
252 if (cur_iter >= widen_delay)
253 {
254 // getFullCycleHeadState handles dense (returns trace[cycle_head])
255 // and semi-sparse (collects ValVars from def-sites) uniformly.
257
261
262 if (increasing)
263 {
264 if (widenCycleState(prev, cur, cycle))
265 {
266 increasing = false;
267 continue;
268 }
269 }
270 else
271 {
272 if (narrowCycleState(prev, cur, cycle))
273 break;
274 }
275 }
276 else
277 {
278 // Before widen_delay: process cycle head with gated pattern
281 }
282
283 // Process cycle body components (each with gated merge+handle)
284 for (const ICFGWTOComp* comp : cycle->getWTOComponents())
285 {
286 if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
287 {
288 const ICFGNode* node = singleton->getICFGNode();
290 handleICFGNode(node);
291 }
292 else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
293 {
294 if (mergeStatesFromPredecessors(subCycle->head()->getICFGNode()))
296 }
297 }
298 }
299}
newitem prev
Definition cJSON.cpp:2285
virtual bool narrowCycleState(const AbstractState &prev, const AbstractState &cur, const ICFGCycleWTO *cycle)
virtual AbstractState getFullCycleHeadState(const ICFGCycleWTO *cycle)
virtual void skipRecursionWithTop(const CallICFGNode *callNode)
virtual bool widenCycleState(const AbstractState &prev, const AbstractState &cur, const ICFGCycleWTO *cycle)
virtual bool isRecursiveFun(const FunObjVar *fun)
Check if a function is recursive (part of a call graph SCC)
static const OptionMap< u32_t > HandleRecur
recursion handling mode, Default: TOP
Definition Options.h:247
static const Option< u32_t > WidenDelay
Definition Options.h:245

◆ handleSVFStatement()

void AbstractInterpretation::handleSVFStatement ( const SVFStmt stmt)
privatevirtual

Dispatch an SVF statement (Addr/Binary/Cmp/Load/Store/Copy/Gep/Select/Phi/Call/Ret) to its handler.

Definition at line 911 of file AbstractInterpretation.cpp.

912{
913 if (const AddrStmt *addr = SVFUtil::dyn_cast<AddrStmt>(stmt))
914 {
916 }
917 else if (const BinaryOPStmt *binary = SVFUtil::dyn_cast<BinaryOPStmt>(stmt))
918 {
920 }
921 else if (const CmpStmt *cmp = SVFUtil::dyn_cast<CmpStmt>(stmt))
922 {
924 }
925 else if (SVFUtil::isa<UnaryOPStmt>(stmt))
926 {
927 }
928 else if (SVFUtil::isa<BranchStmt>(stmt))
929 {
930 // branch stmt is handled in hasBranchES
931 }
932 else if (const LoadStmt *load = SVFUtil::dyn_cast<LoadStmt>(stmt))
933 {
934 updateStateOnLoad(load);
935 }
936 else if (const StoreStmt *store = SVFUtil::dyn_cast<StoreStmt>(stmt))
937 {
938 updateStateOnStore(store);
939 }
940 else if (const CopyStmt *copy = SVFUtil::dyn_cast<CopyStmt>(stmt))
941 {
943 }
944 else if (const GepStmt *gep = SVFUtil::dyn_cast<GepStmt>(stmt))
945 {
947 }
948 else if (const SelectStmt *select = SVFUtil::dyn_cast<SelectStmt>(stmt))
949 {
951 }
952 else if (const PhiStmt *phi = SVFUtil::dyn_cast<PhiStmt>(stmt))
953 {
955 }
956 else if (const CallPE *callPE = SVFUtil::dyn_cast<CallPE>(stmt))
957 {
958 // To handle Call Edge
959 updateStateOnCall(callPE);
960 }
961 else if (const RetPE *retPE = SVFUtil::dyn_cast<RetPE>(stmt))
962 {
963 updateStateOnRet(retPE);
964 }
965 else
966 assert(false && "implement this part");
967 // NullPtr should not be changed by any statement. If the entry is missing
968 // (not yet auto-inserted) we treat that as "unchanged" — only check the
969 // entry if it actually exists.
970 {
971 const auto& vmap = getAbsState(stmt->getICFGNode()).getVarToVal();
972 auto it = vmap.find(IRGraph::NullPtr);
973 assert(it == vmap.end() ||
974 (!it->second.isInterval() && !it->second.isAddr()));
975 }
976}
copy
Definition cJSON.cpp:414
void updateStateOnCall(const CallPE *callPE)
void updateStateOnStore(const StoreStmt *store)
void updateStateOnGep(const GepStmt *gep)
void updateStateOnPhi(const PhiStmt *phi)
void updateStateOnSelect(const SelectStmt *select)
void updateStateOnAddr(const AddrStmt *addr)
void updateStateOnRet(const RetPE *retPE)
void updateStateOnCopy(const CopyStmt *copy)
void updateStateOnLoad(const LoadStmt *load)
void updateStateOnBinary(const BinaryOPStmt *binary)
void updateStateOnCmp(const CmpStmt *cmp)
const VarToAbsValMap & getVarToVal() const
get var2val map

◆ hasAbsState()

bool AbstractInterpretation::hasAbsState ( const ICFGNode node)

Definition at line 64 of file AbstractStateManager.cpp.

65{
66 return abstractTrace.count(node) != 0;
67}

◆ hasAbsValue() [1/3]

bool AbstractInterpretation::hasAbsValue ( const ObjVar var,
const ICFGNode node 
) const
virtual

Reimplemented in SVF::SemiSparseAbstractInterpretation.

Definition at line 119 of file AbstractStateManager.cpp.

120{
121 auto it = abstractTrace.find(node);
122 if (it == abstractTrace.end())
123 return false;
124 return it->second.getLocToVal().count(var->getId()) != 0;
125}

◆ hasAbsValue() [2/3]

bool AbstractInterpretation::hasAbsValue ( const SVFVar var,
const ICFGNode node 
) const
virtual

Reimplemented in SVF::SemiSparseAbstractInterpretation.

Definition at line 127 of file AbstractStateManager.cpp.

128{
129 if (const ObjVar* objVar = SVFUtil::dyn_cast<ObjVar>(var))
130 return hasAbsValue(objVar, node);
131 if (const ValVar* valVar = SVFUtil::dyn_cast<ValVar>(var))
132 return hasAbsValue(valVar, node);
133 return false;
134}
virtual bool hasAbsValue(const ValVar *var, const ICFGNode *node) const
Side-effect-free existence check.

◆ hasAbsValue() [3/3]

bool AbstractInterpretation::hasAbsValue ( const ValVar var,
const ICFGNode node 
) const
virtual

Side-effect-free existence check.

Dense base: direct existence check at node. Mirrors the simplified getAbsValue lookup — uses raw map.contains rather than inVar*Table predicates, which would falsely report neutral (interval=bottom ∧ addrs=∅) entries as "not present".

Reimplemented in SVF::SemiSparseAbstractInterpretation, and SVF::SemiSparseAbstractInterpretation.

Definition at line 111 of file AbstractStateManager.cpp.

112{
113 auto it = abstractTrace.find(node);
114 if (it == abstractTrace.end())
115 return false;
116 return it->second.getVarToVal().count(var->getId()) != 0;
117}

◆ isBranchEdgeFeasible()

bool AbstractInterpretation::isBranchEdgeFeasible ( const IntraCFGEdge edge,
AbstractState as 
)
protected

Returns true if the branch edge is reachable under the current state. Pure query: does not update as or branch refinement traces.

Definition at line 707 of file AbstractInterpretation.cpp.

709{
710 const SVFVar* cmpVar = edge->getCondition();
711 assert(!cmpVar->getInEdges().empty() && "branch condition has no defining edge?");
712 if (SVFUtil::isa<CmpStmt>(*cmpVar->getInEdges().begin()))
715}
bool isCmpBranchEdgeFeasible(const IntraCFGEdge *edge, AbstractState &as)
Returns true if the cmp-conditional branch is feasible.
bool isSwitchBranchEdgeFeasible(const IntraCFGEdge *edge, AbstractState &as)
Returns true if the switch branch is feasible.

◆ isCmpBranchEdgeFeasible()

bool AbstractInterpretation::isCmpBranchEdgeFeasible ( const IntraCFGEdge edge,
AbstractState as 
)
private

Returns true if the cmp-conditional branch is feasible.

Definition at line 491 of file AbstractInterpretation.cpp.

493{
494 const ICFGNode* pred = edge->getSrcNode();
495 s64_t succ = edge->getSuccessorCondValue();
496 const CmpStmt* cmpStmt = SVFUtil::cast<CmpStmt>(
497 *edge->getCondition()->getInEdges().begin());
498
499 if (cmpStmt->getOpVarID(0) == IRGraph::NullPtr ||
500 cmpStmt->getOpVarID(1) == IRGraph::NullPtr)
501 return true;
502
504 {
505 getAbsValue(cmpStmt->getOpVar(0), pred),
506 getAbsValue(cmpStmt->getOpVar(1), pred)
507 };
508
509 const bool hasIntervalCmp = opVal[0].isInterval() && opVal[1].isInterval();
510 if (!hasIntervalCmp && (opVal[0].isAddr() || opVal[1].isAddr()))
511 return true;
512
513 // Feasibility check: cmp result must be compatible with branch successor
516 if (resVal.isBottom())
517 return false;
518
519 return true;
520}

◆ isExtCall()

bool AbstractInterpretation::isExtCall ( const CallICFGNode callNode)
privatevirtual

Definition at line 827 of file AbstractInterpretation.cpp.

828{
829 return SVFUtil::isExtCall(callNode->getCalledFunction());
830}
bool isExtCall(const FunObjVar *fun)
Definition SVFUtil.cpp:437

◆ isRecursiveCallSite()

bool AbstractInterpretation::isRecursiveCallSite ( const CallICFGNode callNode,
const FunObjVar callee 
)
privatevirtual

Check if caller and callee are in the same CallGraph SCC (i.e. a recursive callsite)

Definition at line 104 of file AELoopRecursion.cpp.

106{
107 const FunObjVar* caller = callNode->getCaller();
109}
AndersenWaveDiff * getPointerAnalysis() const
Accessors for Andersen's results.
Definition AEWTO.h:56
bool inSameCallGraphSCC(const FunObjVar *fun1, const FunObjVar *fun2)
Return TRUE if this edge is inside a PTACallGraph SCC, i.e., src node and dst node are in the same SC...

◆ isRecursiveFun()

bool AbstractInterpretation::isRecursiveFun ( const FunObjVar fun)
privatevirtual

Check if a function is recursive (part of a call graph SCC)

Definition at line 47 of file AELoopRecursion.cpp.

48{
50}
bool isInRecursion(const FunObjVar *fun) const

◆ isSwitchBranchEdgeFeasible()

bool AbstractInterpretation::isSwitchBranchEdgeFeasible ( const IntraCFGEdge edge,
AbstractState as 
)
private

Returns true if the switch branch is feasible.

Definition at line 522 of file AbstractInterpretation.cpp.

524{
525 const ICFGNode* pred = edge->getSrcNode();
526 s64_t succ = edge->getSuccessorCondValue();
527 const SVFVar* var = edge->getCondition();
528
530 IntervalValue switch_cond = condVal.getInterval();
532 if (switch_cond.isBottom())
533 return false;
534 return true;
535}

◆ joinStates()

void AbstractInterpretation::joinStates ( AbstractState dst,
const AbstractState src 
)
virtual

Join src into dst with sparsity-aware semantics. Dense merges everything; semi-sparse skips ValVars.

Reimplemented in SVF::SemiSparseAbstractInterpretation, and SVF::FullSparseAbstractInterpretation.

Definition at line 59 of file AbstractStateManager.cpp.

60{
61 dst.joinWith(src);
62}
void joinWith(const AbstractState &other)
domain join with other, important! other widen this.

◆ loadValue()

AbstractValue AbstractInterpretation::loadValue ( const ValVar pointer,
const ICFGNode node 
)
virtual

Virtual so full-sparse can layer the GepObj overlay on top.

Definition at line 337 of file AbstractStateManager.cpp.

338{
339 const AbstractValue& ptrVal = getAbsValue(pointer, node);
341 AbstractValue res;
342 for (auto addr : ptrVal.getAddrs())
343 {
344 res.join_with(
345 getAbsValue(svfir->getSVFVar(as.getIDFromAddr(addr)), node));
346 }
347 return res;
348}
void join_with(const AbstractValue &other)

◆ mergeStatesFromPredecessors()

bool AbstractInterpretation::mergeStatesFromPredecessors ( const ICFGNode node)
protectedvirtual

Pull-based state merge: read abstractTrace[pred] for each predecessor, apply branch refinement for conditional IntraCFGEdges, and join into abstractTrace[node]. Returns true if at least one predecessor had state. Virtual so full-sparse can layer per-MRSVFGNode obj pulls on top of the base ICFG-edge merge.

Pull-based state merge: for each predecessor that has an abstract state, copy its state, apply branch refinement for conditional IntraCFGEdges, and join all feasible states into getAbsState(node). The join is dispatched through the manager so semi-sparse can skip ValVar merging. Returns true if at least one predecessor contributed state.

Reimplemented in SVF::FullSparseAbstractInterpretation.

Definition at line 268 of file AbstractInterpretation.cpp.

269{
270 // Collect all feasible predecessor states, then merge at the end.
272 bool hasFeasiblePred = false;
273
274 for (auto& edge : node->getInEdges())
275 {
276 const ICFGNode* pred = edge->getSrcNode();
277 if (!hasAbsState(pred))
278 continue;
279
280 if (const IntraCFGEdge* intraCfgEdge = SVFUtil::dyn_cast<IntraCFGEdge>(edge))
281 {
282 if (intraCfgEdge->getCondition())
283 {
286 {
289 hasFeasiblePred = true;
290 }
291 }
292 else
293 {
295 hasFeasiblePred = true;
296 }
297 }
298 else if (SVFUtil::isa<CallCFGEdge>(edge))
299 {
301 hasFeasiblePred = true;
302 }
303 else if (SVFUtil::isa<RetCFGEdge>(edge))
304 {
305 switch (Options::HandleRecur())
306 {
307 case TOP:
309 hasFeasiblePred = true;
310 break;
311 case WIDEN_ONLY:
312 case WIDEN_NARROW:
313 {
314 const RetICFGNode* returnSite = SVFUtil::dyn_cast<RetICFGNode>(node);
315 const CallICFGNode* callSite = returnSite->getCallICFGNode();
317 {
319 hasFeasiblePred = true;
320 }
321 break;
322 }
323 }
324 }
325 }
326
327 if (!hasFeasiblePred)
328 return false;
329
330 updateAbsState(node, merged);
331
332 return true;
333}
bool isBranchEdgeFeasible(const IntraCFGEdge *edge, AbstractState &as)
virtual void joinStates(AbstractState &dst, const AbstractState &src)
void collectBranchRefinement(const IntraCFGEdge *edge, AbstractState &as)

◆ narrowCycleState()

bool AbstractInterpretation::narrowCycleState ( const AbstractState prev,
const AbstractState cur,
const ICFGCycleWTO cycle 
)
protectedvirtual

Narrow prev with cur; write the narrowed state back. Returns true when narrowing is disabled or the narrowed state equals prev. Semi-sparse subclass scatters the narrowed ValVars on non-fixpoint.

Reimplemented in SVF::SemiSparseAbstractInterpretation.

Definition at line 183 of file AELoopRecursion.cpp.

185{
186 const ICFGNode* cycle_head = cycle->head()->getICFGNode();
187 if (!shouldApplyNarrowing(cycle_head->getFun()))
188 return true;
191 if (next == prev)
192 return true; // fixpoint
194 return false;
195}
item next
Definition cJSON.cpp:2224
bool shouldApplyNarrowing(const FunObjVar *fun)
Check if narrowing should be applied: always for regular loops, mode-dependent for recursion.
AbstractState narrowing(const AbstractState &other)
domain narrow with other, and return the narrowed domain

◆ operator[]()

AbstractState & SVF::AbstractInterpretation::operator[] ( const ICFGNode node)
inline

Definition at line 196 of file AbstractInterpretation.h.

197 {
198 return abstractTrace[node];
199 }

◆ recordBranchRefinement()

void AbstractInterpretation::recordBranchRefinement ( NodeID  objId,
const IntervalValue narrowed,
AbstractState as,
const ICFGNode loadIcfg,
const ICFGNode succ 
)
protectedvirtual

Hook called by collectBranchRefinement for each obj that the branch narrows. Default (dense/semi): MEET narrowed onto obj's value (read at loadIcfg where sparse keeps it) and write the result into the local as (per-edge predState copy) so joinStates carries it to succ. FullSparse overrides to capture into refinementTrace[succ] instead.

Reimplemented in SVF::FullSparseAbstractInterpretation.

Definition at line 674 of file AbstractInterpretation.cpp.

677{
678 // Default (dense / semi-sparse): MEET narrowed onto obj's current
679 // value, store back into the local `as`. Caller's joinStates
680 // propagates `as` into `merged`, then `updateAbsState(succ, merged)`
681 // commits it to trace[succ].
682 //
683 // We can't go through the polymorphic updateAbsValue here: `as` is
684 // a transient per-edge predState copy that lives outside
685 // abstractTrace, so it has no node id. Writing via `updateAbsValue`
686 // with `succ` as the node would land in trace[succ] but get
687 // clobbered by the subsequent `updateAbsState(succ, merged)`; with
688 // `loadIcfg` it would corrupt the obj's authoritative value at its
689 // load site. AbstractState::store on the transient `as` is the
690 // only sound primitive — and recordBranchRefinement itself is the
691 // virtual customisation point (FullSparse routes to
692 // refinementTrace instead of touching `as`).
693 const ObjVar* objVar = SVFUtil::dyn_cast<ObjVar>(svfir->getGNode(objId));
695 {
697 if (cur.isInterval())
698 {
702 as.store(addr, AbstractValue(itv));
703 }
704 }
705}

◆ runOnModule()

void AbstractInterpretation::runOnModule ( )
virtual

collect checkpoint

Definition at line 44 of file AbstractInterpretation.cpp.

45{
46 stat->startClk();
47 utils = new AbsExtAPI(this);
50
51 analyse();
53 stat->endClk();
55 if (Options::PStat())
57 for (auto& detector: detectors)
58 detector->reportBug();
59}
void finializeStat()
Definition AEStat.cpp:44
void performStat() override
Definition AEStat.cpp:120
Handles external API calls and manages abstract states.
Definition AbsExtAPI.h:43
void collectCheckPoint()
void checkPointAllSet()
static const Option< bool > PStat
Definition Options.h:116
virtual void endClk()
Definition SVFStat.h:63
virtual void startClk()
Definition SVFStat.h:58

◆ shouldApplyNarrowing()

bool AbstractInterpretation::shouldApplyNarrowing ( const FunObjVar fun)
protected

Check if narrowing should be applied: always for regular loops, mode-dependent for recursion.

Definition at line 130 of file AELoopRecursion.cpp.

131{
132 // Non-recursive functions (regular loops): always apply narrowing
133 if (!isRecursiveFun(fun))
134 return true;
135
136 // Recursive functions: WIDEN_NARROW applies narrowing, WIDEN_ONLY does not
137 // TOP mode exits early in handleLoopOrRecursion, so should not reach here
138 switch (Options::HandleRecur())
139 {
140 case TOP:
141 assert(false && "TOP mode should not reach narrowing phase for recursive functions");
142 return false;
143 case WIDEN_ONLY:
144 return false; // Skip narrowing for recursive functions
145 case WIDEN_NARROW:
146 return true; // Apply narrowing for recursive functions
147 default:
148 assert(false && "Unknown recursion handling mode");
149 return false;
150 }
151}

◆ skipRecursionWithTop()

void AbstractInterpretation::skipRecursionWithTop ( const CallICFGNode callNode)
privatevirtual

TOP mode for recursive calls: skip the function body entirely and conservatively set all reachable stores and the return value to TOP.

Definition at line 54 of file AELoopRecursion.cpp.

55{
56 const RetICFGNode *retNode = callNode->getRetICFGNode();
57
58 // 1. Set return value to TOP
59 if (retNode->getSVFStmts().size() > 0)
60 {
61 if (const RetPE *retPE = SVFUtil::dyn_cast<RetPE>(*retNode->getSVFStmts().begin()))
62 {
63 if (!retPE->getLHSVar()->isPointer() &&
64 !retPE->getLHSVar()->isConstDataOrAggDataButNotNullPtr())
65 updateAbsValue(retPE->getLHSVar(), IntervalValue::top(), callNode);
66 }
67 }
68
69 // 2. Set all stores in callee's reachable BBs to TOP
70 if (retNode->getOutEdges().size() > 1)
71 {
73 return;
74 }
75 for (const SVFBasicBlock* bb : callNode->getCalledFunction()->getReachableBBs())
76 {
77 for (const ICFGNode* node : bb->getICFGNodeList())
78 {
79 for (const SVFStmt* stmt : node->getSVFStmts())
80 {
81 if (const StoreStmt* store = SVFUtil::dyn_cast<StoreStmt>(stmt))
82 {
83 const SVFVar* rhsVar = store->getRHSVar();
84 if (!rhsVar->isPointer() && !rhsVar->isConstDataOrAggDataButNotNullPtr())
85 {
86 const AbstractValue& addrs = getAbsValue(store->getLHSVar(), callNode);
87 if (addrs.isAddr())
88 {
90 for (const auto& addr : addrs.getAddrs())
91 as.store(addr, IntervalValue::top());
92 }
93 }
94 }
95 }
96 }
97 }
98
99 // 3. Copy callNode's state to retNode
101}
virtual void updateAbsValue(const ValVar *var, const AbstractValue &val, const ICFGNode *node)
bool isAddr() const

◆ skipRecursiveCall()

bool AbstractInterpretation::skipRecursiveCall ( const CallICFGNode callNode)
private

Skip recursive callsites (within SCC); entry calls from outside SCC are not skipped.

Definition at line 112 of file AELoopRecursion.cpp.

113{
115 if (!callee)
116 return false;
117
118 // Non-recursive function: never skip, always inline
120 return false;
121
122 // For recursive functions, skip only recursive callsites (within same SCC).
123 // Entry calls (from outside SCC) are not skipped - they are inlined so that
124 // handleLoopOrRecursion() can analyze the function body.
125 // This applies uniformly to all modes (TOP/WIDEN_ONLY/WIDEN_NARROW).
127}
const FunObjVar * getCallee(const CallICFGNode *callNode)
Get callee function: directly for direct calls, via pointer analysis for indirect calls.
virtual bool isRecursiveCallSite(const CallICFGNode *callNode, const FunObjVar *)
Check if caller and callee are in the same CallGraph SCC (i.e. a recursive callsite)

◆ storeValue()

void AbstractInterpretation::storeValue ( const ValVar pointer,
const AbstractValue val,
const ICFGNode node 
)
virtual

Reimplemented in SVF::FullSparseAbstractInterpretation.

Definition at line 350 of file AbstractStateManager.cpp.

351{
352 const AbstractValue& ptrVal = getAbsValue(pointer, node);
354 for (auto addr : ptrVal.getAddrs())
355 updateAbsValue(svfir->getSVFVar(as.getIDFromAddr(addr)), val, node);
356}

◆ updateAbsState()

void AbstractInterpretation::updateAbsState ( const ICFGNode node,
const AbstractState state 
)
virtual

Replace the state at node. Sparse subclasses replace only the ObjVar map (ValVars live at def-sites).

Reimplemented in SVF::SemiSparseAbstractInterpretation.

Definition at line 54 of file AbstractStateManager.cpp.

55{
56 abstractTrace[node] = state;
57}

◆ updateAbsValue() [1/3]

void AbstractInterpretation::updateAbsValue ( const ObjVar var,
const AbstractValue val,
const ICFGNode node 
)
virtual

Reimplemented in SVF::SemiSparseAbstractInterpretation.

Definition at line 141 of file AbstractStateManager.cpp.

142{
145 as.store(addr, val);
146}

◆ updateAbsValue() [2/3]

void AbstractInterpretation::updateAbsValue ( const SVFVar var,
const AbstractValue val,
const ICFGNode node 
)
virtual

Reimplemented in SVF::SemiSparseAbstractInterpretation.

Definition at line 148 of file AbstractStateManager.cpp.

149{
150 if (const ObjVar* objVar = SVFUtil::dyn_cast<ObjVar>(var))
151 updateAbsValue(objVar, val, node);
152 else if (const ValVar* valVar = SVFUtil::dyn_cast<ValVar>(var))
153 updateAbsValue(valVar, val, node);
154 else
155 assert(false && "Unknown SVFVar kind");
156}

◆ updateAbsValue() [3/3]

void AbstractInterpretation::updateAbsValue ( const ValVar var,
const AbstractValue val,
const ICFGNode node 
)
virtual

Write a variable's abstract value. Sparse subclasses re-route ValVar writes to the def-site.

Reimplemented in SVF::SemiSparseAbstractInterpretation, and SVF::SemiSparseAbstractInterpretation.

Definition at line 136 of file AbstractStateManager.cpp.

137{
138 abstractTrace[node][var->getId()] = val;
139}

◆ updateStateOnAddr()

void AbstractInterpretation::updateStateOnAddr ( const AddrStmt addr)
private

Definition at line 1065 of file AbstractInterpretation.cpp.

1066{
1067 const ICFGNode* node = addr->getICFGNode();
1068 // initObjVar mutates _varToAbsVal/_addrToAbsVal directly, so we need
1069 // mutable access; route via the manager.
1070 AbstractState& as = getAbsState(node);
1071 as.initObjVar(SVFUtil::cast<ObjVar>(addr->getRHSVar()));
1072 // AddrStmt: lhs(ValVar) = &rhs(ObjVar).
1073 // as[rhsId] stores the ObjVar's virtual address in _varToVal,
1074 // NOT the object contents. So we must use as[] directly for ObjVar.
1075 u32_t rhsId = addr->getRHSVarID();
1076 if (addr->getRHSVar()->getType()->getKind() == SVFType::SVFIntegerTy)
1077 as[rhsId].getInterval().meet_with(utils->getRangeLimitFromType(addr->getRHSVar()->getType()));
1078 // LHS is a ValVar (pointer), write through the API
1079 updateAbsValue(addr->getLHSVar(), as[rhsId], node);
1080}
IntervalValue getRangeLimitFromType(const SVFType *type)
Gets the range limit from a type.

◆ updateStateOnBinary()

void AbstractInterpretation::updateStateOnBinary ( const BinaryOPStmt binary)
private

Definition at line 1083 of file AbstractInterpretation.cpp.

1084{
1085 const ICFGNode* node = binary->getICFGNode();
1086 // Treat bottom (uninitialized) operands as top for soundness
1087 const AbstractValue& op0Val = getAbsValue(binary->getOpVar(0), node);
1088 const AbstractValue& op1Val = getAbsValue(binary->getOpVar(1), node);
1089 IntervalValue lhs = op0Val.getInterval().isBottom() ? IntervalValue::top() : op0Val.getInterval();
1090 IntervalValue rhs = op1Val.getInterval().isBottom() ? IntervalValue::top() : op1Val.getInterval();
1092 switch (binary->getOpcode())
1093 {
1094 case BinaryOPStmt::Add:
1095 case BinaryOPStmt::FAdd:
1096 resVal = (lhs + rhs);
1097 break;
1098 case BinaryOPStmt::Sub:
1099 case BinaryOPStmt::FSub:
1100 resVal = (lhs - rhs);
1101 break;
1102 case BinaryOPStmt::Mul:
1103 case BinaryOPStmt::FMul:
1104 resVal = (lhs * rhs);
1105 break;
1106 case BinaryOPStmt::SDiv:
1107 case BinaryOPStmt::FDiv:
1108 case BinaryOPStmt::UDiv:
1109 resVal = (lhs / rhs);
1110 break;
1111 case BinaryOPStmt::SRem:
1112 case BinaryOPStmt::FRem:
1113 case BinaryOPStmt::URem:
1114 resVal = (lhs % rhs);
1115 break;
1116 case BinaryOPStmt::Xor:
1117 resVal = (lhs ^ rhs);
1118 break;
1119 case BinaryOPStmt::And:
1120 resVal = (lhs & rhs);
1121 break;
1122 case BinaryOPStmt::Or:
1123 resVal = (lhs | rhs);
1124 break;
1125 case BinaryOPStmt::AShr:
1126 resVal = (lhs >> rhs);
1127 break;
1128 case BinaryOPStmt::Shl:
1129 resVal = (lhs << rhs);
1130 break;
1131 case BinaryOPStmt::LShr:
1132 resVal = (lhs >> rhs);
1133 break;
1134 default:
1135 assert(false && "undefined binary: ");
1136 }
1137 updateAbsValue(binary->getRes(), resVal, node);
1138}
bool isBottom() const

◆ updateStateOnCall()

void AbstractInterpretation::updateStateOnCall ( const CallPE callPE)
private

Handle CallPE: phi-like merging of actual parameters from all call sites into the formal parameter at FunEntryICFGNode (e.g., formal = join(actual1@cs1, actual2@cs2, ...))

Definition at line 1040 of file AbstractInterpretation.cpp.

1041{
1042 const ICFGNode* node = callPE->getICFGNode();
1043 const SVFVar* res = callPE->getRes();
1045 for (u32_t i = 0; i < callPE->getOpVarNum(); i++)
1046 {
1047 const ICFGNode* opICFGNode = callPE->getOpCallICFGNode(i);
1049 {
1050 const AbstractValue& opVal = getAbsValue(callPE->getOpVar(i), opICFGNode);
1051 rhs.join_with(opVal);
1052 }
1053 }
1054 updateAbsValue(res, rhs, node);
1055}
const CallICFGNode * getOpCallICFGNode(u32_t op_idx) const
Return the CallICFGNode of the i-th operand.
const ValVar * getRes() const
Result SVFVar.
const ValVar * getOpVar(u32_t pos) const
Operand SVFVars.
u32_t getOpVarNum() const

◆ updateStateOnCmp()

void AbstractInterpretation::updateStateOnCmp ( const CmpStmt cmp)
private

Definition at line 1140 of file AbstractInterpretation.cpp.

1141{
1142 const ICFGNode* node = cmp->getICFGNode();
1143 u32_t op0 = cmp->getOpVarID(0);
1144 u32_t op1 = cmp->getOpVarID(1);
1145 const AbstractValue& op0Val = getAbsValue(cmp->getOpVar(0), node);
1146 const AbstractValue& op1Val = getAbsValue(cmp->getOpVar(1), node);
1147
1148 // if it is address
1149 if (op0Val.isAddr() && op1Val.isAddr())
1150 {
1152 const AddressValue& addrOp0 = op0Val.getAddrs();
1153 const AddressValue& addrOp1 = op1Val.getAddrs();
1154 if (addrOp0.equals(addrOp1))
1155 {
1156 resVal = IntervalValue(1, 1);
1157 }
1158 else if (addrOp0.hasIntersect(addrOp1))
1159 {
1160 resVal = IntervalValue(0, 1);
1161 }
1162 else
1163 {
1164 resVal = IntervalValue(0, 0);
1165 }
1166 updateAbsValue(cmp->getRes(), resVal, node);
1167 }
1168 // if op0 or op1 is nullptr, compare abstractValue instead of touching addr or interval
1169 else if (op0 == IRGraph::NullPtr || op1 == IRGraph::NullPtr)
1170 {
1171 IntervalValue resVal = (op0Val.equals(op1Val)) ? IntervalValue(1, 1) : IntervalValue(0, 0);
1172 updateAbsValue(cmp->getRes(), resVal, node);
1173 }
1174 else
1175 {
1176 {
1178 if (op0Val.isInterval() && op1Val.isInterval())
1179 {
1180 // Treat bottom (uninitialized) operands as top for soundness
1181 IntervalValue lhs = op0Val.getInterval().isBottom() ? IntervalValue::top() : op0Val.getInterval(),
1182 rhs = op1Val.getInterval().isBottom() ? IntervalValue::top() : op1Val.getInterval();
1183 // AbstractValue
1184 auto predicate = cmp->getPredicate();
1185 switch (predicate)
1186 {
1187 case CmpStmt::ICMP_EQ:
1188 case CmpStmt::FCMP_OEQ:
1189 case CmpStmt::FCMP_UEQ:
1190 resVal = (lhs == rhs);
1191 // resVal = (lhs.getInterval() == rhs.getInterval());
1192 break;
1193 case CmpStmt::ICMP_NE:
1194 case CmpStmt::FCMP_ONE:
1195 case CmpStmt::FCMP_UNE:
1196 resVal = (lhs != rhs);
1197 break;
1198 case CmpStmt::ICMP_UGT:
1199 case CmpStmt::ICMP_SGT:
1200 case CmpStmt::FCMP_OGT:
1201 case CmpStmt::FCMP_UGT:
1202 resVal = (lhs > rhs);
1203 break;
1204 case CmpStmt::ICMP_UGE:
1205 case CmpStmt::ICMP_SGE:
1206 case CmpStmt::FCMP_OGE:
1207 case CmpStmt::FCMP_UGE:
1208 resVal = (lhs >= rhs);
1209 break;
1210 case CmpStmt::ICMP_ULT:
1211 case CmpStmt::ICMP_SLT:
1212 case CmpStmt::FCMP_OLT:
1213 case CmpStmt::FCMP_ULT:
1214 resVal = (lhs < rhs);
1215 break;
1216 case CmpStmt::ICMP_ULE:
1217 case CmpStmt::ICMP_SLE:
1218 case CmpStmt::FCMP_OLE:
1219 case CmpStmt::FCMP_ULE:
1220 resVal = (lhs <= rhs);
1221 break;
1223 resVal = IntervalValue(0, 0);
1224 break;
1225 case CmpStmt::FCMP_TRUE:
1226 resVal = IntervalValue(1, 1);
1227 break;
1228 case CmpStmt::FCMP_ORD:
1229 case CmpStmt::FCMP_UNO:
1230 // FCMP_ORD: true if both operands are not NaN
1231 // FCMP_UNO: true if either operand is NaN
1232 // Conservatively return [0, 1] since we don't track NaN
1233 resVal = IntervalValue(0, 1);
1234 break;
1235 default:
1236 assert(false && "undefined compare: ");
1237 }
1238 updateAbsValue(cmp->getRes(), resVal, node);
1239 }
1240 else if (op0Val.isAddr() && op1Val.isAddr())
1241 {
1242 const AddressValue& lhs = op0Val.getAddrs();
1243 const AddressValue& rhs = op1Val.getAddrs();
1244 auto predicate = cmp->getPredicate();
1245 switch (predicate)
1246 {
1247 case CmpStmt::ICMP_EQ:
1248 case CmpStmt::FCMP_OEQ:
1249 case CmpStmt::FCMP_UEQ:
1250 {
1251 if (lhs.hasIntersect(rhs))
1252 {
1253 resVal = IntervalValue(0, 1);
1254 }
1255 else if (lhs.empty() && rhs.empty())
1256 {
1257 resVal = IntervalValue(1, 1);
1258 }
1259 else
1260 {
1261 resVal = IntervalValue(0, 0);
1262 }
1263 break;
1264 }
1265 case CmpStmt::ICMP_NE:
1266 case CmpStmt::FCMP_ONE:
1267 case CmpStmt::FCMP_UNE:
1268 {
1269 if (lhs.hasIntersect(rhs))
1270 {
1271 resVal = IntervalValue(0, 1);
1272 }
1273 else if (lhs.empty() && rhs.empty())
1274 {
1275 resVal = IntervalValue(0, 0);
1276 }
1277 else
1278 {
1279 resVal = IntervalValue(1, 1);
1280 }
1281 break;
1282 }
1283 case CmpStmt::ICMP_UGT:
1284 case CmpStmt::ICMP_SGT:
1285 case CmpStmt::FCMP_OGT:
1286 case CmpStmt::FCMP_UGT:
1287 {
1288 if (lhs.size() == 1 && rhs.size() == 1)
1289 {
1290 resVal = IntervalValue(*lhs.begin() > *rhs.begin());
1291 }
1292 else
1293 {
1294 resVal = IntervalValue(0, 1);
1295 }
1296 break;
1297 }
1298 case CmpStmt::ICMP_UGE:
1299 case CmpStmt::ICMP_SGE:
1300 case CmpStmt::FCMP_OGE:
1301 case CmpStmt::FCMP_UGE:
1302 {
1303 if (lhs.size() == 1 && rhs.size() == 1)
1304 {
1305 resVal = IntervalValue(*lhs.begin() >= *rhs.begin());
1306 }
1307 else
1308 {
1309 resVal = IntervalValue(0, 1);
1310 }
1311 break;
1312 }
1313 case CmpStmt::ICMP_ULT:
1314 case CmpStmt::ICMP_SLT:
1315 case CmpStmt::FCMP_OLT:
1316 case CmpStmt::FCMP_ULT:
1317 {
1318 if (lhs.size() == 1 && rhs.size() == 1)
1319 {
1320 resVal = IntervalValue(*lhs.begin() < *rhs.begin());
1321 }
1322 else
1323 {
1324 resVal = IntervalValue(0, 1);
1325 }
1326 break;
1327 }
1328 case CmpStmt::ICMP_ULE:
1329 case CmpStmt::ICMP_SLE:
1330 case CmpStmt::FCMP_OLE:
1331 case CmpStmt::FCMP_ULE:
1332 {
1333 if (lhs.size() == 1 && rhs.size() == 1)
1334 {
1335 resVal = IntervalValue(*lhs.begin() <= *rhs.begin());
1336 }
1337 else
1338 {
1339 resVal = IntervalValue(0, 1);
1340 }
1341 break;
1342 }
1344 resVal = IntervalValue(0, 0);
1345 break;
1346 case CmpStmt::FCMP_TRUE:
1347 resVal = IntervalValue(1, 1);
1348 break;
1349 case CmpStmt::FCMP_ORD:
1350 case CmpStmt::FCMP_UNO:
1351 // FCMP_ORD: true if both operands are not NaN
1352 // FCMP_UNO: true if either operand is NaN
1353 // Conservatively return [0, 1] since we don't track NaN
1354 resVal = IntervalValue(0, 1);
1355 break;
1356 default:
1357 assert(false && "undefined compare: ");
1358 }
1359 updateAbsValue(cmp->getRes(), resVal, node);
1360 }
1361 }
1362 }
1363}
@ ICMP_SGT
signed greater than
@ FCMP_UEQ
1 0 0 1 True if unordered or equal
@ FCMP_ONE
0 1 1 0 True if ordered and operands are unequal
@ ICMP_UGE
unsigned greater or equal
@ FCMP_UGT
1 0 1 0 True if unordered or greater than
@ ICMP_ULE
unsigned less or equal
@ FCMP_OGE
0 0 1 1 True if ordered and greater than or equal
@ FCMP_OLT
0 1 0 0 True if ordered and less than
@ FCMP_OGT
0 0 1 0 True if ordered and greater than
@ ICMP_NE
not equal
@ FCMP_TRUE
1 1 1 1 Always true (always folded)
@ ICMP_ULT
unsigned less than
@ FCMP_ULE
1 1 0 1 True if unordered, less than, or equal
@ ICMP_SLT
signed less than
@ ICMP_UGT
unsigned greater than
@ FCMP_OEQ
0 0 0 1 True if ordered and equal
@ FCMP_ORD
0 1 1 1 True if ordered (no nans)
@ FCMP_OLE
0 1 0 1 True if ordered and less than or equal
@ FCMP_FALSE
0 0 0 0 Always false (always folded)
@ FCMP_ULT
1 1 0 0 True if unordered or less than
@ FCMP_UNO
1 0 0 0 True if unordered: isnan(X) | isnan(Y)
@ FCMP_UGE
1 0 1 1 True if unordered, greater than, or equal
@ ICMP_SGE
signed greater or equal
@ FCMP_UNE
1 1 1 0 True if unordered or not equal
@ ICMP_SLE
signed less or equal

◆ updateStateOnCopy()

void AbstractInterpretation::updateStateOnCopy ( const CopyStmt copy)
private

Definition at line 1380 of file AbstractInterpretation.cpp.

1381{
1382 const ICFGNode* node = copy->getICFGNode();
1383 const SVFVar* lhsVar = copy->getLHSVar();
1384 const SVFVar* rhsVar = copy->getRHSVar();
1385
1386 auto getZExtValue = [&](const SVFVar* var)
1387 {
1388 const SVFType* type = var->getType();
1389 if (SVFUtil::isa<SVFIntegerType>(type))
1390 {
1391 u32_t bits = type->getByteSize() * 8;
1392 const AbstractValue& val = getAbsValue(var, node);
1393 if (val.getInterval().is_numeral())
1394 {
1395 if (bits == 8)
1396 {
1397 int8_t signed_i8_value = val.getInterval().getIntNumeral();
1400 }
1401 else if (bits == 16)
1402 {
1403 s16_t signed_i16_value = val.getInterval().getIntNumeral();
1406 }
1407 else if (bits == 32)
1408 {
1409 s32_t signed_i32_value = val.getInterval().getIntNumeral();
1412 }
1413 else if (bits == 64)
1414 {
1415 s64_t signed_i64_value = val.getInterval().getIntNumeral();
1417 }
1418 else
1419 assert(false && "cannot support int type other than u8/16/32/64");
1420 }
1421 else
1422 {
1423 return IntervalValue::top();
1424 }
1425 }
1426 return IntervalValue::top();
1427 };
1428
1429 auto getTruncValue = [&](const SVFVar* var, const SVFType* dstType)
1430 {
1431 const IntervalValue& itv = getAbsValue(var, node).getInterval();
1432 if(itv.isBottom()) return itv;
1434 s64_t int_ub = itv.ub().getIntNumeral();
1435 u32_t dst_bits = dstType->getByteSize() * 8;
1436 if (dst_bits == 8)
1437 {
1438 int8_t s8_lb = static_cast<int8_t>(int_lb);
1439 int8_t s8_ub = static_cast<int8_t>(int_ub);
1440 if (s8_lb > s8_ub)
1442 return IntervalValue(s8_lb, s8_ub);
1443 }
1444 else if (dst_bits == 16)
1445 {
1446 s16_t s16_lb = static_cast<s16_t>(int_lb);
1447 s16_t s16_ub = static_cast<s16_t>(int_ub);
1448 if (s16_lb > s16_ub)
1450 return IntervalValue(s16_lb, s16_ub);
1451 }
1452 else if (dst_bits == 32)
1453 {
1454 s32_t s32_lb = static_cast<s32_t>(int_lb);
1455 s32_t s32_ub = static_cast<s32_t>(int_ub);
1456 if (s32_lb > s32_ub)
1458 return IntervalValue(s32_lb, s32_ub);
1459 }
1460 else
1461 {
1462 assert(false && "cannot support dst int type other than u8/16/32");
1463 abort();
1464 }
1465 };
1466
1467 const AbstractValue& rhsVal = getAbsValue(rhsVar, node);
1468
1469 if (copy->getCopyKind() == CopyStmt::COPYVAL)
1470 {
1472 }
1473 else if (copy->getCopyKind() == CopyStmt::ZEXT)
1474 {
1475 updateAbsValue(lhsVar, getZExtValue(rhsVar), node);
1476 }
1477 else if (copy->getCopyKind() == CopyStmt::SEXT)
1478 {
1479 updateAbsValue(lhsVar, rhsVal.getInterval(), node);
1480 }
1481 else if (copy->getCopyKind() == CopyStmt::FPTOSI)
1482 {
1483 updateAbsValue(lhsVar, rhsVal.getInterval(), node);
1484 }
1485 else if (copy->getCopyKind() == CopyStmt::FPTOUI)
1486 {
1487 updateAbsValue(lhsVar, rhsVal.getInterval(), node);
1488 }
1489 else if (copy->getCopyKind() == CopyStmt::SITOFP)
1490 {
1491 updateAbsValue(lhsVar, rhsVal.getInterval(), node);
1492 }
1493 else if (copy->getCopyKind() == CopyStmt::UITOFP)
1494 {
1495 updateAbsValue(lhsVar, rhsVal.getInterval(), node);
1496 }
1497 else if (copy->getCopyKind() == CopyStmt::TRUNC)
1498 {
1499 updateAbsValue(lhsVar, getTruncValue(rhsVar, lhsVar->getType()), node);
1500 }
1501 else if (copy->getCopyKind() == CopyStmt::FPTRUNC)
1502 {
1503 updateAbsValue(lhsVar, rhsVal.getInterval(), node);
1504 }
1505 else if (copy->getCopyKind() == CopyStmt::INTTOPTR)
1506 {
1507 //insert nullptr
1508 }
1509 else if (copy->getCopyKind() == CopyStmt::PTRTOINT)
1510 {
1512 }
1513 else if (copy->getCopyKind() == CopyStmt::BITCAST)
1514 {
1515 if (rhsVal.isAddr())
1517 }
1518 else
1519 assert(false && "undefined copy kind");
1520}
signed short s16_t
Definition GeneralType.h:54
unsigned short u16_t
Definition GeneralType.h:53

◆ updateStateOnGep()

void AbstractInterpretation::updateStateOnGep ( const GepStmt gep)
private

Definition at line 978 of file AbstractInterpretation.cpp.

979{
980 const ICFGNode* node = gep->getICFGNode();
982 AddressValue gepAddrs = getGepObjAddrs(SVFUtil::cast<ValVar>(gep->getRHSVar()), offsetPair);
983 updateAbsValue(gep->getLHSVar(), gepAddrs, node);
984}
AddressValue getGepObjAddrs(const ValVar *pointer, IntervalValue offset)
IntervalValue getGepElementIndex(const GepStmt *gep)

◆ updateStateOnLoad()

void AbstractInterpretation::updateStateOnLoad ( const LoadStmt load)
private

Definition at line 1365 of file AbstractInterpretation.cpp.

1366{
1367 const ICFGNode* node = load->getICFGNode();
1369 loadValue(SVFUtil::cast<ValVar>(load->getRHSVar()), node);
1370 updateAbsValue(load->getLHSVar(), loaded, node);
1371}
virtual AbstractValue loadValue(const ValVar *pointer, const ICFGNode *node)
Virtual so full-sparse can layer the GepObj overlay on top.
const ValVar * getLHSVar() const

◆ updateStateOnPhi()

void AbstractInterpretation::updateStateOnPhi ( const PhiStmt phi)
private

Definition at line 1005 of file AbstractInterpretation.cpp.

1006{
1007 const ICFGNode* icfgNode = phi->getICFGNode();
1009 for (u32_t i = 0; i < phi->getOpVarNum(); i++)
1010 {
1011 const ICFGNode* opICFGNode = phi->getOpICFGNode(i);
1013 {
1015 const AbstractValue& opVal = getAbsValue(phi->getOpVar(i), opICFGNode);
1017 if (edge)
1018 {
1019 const IntraCFGEdge* intraEdge = SVFUtil::cast<IntraCFGEdge>(edge);
1020 if (intraEdge->getCondition())
1021 {
1023 rhs.join_with(opVal);
1024 }
1025 else
1026 rhs.join_with(opVal);
1027 }
1028 else
1029 {
1030 rhs.join_with(opVal);
1031 }
1032 }
1033 }
1034 updateAbsValue(phi->getRes(), rhs, icfgNode);
1035}
ICFGEdge * getICFGEdge(const ICFGNode *src, const ICFGNode *dst, ICFGEdge::ICFGEdgeK kind)
Get a SVFG edge according to src and dst.
Definition ICFG.cpp:311

◆ updateStateOnRet()

void AbstractInterpretation::updateStateOnRet ( const RetPE retPE)
private

Definition at line 1057 of file AbstractInterpretation.cpp.

1058{
1059 const ICFGNode* node = retPE->getICFGNode();
1060 const AbstractValue& rhsVal = getAbsValue(retPE->getRHSVar(), node);
1061 updateAbsValue(retPE->getLHSVar(), rhsVal, node);
1062}
const ValVar * getRHSVar() const
const ValVar * getLHSVar() const

◆ updateStateOnSelect()

void AbstractInterpretation::updateStateOnSelect ( const SelectStmt select)
private

Definition at line 986 of file AbstractInterpretation.cpp.

987{
988 const ICFGNode* node = select->getICFGNode();
989 const AbstractValue& condVal = getAbsValue(select->getCondition(), node);
990 const AbstractValue& tVal = getAbsValue(select->getTrueValue(), node);
991 const AbstractValue& fVal = getAbsValue(select->getFalseValue(), node);
993 if (condVal.getInterval().is_numeral())
994 {
996 }
997 else
998 {
999 resVal = tVal;
1000 resVal.join_with(fVal);
1001 }
1002 updateAbsValue(select->getRes(), resVal, node);
1003}
bool is_zero() const
Return true if the IntervalValue is [0, 0].

◆ updateStateOnStore()

void AbstractInterpretation::updateStateOnStore ( const StoreStmt store)
private

Definition at line 1373 of file AbstractInterpretation.cpp.

1374{
1375 const ICFGNode* node = store->getICFGNode();
1376 AbstractValue val = getAbsValue(store->getRHSVar(), node);
1377 storeValue(SVFUtil::cast<ValVar>(store->getLHSVar()), val, node);
1378}
virtual void storeValue(const ValVar *pointer, const AbstractValue &val, const ICFGNode *node)
const ValVar * getRHSVar() const
const ValVar * getLHSVar() const

◆ widenCycleState()

bool AbstractInterpretation::widenCycleState ( const AbstractState prev,
const AbstractState cur,
const ICFGCycleWTO cycle 
)
protectedvirtual

Widen prev with cur; write the widened state to trace[cycle_head]. Returns true when next == prev (fixpoint). Semi-sparse subclass additionally scatters ValVars to their def-sites.

Reimplemented in SVF::SemiSparseAbstractInterpretation.

Definition at line 171 of file AELoopRecursion.cpp.

173{
176 // Always write back (even at fixpoint) so cycle_head's trace holds the
177 // widened state for the upcoming narrowing phase.
178 const ICFGNode* cycle_head = cycle->head()->getICFGNode();
180 return next == prev;
181}
AbstractState widening(const AbstractState &other)
domain widen with other, and return the widened domain

Friends And Related Symbol Documentation

◆ AEAPI

friend class AEAPI
friend

Definition at line 63 of file AbstractInterpretation.h.

◆ AEStat

Definition at line 62 of file AbstractInterpretation.h.

◆ BufOverflowDetector

Definition at line 64 of file AbstractInterpretation.h.

◆ NullptrDerefDetector

Definition at line 65 of file AbstractInterpretation.h.

Member Data Documentation

◆ abstractTrace

Map<const ICFGNode*, AbstractState> SVF::AbstractInterpretation::abstractTrace
protected

per-node trace; owned here

Definition at line 339 of file AbstractInterpretation.h.

◆ allAnalyzedNodes

Set<const ICFGNode*> SVF::AbstractInterpretation::allAnalyzedNodes
private

Definition at line 329 of file AbstractInterpretation.h.

◆ api

AEAPI* SVF::AbstractInterpretation::api {nullptr}
private

Execution State, used to store the Interval Value of every SVF variable.

Definition at line 304 of file AbstractInterpretation.h.

304{nullptr};

◆ callGraph

CallGraph* SVF::AbstractInterpretation::callGraph
private

Definition at line 307 of file AbstractInterpretation.h.

◆ detectors

std::vector<std::unique_ptr<AEDetector> > SVF::AbstractInterpretation::detectors
private

Definition at line 332 of file AbstractInterpretation.h.

◆ func_map

Map<std::string, std::function<void(const CallICFGNode*)> > SVF::AbstractInterpretation::func_map
private

Definition at line 327 of file AbstractInterpretation.h.

◆ icfg

ICFG* SVF::AbstractInterpretation::icfg
private

Definition at line 306 of file AbstractInterpretation.h.

◆ moduleName

std::string SVF::AbstractInterpretation::moduleName
private

Definition at line 330 of file AbstractInterpretation.h.

◆ preAnalysis

AEWTO* SVF::AbstractInterpretation::preAnalysis {nullptr}
protected

Definition at line 338 of file AbstractInterpretation.h.

338{nullptr};

◆ stat

AEStat* SVF::AbstractInterpretation::stat
private

Definition at line 308 of file AbstractInterpretation.h.

◆ svfir

SVFIR* SVF::AbstractInterpretation::svfir {nullptr}
protected

Data and helpers reachable from SparseAbstractInterpretation.

Definition at line 337 of file AbstractInterpretation.h.

337{nullptr};

◆ utils

AbsExtAPI* SVF::AbstractInterpretation::utils
private

Definition at line 333 of file AbstractInterpretation.h.


The documentation for this class was generated from the following files: