svf-tools 1.0.1189 → 1.0.1191
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svf-tools",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1191",
|
|
4
4
|
"description": "* <b>[TypeClone](https://github.com/SVF-tools/SVF/wiki/TypeClone) published in our [ECOOP paper](https://yuleisui.github.io/publications/ecoop20.pdf) is now available in SVF </b> * <b>SVF now uses a single script for its build. Just type [`source ./build.sh`](https://github.com/SVF-tools/SVF/blob/master/build.sh) in your terminal, that's it!</b> * <b>SVF now supports LLVM-10.0.0! </b> * <b>We thank [bsauce](https://github.com/bsauce) for writing a user manual of SVF ([link1](https://www.jianshu.com/p/068a08ec749c) and [link2](https://www.jianshu.com/p/777c30d4240e)) in Chinese </b> * <b>SVF now supports LLVM-9.0.0 (Thank [Byoungyoung Lee](https://github.com/SVF-tools/SVF/issues/142) for his help!). </b> * <b>SVF now supports a set of [field-sensitive pointer analyses](https://yuleisui.github.io/publications/sas2019a.pdf). </b> * <b>[Use SVF as an external lib](https://github.com/SVF-tools/SVF/wiki/Using-SVF-as-a-lib-in-your-own-tool) for your own project (Contributed by [Hongxu Chen](https://github.com/HongxuChen)). </b> * <b>SVF now supports LLVM-7.0.0. </b> * <b>SVF now supports Docker. [Try SVF in Docker](https://github.com/SVF-tools/SVF/wiki/Try-SVF-in-Docker)! </b> * <b>SVF now supports [LLVM-6.0.0](https://github.com/svf-tools/SVF/pull/38) (Contributed by [Jack Anthony](https://github.com/jackanth)). </b> * <b>SVF now supports [LLVM-4.0.0](https://github.com/svf-tools/SVF/pull/23) (Contributed by Jared Carlson. Thank [Jared](https://github.com/jcarlson23) and [Will](https://github.com/dtzWill) for their in-depth [discussions](https://github.com/svf-tools/SVF/pull/18) about updating SVF!) </b> * <b>SVF now supports analysis for C++ programs.</b> <br />",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -218,26 +218,55 @@ private:
|
|
|
218
218
|
*
|
|
219
219
|
* @param cycle WTOCycle which has weak topo order of basic blocks and nested cycles
|
|
220
220
|
*/
|
|
221
|
-
virtual void
|
|
221
|
+
virtual void handleLoopOrRecursion(const ICFGCycleWTO* cycle);
|
|
222
222
|
|
|
223
|
-
|
|
223
|
+
/**
|
|
224
|
+
* Handle a function using worklist algorithm
|
|
225
|
+
*
|
|
226
|
+
* @param funEntry The entry node of the function to handle
|
|
227
|
+
*/
|
|
228
|
+
void handleFunction(const ICFGNode* funEntry);
|
|
224
229
|
|
|
225
|
-
|
|
230
|
+
/**
|
|
231
|
+
* Handle an ICFG node by merging states and processing statements
|
|
232
|
+
*
|
|
233
|
+
* @param node The ICFG node to handle
|
|
234
|
+
* @return true if state changed, false if fixpoint reached or infeasible
|
|
235
|
+
*/
|
|
236
|
+
bool handleICFGNode(const ICFGNode* node);
|
|
226
237
|
|
|
238
|
+
/**
|
|
239
|
+
* Get the next nodes of a node within the same function
|
|
240
|
+
*
|
|
241
|
+
* @param node The node to get successors for
|
|
242
|
+
* @return Vector of successor nodes
|
|
243
|
+
*/
|
|
244
|
+
std::vector<const ICFGNode*> getNextNodes(const ICFGNode* node) const;
|
|
227
245
|
|
|
228
246
|
/**
|
|
229
|
-
*
|
|
247
|
+
* Get the next nodes outside a cycle
|
|
230
248
|
*
|
|
231
|
-
* @param
|
|
249
|
+
* @param cycle The cycle to get exit successors for
|
|
250
|
+
* @return Vector of successor nodes outside the cycle
|
|
232
251
|
*/
|
|
233
|
-
|
|
252
|
+
std::vector<const ICFGNode*> getNextNodesOfCycle(const ICFGCycleWTO* cycle) const;
|
|
234
253
|
|
|
235
254
|
/**
|
|
236
|
-
*
|
|
255
|
+
* Recursively collect cycle heads from nested WTO components
|
|
237
256
|
*
|
|
238
|
-
* @param
|
|
257
|
+
* @param comps The list of WTO components to collect cycle heads from
|
|
239
258
|
*/
|
|
240
|
-
|
|
259
|
+
void collectCycleHeads(const std::list<const ICFGWTOComp*>& comps);
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* handle SVF Statement like CmpStmt, CallStmt, GepStmt, LoadStmt, StoreStmt, etc.
|
|
264
|
+
*
|
|
265
|
+
* @param stmt SVFStatement which is a value flow of instruction
|
|
266
|
+
*/
|
|
267
|
+
virtual void handleSVFStatement(const SVFStmt* stmt);
|
|
268
|
+
|
|
269
|
+
virtual void setTopToObjInRecursion(const CallICFGNode* callnode);
|
|
241
270
|
|
|
242
271
|
|
|
243
272
|
/**
|
|
@@ -299,6 +328,7 @@ private:
|
|
|
299
328
|
Map<const FunObjVar*, const ICFGWTO*> funcToWTO;
|
|
300
329
|
Set<std::pair<const CallICFGNode*, NodeID>> nonRecursiveCallSites;
|
|
301
330
|
Set<const FunObjVar*> recursiveFuns;
|
|
331
|
+
Map<const ICFGNode*, const ICFGCycleWTO*> cycleHeadToCycle;
|
|
302
332
|
|
|
303
333
|
|
|
304
334
|
bool hasAbsStateFromTrace(const ICFGNode* node)
|
|
@@ -313,15 +343,16 @@ private:
|
|
|
313
343
|
|
|
314
344
|
// helper functions in handleCallSite
|
|
315
345
|
virtual bool isExtCall(const CallICFGNode* callNode);
|
|
316
|
-
virtual void
|
|
346
|
+
virtual void handleExtCall(const CallICFGNode* callNode);
|
|
317
347
|
virtual bool isRecursiveFun(const FunObjVar* fun);
|
|
318
348
|
virtual bool isRecursiveCall(const CallICFGNode* callNode);
|
|
319
349
|
virtual void recursiveCallPass(const CallICFGNode *callNode);
|
|
320
350
|
virtual bool isRecursiveCallSite(const CallICFGNode* callNode, const FunObjVar *);
|
|
321
|
-
virtual
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
351
|
+
virtual void handleFunCall(const CallICFGNode* callNode);
|
|
352
|
+
|
|
353
|
+
bool skipRecursiveCall(const CallICFGNode* callNode);
|
|
354
|
+
const FunObjVar* getCallee(const CallICFGNode* callNode);
|
|
355
|
+
bool shouldApplyNarrowing(const FunObjVar* fun);
|
|
325
356
|
|
|
326
357
|
// there data should be shared with subclasses
|
|
327
358
|
Map<std::string, std::function<void(const CallICFGNode*)>> func_map;
|
|
@@ -73,14 +73,25 @@ AbstractInterpretation::~AbstractInterpretation()
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
|
-
* @brief
|
|
76
|
+
* @brief Recursively collect cycle heads from nested WTO components
|
|
77
77
|
*
|
|
78
|
-
* This function
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* Each SCC forms a function partition, and any function that is invoked from outside its SCC
|
|
82
|
-
* is identified as an entry of the function partition.
|
|
78
|
+
* This helper function traverses the WTO component tree and builds the cycleHeadToCycle
|
|
79
|
+
* map, which maps each cycle head node to its corresponding ICFGCycleWTO object.
|
|
80
|
+
* This enables efficient O(1) lookup of cycles during analysis.
|
|
83
81
|
*/
|
|
82
|
+
void AbstractInterpretation::collectCycleHeads(const std::list<const ICFGWTOComp*>& comps)
|
|
83
|
+
{
|
|
84
|
+
for (const ICFGWTOComp* comp : comps)
|
|
85
|
+
{
|
|
86
|
+
if (const ICFGCycleWTO* cycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
|
|
87
|
+
{
|
|
88
|
+
cycleHeadToCycle[cycle->head()->getICFGNode()] = cycle;
|
|
89
|
+
// Recursively collect nested cycle heads
|
|
90
|
+
collectCycleHeads(cycle->getWTOComponents());
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
84
95
|
void AbstractInterpretation::initWTO()
|
|
85
96
|
{
|
|
86
97
|
AndersenWaveDiff* ander = AndersenWaveDiff::createAndersenWaveDiff(svfir);
|
|
@@ -142,6 +153,13 @@ void AbstractInterpretation::initWTO()
|
|
|
142
153
|
funcToWTO[it->second->getFunction()] = iwto;
|
|
143
154
|
}
|
|
144
155
|
}
|
|
156
|
+
|
|
157
|
+
// Build cycleHeadToCycle map for all functions
|
|
158
|
+
// This maps cycle head nodes to their corresponding WTO cycles for efficient lookup
|
|
159
|
+
for (auto& [func, wto] : funcToWTO)
|
|
160
|
+
{
|
|
161
|
+
collectCycleHeads(wto->getWTOComponents());
|
|
162
|
+
}
|
|
145
163
|
}
|
|
146
164
|
|
|
147
165
|
/// Program entry
|
|
@@ -154,8 +172,9 @@ void AbstractInterpretation::analyse()
|
|
|
154
172
|
icfg->getGlobalICFGNode())[PAG::getPAG()->getBlkPtr()] = IntervalValue::top();
|
|
155
173
|
if (const CallGraphNode* cgn = svfir->getCallGraph()->getCallGraphNode("main"))
|
|
156
174
|
{
|
|
157
|
-
|
|
158
|
-
|
|
175
|
+
// Use worklist-based function handling instead of recursive WTO component handling
|
|
176
|
+
const ICFGNode* mainEntry = icfg->getFunEntryICFGNode(cgn->getFunction());
|
|
177
|
+
handleFunction(mainEntry);
|
|
159
178
|
}
|
|
160
179
|
}
|
|
161
180
|
|
|
@@ -215,21 +234,12 @@ bool AbstractInterpretation::mergeStatesFromPredecessors(const ICFGNode * icfgNo
|
|
|
215
234
|
else if (const RetCFGEdge *retCfgEdge =
|
|
216
235
|
SVFUtil::dyn_cast<RetCFGEdge>(edge))
|
|
217
236
|
{
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
237
|
+
// Only include return edge if the corresponding callsite was processed
|
|
238
|
+
// (skipped recursive callsites in WIDEN_ONLY/WIDEN_NARROW won't have state)
|
|
239
|
+
const RetICFGNode* retNode = SVFUtil::dyn_cast<RetICFGNode>(icfgNode);
|
|
240
|
+
if (hasAbsStateFromTrace(retNode->getCallICFGNode()))
|
|
221
241
|
{
|
|
222
242
|
workList.push_back(abstractTrace[retCfgEdge->getSrcNode()]);
|
|
223
|
-
break;
|
|
224
|
-
}
|
|
225
|
-
case WIDEN_ONLY:
|
|
226
|
-
case WIDEN_NARROW:
|
|
227
|
-
{
|
|
228
|
-
const RetICFGNode* returnSite = SVFUtil::dyn_cast<RetICFGNode>(icfgNode);
|
|
229
|
-
const CallICFGNode* callSite = returnSite->getCallICFGNode();
|
|
230
|
-
if (hasAbsStateFromTrace(callSite))
|
|
231
|
-
workList.push_back(abstractTrace[retCfgEdge->getSrcNode()]);
|
|
232
|
-
}
|
|
233
243
|
}
|
|
234
244
|
}
|
|
235
245
|
else
|
|
@@ -577,56 +587,228 @@ void AbstractInterpretation::handleSingletonWTO(const ICFGSingletonWTO *icfgSing
|
|
|
577
587
|
}
|
|
578
588
|
|
|
579
589
|
/**
|
|
580
|
-
*
|
|
590
|
+
* Handle an ICFG node by merging states from predecessors and processing statements
|
|
591
|
+
* Returns true if the abstract state has changed, false if fixpoint reached or infeasible
|
|
581
592
|
*/
|
|
582
|
-
|
|
593
|
+
bool AbstractInterpretation::handleICFGNode(const ICFGNode* node)
|
|
583
594
|
{
|
|
584
|
-
|
|
595
|
+
// Store the previous state for fixpoint detection
|
|
596
|
+
AbstractState prevState;
|
|
597
|
+
bool hadPrevState = hasAbsStateFromTrace(node);
|
|
598
|
+
if (hadPrevState)
|
|
599
|
+
prevState = abstractTrace[node];
|
|
600
|
+
|
|
601
|
+
// For function entry nodes, initialize state from caller or global
|
|
602
|
+
bool isFunEntry = SVFUtil::isa<FunEntryICFGNode>(node);
|
|
603
|
+
if (isFunEntry)
|
|
604
|
+
{
|
|
605
|
+
// Try to merge from predecessors first (handles call edges)
|
|
606
|
+
if (!mergeStatesFromPredecessors(node))
|
|
607
|
+
{
|
|
608
|
+
// No predecessors with state - initialize from caller or global
|
|
609
|
+
if (!callSiteStack.empty())
|
|
610
|
+
{
|
|
611
|
+
// Get state from the most recent call site
|
|
612
|
+
const CallICFGNode* caller = callSiteStack.back();
|
|
613
|
+
if (hasAbsStateFromTrace(caller))
|
|
614
|
+
{
|
|
615
|
+
abstractTrace[node] = abstractTrace[caller];
|
|
616
|
+
}
|
|
617
|
+
else
|
|
618
|
+
{
|
|
619
|
+
abstractTrace[node] = AbstractState();
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
else
|
|
623
|
+
{
|
|
624
|
+
// This is the main function entry, inherit from global node
|
|
625
|
+
const ICFGNode* globalNode = icfg->getGlobalICFGNode();
|
|
626
|
+
if (hasAbsStateFromTrace(globalNode))
|
|
627
|
+
{
|
|
628
|
+
abstractTrace[node] = abstractTrace[globalNode];
|
|
629
|
+
}
|
|
630
|
+
else
|
|
631
|
+
{
|
|
632
|
+
abstractTrace[node] = AbstractState();
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
else
|
|
585
638
|
{
|
|
586
|
-
|
|
639
|
+
// Merge states from predecessors
|
|
640
|
+
if (!mergeStatesFromPredecessors(node))
|
|
641
|
+
return false;
|
|
587
642
|
}
|
|
643
|
+
|
|
644
|
+
stat->getBlockTrace()++;
|
|
645
|
+
stat->getICFGNodeTrace()++;
|
|
646
|
+
|
|
647
|
+
// Handle SVF statements
|
|
648
|
+
for (const SVFStmt *stmt: node->getSVFStmts())
|
|
649
|
+
{
|
|
650
|
+
handleSVFStatement(stmt);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// Handle call sites
|
|
654
|
+
if (const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(node))
|
|
655
|
+
{
|
|
656
|
+
handleCallSite(callNode);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Run detectors
|
|
660
|
+
for (auto& detector: detectors)
|
|
661
|
+
detector->detect(getAbsStateFromTrace(node), node);
|
|
662
|
+
stat->countStateSize();
|
|
663
|
+
|
|
664
|
+
// Check if state changed (for fixpoint detection)
|
|
665
|
+
// For entry nodes on first visit, always return true to process successors
|
|
666
|
+
if (isFunEntry && !hadPrevState)
|
|
667
|
+
return true;
|
|
668
|
+
|
|
669
|
+
if (abstractTrace[node] == prevState)
|
|
670
|
+
return false;
|
|
671
|
+
|
|
672
|
+
return true;
|
|
588
673
|
}
|
|
589
674
|
|
|
590
|
-
|
|
675
|
+
/**
|
|
676
|
+
* Get the next nodes of a node within the same function
|
|
677
|
+
* For CallICFGNode, includes shortcut to RetICFGNode
|
|
678
|
+
*/
|
|
679
|
+
std::vector<const ICFGNode*> AbstractInterpretation::getNextNodes(const ICFGNode* node) const
|
|
591
680
|
{
|
|
592
|
-
|
|
681
|
+
std::vector<const ICFGNode*> outEdges;
|
|
682
|
+
for (const ICFGEdge* edge : node->getOutEdges())
|
|
593
683
|
{
|
|
594
|
-
|
|
595
|
-
|
|
684
|
+
const ICFGNode* dst = edge->getDstNode();
|
|
685
|
+
// Only nodes inside the same function are included
|
|
686
|
+
if (dst->getFun() == node->getFun())
|
|
687
|
+
{
|
|
688
|
+
outEdges.push_back(dst);
|
|
689
|
+
}
|
|
596
690
|
}
|
|
597
|
-
|
|
598
|
-
else if (const ICFGCycleWTO* cycle = SVFUtil::dyn_cast<ICFGCycleWTO>(wtoNode))
|
|
691
|
+
if (const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(node))
|
|
599
692
|
{
|
|
600
|
-
|
|
601
|
-
|
|
693
|
+
// Shortcut to the RetICFGNode
|
|
694
|
+
const ICFGNode* retNode = callNode->getRetICFGNode();
|
|
695
|
+
outEdges.push_back(retNode);
|
|
602
696
|
}
|
|
603
|
-
|
|
604
|
-
else
|
|
605
|
-
assert(false && "unknown WTO type!");
|
|
697
|
+
return outEdges;
|
|
606
698
|
}
|
|
607
699
|
|
|
608
|
-
|
|
700
|
+
/**
|
|
701
|
+
* Get the next nodes outside a cycle
|
|
702
|
+
* Inner cycles are skipped since their next nodes cannot be outside the outer cycle
|
|
703
|
+
*/
|
|
704
|
+
std::vector<const ICFGNode*> AbstractInterpretation::getNextNodesOfCycle(const ICFGCycleWTO* cycle) const
|
|
609
705
|
{
|
|
610
|
-
|
|
706
|
+
Set<const ICFGNode*> cycleNodes;
|
|
707
|
+
// Insert the head of the cycle and all component nodes
|
|
708
|
+
cycleNodes.insert(cycle->head()->getICFGNode());
|
|
709
|
+
for (const ICFGWTOComp* comp : cycle->getWTOComponents())
|
|
611
710
|
{
|
|
612
|
-
if (
|
|
711
|
+
if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
|
|
613
712
|
{
|
|
614
|
-
|
|
713
|
+
cycleNodes.insert(singleton->getICFGNode());
|
|
615
714
|
}
|
|
616
|
-
else if (
|
|
715
|
+
else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
|
|
617
716
|
{
|
|
618
|
-
|
|
717
|
+
cycleNodes.insert(subCycle->head()->getICFGNode());
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
std::vector<const ICFGNode*> outEdges;
|
|
722
|
+
|
|
723
|
+
// Check head's successors
|
|
724
|
+
std::vector<const ICFGNode*> nextNodes = getNextNodes(cycle->head()->getICFGNode());
|
|
725
|
+
for (const ICFGNode* nextNode : nextNodes)
|
|
726
|
+
{
|
|
727
|
+
// Only nodes that point outside the cycle are included
|
|
728
|
+
if (cycleNodes.find(nextNode) == cycleNodes.end())
|
|
729
|
+
{
|
|
730
|
+
outEdges.push_back(nextNode);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// Check each component's successors
|
|
735
|
+
for (const ICFGWTOComp* comp : cycle->getWTOComponents())
|
|
736
|
+
{
|
|
737
|
+
if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
|
|
738
|
+
{
|
|
739
|
+
std::vector<const ICFGNode*> compNextNodes = getNextNodes(singleton->getICFGNode());
|
|
740
|
+
for (const ICFGNode* nextNode : compNextNodes)
|
|
741
|
+
{
|
|
742
|
+
if (cycleNodes.find(nextNode) == cycleNodes.end())
|
|
743
|
+
{
|
|
744
|
+
outEdges.push_back(nextNode);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
619
747
|
}
|
|
620
|
-
|
|
748
|
+
// Skip inner cycles - they are handled within the outer cycle
|
|
749
|
+
}
|
|
750
|
+
return outEdges;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Handle a function using worklist algorithm
|
|
755
|
+
* This replaces the recursive WTO component handling with explicit worklist iteration
|
|
756
|
+
*/
|
|
757
|
+
void AbstractInterpretation::handleFunction(const ICFGNode* funEntry)
|
|
758
|
+
{
|
|
759
|
+
FIFOWorkList<const ICFGNode*> worklist;
|
|
760
|
+
worklist.push(funEntry);
|
|
761
|
+
|
|
762
|
+
while (!worklist.empty())
|
|
763
|
+
{
|
|
764
|
+
const ICFGNode* node = worklist.pop();
|
|
765
|
+
|
|
766
|
+
// Check if this node is a cycle head
|
|
767
|
+
if (cycleHeadToCycle.find(node) != cycleHeadToCycle.end())
|
|
768
|
+
{
|
|
769
|
+
const ICFGCycleWTO* cycle = cycleHeadToCycle[node];
|
|
770
|
+
handleLoopOrRecursion(cycle);
|
|
771
|
+
|
|
772
|
+
// Push nodes outside the cycle to the worklist
|
|
773
|
+
std::vector<const ICFGNode*> cycleNextNodes = getNextNodesOfCycle(cycle);
|
|
774
|
+
for (const ICFGNode* nextNode : cycleNextNodes)
|
|
775
|
+
{
|
|
776
|
+
worklist.push(nextNode);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
else
|
|
621
780
|
{
|
|
622
|
-
|
|
781
|
+
// Handle regular node
|
|
782
|
+
if (!handleICFGNode(node))
|
|
783
|
+
{
|
|
784
|
+
// Fixpoint reached or infeasible, skip successors
|
|
785
|
+
continue;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// Push successor nodes to the worklist
|
|
789
|
+
std::vector<const ICFGNode*> nextNodes = getNextNodes(node);
|
|
790
|
+
for (const ICFGNode* nextNode : nextNodes)
|
|
791
|
+
{
|
|
792
|
+
worklist.push(nextNode);
|
|
793
|
+
}
|
|
623
794
|
}
|
|
624
|
-
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
void AbstractInterpretation::handleCallSite(const ICFGNode* node)
|
|
800
|
+
{
|
|
801
|
+
if (const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(node))
|
|
802
|
+
{
|
|
803
|
+
if (isExtCall(callNode))
|
|
625
804
|
{
|
|
626
|
-
|
|
805
|
+
handleExtCall(callNode);
|
|
627
806
|
}
|
|
628
807
|
else
|
|
629
|
-
|
|
808
|
+
{
|
|
809
|
+
// Handle both direct and indirect calls uniformly
|
|
810
|
+
handleFunCall(callNode);
|
|
811
|
+
}
|
|
630
812
|
}
|
|
631
813
|
else
|
|
632
814
|
assert (false && "it is not call node");
|
|
@@ -637,7 +819,7 @@ bool AbstractInterpretation::isExtCall(const CallICFGNode *callNode)
|
|
|
637
819
|
return SVFUtil::isExtCall(callNode->getCalledFunction());
|
|
638
820
|
}
|
|
639
821
|
|
|
640
|
-
void AbstractInterpretation::
|
|
822
|
+
void AbstractInterpretation::handleExtCall(const CallICFGNode *callNode)
|
|
641
823
|
{
|
|
642
824
|
callSiteStack.push_back(callNode);
|
|
643
825
|
utils->handleExtAPI(callNode);
|
|
@@ -648,11 +830,13 @@ void AbstractInterpretation::extCallPass(const CallICFGNode *callNode)
|
|
|
648
830
|
callSiteStack.pop_back();
|
|
649
831
|
}
|
|
650
832
|
|
|
833
|
+
/// Check if a function is recursive (part of a call graph SCC)
|
|
651
834
|
bool AbstractInterpretation::isRecursiveFun(const FunObjVar* fun)
|
|
652
835
|
{
|
|
653
836
|
return recursiveFuns.find(fun) != recursiveFuns.end();
|
|
654
837
|
}
|
|
655
838
|
|
|
839
|
+
/// Check if a call node calls a recursive function
|
|
656
840
|
bool AbstractInterpretation::isRecursiveCall(const CallICFGNode *callNode)
|
|
657
841
|
{
|
|
658
842
|
const FunObjVar *callfun = callNode->getCalledFunction();
|
|
@@ -662,10 +846,11 @@ bool AbstractInterpretation::isRecursiveCall(const CallICFGNode *callNode)
|
|
|
662
846
|
return isRecursiveFun(callfun);
|
|
663
847
|
}
|
|
664
848
|
|
|
849
|
+
/// Handle recursive call in TOP mode: set all stores and return value to TOP
|
|
665
850
|
void AbstractInterpretation::recursiveCallPass(const CallICFGNode *callNode)
|
|
666
851
|
{
|
|
667
852
|
AbstractState& as = getAbsStateFromTrace(callNode);
|
|
668
|
-
|
|
853
|
+
setTopToObjInRecursion(callNode);
|
|
669
854
|
const RetICFGNode *retNode = callNode->getRetICFGNode();
|
|
670
855
|
if (retNode->getSVFStmts().size() > 0)
|
|
671
856
|
{
|
|
@@ -681,6 +866,7 @@ void AbstractInterpretation::recursiveCallPass(const CallICFGNode *callNode)
|
|
|
681
866
|
abstractTrace[retNode] = as;
|
|
682
867
|
}
|
|
683
868
|
|
|
869
|
+
/// Check if a call is a recursive callsite (within same SCC, not entry call from outside)
|
|
684
870
|
bool AbstractInterpretation::isRecursiveCallSite(const CallICFGNode* callNode,
|
|
685
871
|
const FunObjVar* callee)
|
|
686
872
|
{
|
|
@@ -688,174 +874,219 @@ bool AbstractInterpretation::isRecursiveCallSite(const CallICFGNode* callNode,
|
|
|
688
874
|
nonRecursiveCallSites.end();
|
|
689
875
|
}
|
|
690
876
|
|
|
691
|
-
|
|
877
|
+
/// Get callee function: directly for direct calls, via pointer analysis for indirect calls
|
|
878
|
+
const FunObjVar* AbstractInterpretation::getCallee(const CallICFGNode* callNode)
|
|
692
879
|
{
|
|
693
|
-
|
|
694
|
-
if (
|
|
695
|
-
return
|
|
696
|
-
|
|
697
|
-
|
|
880
|
+
// Direct call: get callee directly from call node
|
|
881
|
+
if (const FunObjVar* callee = callNode->getCalledFunction())
|
|
882
|
+
return callee;
|
|
883
|
+
|
|
884
|
+
// Indirect call: resolve callee through pointer analysis
|
|
885
|
+
const auto callsiteMaps = svfir->getIndirectCallsites();
|
|
886
|
+
auto it = callsiteMaps.find(callNode);
|
|
887
|
+
if (it == callsiteMaps.end())
|
|
888
|
+
return nullptr;
|
|
889
|
+
|
|
890
|
+
NodeID call_id = it->second;
|
|
891
|
+
if (!hasAbsStateFromTrace(callNode))
|
|
892
|
+
return nullptr;
|
|
893
|
+
|
|
894
|
+
AbstractState& as = getAbsStateFromTrace(callNode);
|
|
895
|
+
if (!as.inVarToAddrsTable(call_id))
|
|
896
|
+
return nullptr;
|
|
897
|
+
|
|
898
|
+
AbstractValue Addrs = as[call_id];
|
|
899
|
+
if (Addrs.getAddrs().empty())
|
|
900
|
+
return nullptr;
|
|
901
|
+
|
|
902
|
+
NodeID addr = *Addrs.getAddrs().begin();
|
|
903
|
+
SVFVar* func_var = svfir->getGNode(as.getIDFromAddr(addr));
|
|
904
|
+
return SVFUtil::dyn_cast<FunObjVar>(func_var);
|
|
698
905
|
}
|
|
699
|
-
|
|
906
|
+
|
|
907
|
+
/// Skip recursive callsites (within SCC); entry calls from outside SCC are not skipped
|
|
908
|
+
bool AbstractInterpretation::skipRecursiveCall(const CallICFGNode* callNode)
|
|
700
909
|
{
|
|
701
|
-
|
|
910
|
+
const FunObjVar* callee = getCallee(callNode);
|
|
911
|
+
if (!callee)
|
|
912
|
+
return false;
|
|
702
913
|
|
|
703
|
-
|
|
914
|
+
// Non-recursive function: never skip, always inline
|
|
915
|
+
if (!isRecursiveFun(callee))
|
|
916
|
+
return false;
|
|
704
917
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
918
|
+
// For recursive functions, skip only recursive callsites (within same SCC).
|
|
919
|
+
// Entry calls (from outside SCC) are not skipped - they are inlined so that
|
|
920
|
+
// handleLoopOrRecursion() can analyze the function body.
|
|
921
|
+
// This applies uniformly to all modes (TOP/WIDEN_ONLY/WIDEN_NARROW).
|
|
922
|
+
return isRecursiveCallSite(callNode, callee);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
/// Check if narrowing should be applied: always for regular loops, mode-dependent for recursion
|
|
926
|
+
bool AbstractInterpretation::shouldApplyNarrowing(const FunObjVar* fun)
|
|
927
|
+
{
|
|
928
|
+
// Non-recursive functions (regular loops): always apply narrowing
|
|
929
|
+
if (!isRecursiveFun(fun))
|
|
930
|
+
return true;
|
|
931
|
+
|
|
932
|
+
// Recursive functions: WIDEN_NARROW applies narrowing, WIDEN_ONLY does not
|
|
933
|
+
// TOP mode exits early in handleLoopOrRecursion, so should not reach here
|
|
934
|
+
switch (Options::HandleRecur())
|
|
716
935
|
{
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
936
|
+
case TOP:
|
|
937
|
+
assert(false && "TOP mode should not reach narrowing phase for recursive functions");
|
|
938
|
+
return false;
|
|
939
|
+
case WIDEN_ONLY:
|
|
940
|
+
return false; // Skip narrowing for recursive functions
|
|
941
|
+
case WIDEN_NARROW:
|
|
942
|
+
return true; // Apply narrowing for recursive functions
|
|
943
|
+
default:
|
|
944
|
+
assert(false && "Unknown recursion handling mode");
|
|
945
|
+
return false;
|
|
720
946
|
}
|
|
947
|
+
}
|
|
948
|
+
/// Handle direct or indirect call: get callee, process function body, set return state
|
|
949
|
+
void AbstractInterpretation::handleFunCall(const CallICFGNode *callNode)
|
|
950
|
+
{
|
|
951
|
+
AbstractState& as = getAbsStateFromTrace(callNode);
|
|
952
|
+
abstractTrace[callNode] = as;
|
|
953
|
+
|
|
954
|
+
// Skip recursive callsites (within SCC); entry calls are not skipped
|
|
955
|
+
if (skipRecursiveCall(callNode))
|
|
956
|
+
return;
|
|
957
|
+
|
|
958
|
+
const FunObjVar* callee = getCallee(callNode);
|
|
959
|
+
if (!callee)
|
|
960
|
+
return;
|
|
721
961
|
|
|
722
962
|
callSiteStack.push_back(callNode);
|
|
723
963
|
|
|
724
|
-
const
|
|
725
|
-
|
|
964
|
+
const ICFGNode* calleeEntry = icfg->getFunEntryICFGNode(callee);
|
|
965
|
+
handleFunction(calleeEntry);
|
|
726
966
|
|
|
727
967
|
callSiteStack.pop_back();
|
|
728
|
-
|
|
729
|
-
const RetICFGNode *retNode = callNode->getRetICFGNode();
|
|
730
|
-
// resume ES to callnode
|
|
968
|
+
const RetICFGNode* retNode = callNode->getRetICFGNode();
|
|
731
969
|
abstractTrace[retNode] = abstractTrace[callNode];
|
|
732
970
|
}
|
|
733
971
|
|
|
734
|
-
|
|
972
|
+
/// Handle WTO cycle (loop or recursive function) using widening/narrowing iteration.
|
|
973
|
+
///
|
|
974
|
+
/// Widening is applied at the cycle head to ensure termination of the analysis.
|
|
975
|
+
/// The cycle head's abstract state is iteratively updated until a fixpoint is reached.
|
|
976
|
+
///
|
|
977
|
+
/// == What is being widened ==
|
|
978
|
+
/// The abstract state at the cycle head node, which includes:
|
|
979
|
+
/// - Variable values (intervals) that may change across loop iterations
|
|
980
|
+
/// - For example, a loop counter `i` starting at 0 and incrementing each iteration
|
|
981
|
+
///
|
|
982
|
+
/// == Regular loops (non-recursive functions) ==
|
|
983
|
+
/// All modes (TOP/WIDEN_ONLY/WIDEN_NARROW) behave the same for regular loops:
|
|
984
|
+
/// 1. Widening phase: Iterate until the cycle head state stabilizes
|
|
985
|
+
/// Example: for(i=0; i<100; i++) -> i widens to [0, +inf]
|
|
986
|
+
/// 2. Narrowing phase: Refine the over-approximation from widening
|
|
987
|
+
/// Example: [0, +inf] narrows to [0, 100] using loop condition
|
|
988
|
+
///
|
|
989
|
+
/// == Recursive function cycles ==
|
|
990
|
+
/// Behavior depends on Options::HandleRecur():
|
|
991
|
+
///
|
|
992
|
+
/// - TOP mode:
|
|
993
|
+
/// Does not iterate. Calls recursiveCallPass() to set all stores and
|
|
994
|
+
/// return value to TOP immediately. This is the most conservative but fastest.
|
|
995
|
+
/// Example:
|
|
996
|
+
/// int factorial(int n) { return n <= 1 ? 1 : n * factorial(n-1); }
|
|
997
|
+
/// factorial(5) -> returns [-inf, +inf]
|
|
998
|
+
///
|
|
999
|
+
/// - WIDEN_ONLY mode:
|
|
1000
|
+
/// Widening phase only, no narrowing for recursive functions.
|
|
1001
|
+
/// The recursive function body is analyzed with widening until fixpoint.
|
|
1002
|
+
/// Example:
|
|
1003
|
+
/// int factorial(int n) { return n <= 1 ? 1 : n * factorial(n-1); }
|
|
1004
|
+
/// factorial(5) -> returns [10000, +inf] (widened upper bound)
|
|
1005
|
+
///
|
|
1006
|
+
/// - WIDEN_NARROW mode:
|
|
1007
|
+
/// Both widening and narrowing phases for recursive functions.
|
|
1008
|
+
/// After widening reaches fixpoint, narrowing refines the result.
|
|
1009
|
+
/// Example:
|
|
1010
|
+
/// int factorial(int n) { return n <= 1 ? 1 : n * factorial(n-1); }
|
|
1011
|
+
/// factorial(5) -> returns [10000, 10000] (precise after narrowing)
|
|
1012
|
+
void AbstractInterpretation::handleLoopOrRecursion(const ICFGCycleWTO* cycle)
|
|
735
1013
|
{
|
|
736
|
-
const
|
|
737
|
-
return callsiteMaps.find(callNode) != callsiteMaps.end();
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
void AbstractInterpretation::indirectCallFunPass(const CallICFGNode *callNode)
|
|
741
|
-
{
|
|
742
|
-
AbstractState& as = getAbsStateFromTrace(callNode);
|
|
743
|
-
const auto callsiteMaps = svfir->getIndirectCallsites();
|
|
744
|
-
NodeID call_id = callsiteMaps.at(callNode);
|
|
745
|
-
if (!as.inVarToAddrsTable(call_id))
|
|
746
|
-
{
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
749
|
-
AbstractValue Addrs = as[call_id];
|
|
750
|
-
NodeID addr = *Addrs.getAddrs().begin();
|
|
751
|
-
SVFVar *func_var = svfir->getGNode(as.getIDFromAddr(addr));
|
|
1014
|
+
const ICFGNode* cycle_head = cycle->head()->getICFGNode();
|
|
752
1015
|
|
|
753
|
-
|
|
1016
|
+
// TOP mode for recursive function cycles: use recursiveCallPass to set
|
|
1017
|
+
// all stores and return value to TOP, maintaining original semantics
|
|
1018
|
+
if (Options::HandleRecur() == TOP && isRecursiveFun(cycle_head->getFun()))
|
|
754
1019
|
{
|
|
755
|
-
|
|
1020
|
+
// Get the call node from callSiteStack (the call that entered this function)
|
|
1021
|
+
if (!callSiteStack.empty())
|
|
756
1022
|
{
|
|
757
|
-
|
|
758
|
-
|
|
1023
|
+
const CallICFGNode* callNode = callSiteStack.back();
|
|
1024
|
+
recursiveCallPass(callNode);
|
|
759
1025
|
}
|
|
760
|
-
|
|
761
|
-
const FunObjVar* callfun = funObjVar->getFunction();
|
|
762
|
-
callSiteStack.push_back(callNode);
|
|
763
|
-
abstractTrace[callNode] = as;
|
|
764
|
-
|
|
765
|
-
const ICFGWTO* wto = funcToWTO[callfun];
|
|
766
|
-
handleWTOComponents(wto->getWTOComponents());
|
|
767
|
-
callSiteStack.pop_back();
|
|
768
|
-
// handle Ret node
|
|
769
|
-
const RetICFGNode* retNode = callNode->getRetICFGNode();
|
|
770
|
-
abstractTrace[retNode] = abstractTrace[callNode];
|
|
1026
|
+
return;
|
|
771
1027
|
}
|
|
772
|
-
}
|
|
773
1028
|
|
|
774
|
-
|
|
775
|
-
void AbstractInterpretation::handleCycleWTO(const ICFGCycleWTO*cycle)
|
|
776
|
-
{
|
|
777
|
-
const ICFGNode* cycle_head = cycle->head()->getICFGNode();
|
|
778
|
-
// Flag to indicate if we are in the increasing phase
|
|
1029
|
+
// WIDEN_ONLY / WIDEN_NARROW modes (and regular loops): iterate until fixpoint
|
|
779
1030
|
bool increasing = true;
|
|
780
|
-
|
|
1031
|
+
u32_t widen_delay = Options::WidenDelay();
|
|
1032
|
+
|
|
781
1033
|
for (u32_t cur_iter = 0;; cur_iter++)
|
|
782
1034
|
{
|
|
783
|
-
//
|
|
784
|
-
|
|
1035
|
+
// Get the abstract state before processing the cycle head
|
|
1036
|
+
AbstractState prev_head_state;
|
|
1037
|
+
if (hasAbsStateFromTrace(cycle_head))
|
|
1038
|
+
prev_head_state = abstractTrace[cycle_head];
|
|
1039
|
+
|
|
1040
|
+
// Process the cycle head node
|
|
1041
|
+
handleICFGNode(cycle_head);
|
|
1042
|
+
AbstractState cur_head_state = abstractTrace[cycle_head];
|
|
1043
|
+
|
|
1044
|
+
// Start widening or narrowing if cur_iter >= widen delay threshold
|
|
1045
|
+
if (cur_iter >= widen_delay)
|
|
785
1046
|
{
|
|
786
|
-
// Widen or narrow after processing cycle head node
|
|
787
|
-
AbstractState prev_head_state = abstractTrace[cycle_head];
|
|
788
|
-
handleWTOComponent(cycle->head());
|
|
789
|
-
AbstractState cur_head_state = abstractTrace[cycle_head];
|
|
790
1047
|
if (increasing)
|
|
791
1048
|
{
|
|
792
|
-
|
|
793
|
-
if (isRecursiveFun(cycle->head()->getICFGNode()->getFun()) &&
|
|
794
|
-
!(Options::HandleRecur() == WIDEN_ONLY ||
|
|
795
|
-
Options::HandleRecur() == WIDEN_NARROW))
|
|
796
|
-
{
|
|
797
|
-
// When Options::HandleRecur() == TOP, skipRecursiveCall will handle recursions,
|
|
798
|
-
// thus should not reach this branch
|
|
799
|
-
assert(false && "Recursion mode TOP should not reach here!");
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
// Widening
|
|
1049
|
+
// Apply widening operator
|
|
803
1050
|
abstractTrace[cycle_head] = prev_head_state.widening(cur_head_state);
|
|
804
1051
|
|
|
805
1052
|
if (abstractTrace[cycle_head] == prev_head_state)
|
|
806
1053
|
{
|
|
1054
|
+
// Widening fixpoint reached, switch to narrowing phase
|
|
807
1055
|
increasing = false;
|
|
808
1056
|
continue;
|
|
809
1057
|
}
|
|
810
1058
|
}
|
|
811
1059
|
else
|
|
812
1060
|
{
|
|
813
|
-
// Narrowing
|
|
814
|
-
if (
|
|
1061
|
+
// Narrowing phase - check if narrowing should be applied
|
|
1062
|
+
if (!shouldApplyNarrowing(cycle_head->getFun()))
|
|
815
1063
|
{
|
|
816
|
-
|
|
817
|
-
if (Options::HandleRecur() == WIDEN_ONLY)
|
|
818
|
-
{
|
|
819
|
-
break;
|
|
820
|
-
}
|
|
821
|
-
// Perform normal narrowing in WIDEN_NARROW mode
|
|
822
|
-
else if (Options::HandleRecur() == WIDEN_NARROW)
|
|
823
|
-
{
|
|
824
|
-
// Widening's fixpoint reached in the widening phase, switch to narrowing
|
|
825
|
-
abstractTrace[cycle_head] = prev_head_state.narrowing(cur_head_state);
|
|
826
|
-
if (abstractTrace[cycle_head] == prev_head_state)
|
|
827
|
-
{
|
|
828
|
-
// Narrowing's fixpoint reached in the narrowing phase, exit loop
|
|
829
|
-
break;
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
// When Options::HandleRecur() == TOP, skipRecursiveCall will handle recursions,
|
|
833
|
-
// thus should not reach this branch
|
|
834
|
-
else
|
|
835
|
-
{
|
|
836
|
-
assert(false && "Recursion mode TOP should not reach here");
|
|
837
|
-
}
|
|
1064
|
+
break;
|
|
838
1065
|
}
|
|
839
|
-
|
|
840
|
-
|
|
1066
|
+
|
|
1067
|
+
// Apply narrowing
|
|
1068
|
+
abstractTrace[cycle_head] = prev_head_state.narrowing(cur_head_state);
|
|
1069
|
+
if (abstractTrace[cycle_head] == prev_head_state)
|
|
841
1070
|
{
|
|
842
|
-
//
|
|
843
|
-
|
|
844
|
-
if (abstractTrace[cycle_head] == prev_head_state)
|
|
845
|
-
{
|
|
846
|
-
// Narrowing's fixpoint reached in the narrowing phase, exit loop
|
|
847
|
-
break;
|
|
848
|
-
}
|
|
1071
|
+
// Narrowing fixpoint reached, exit loop
|
|
1072
|
+
break;
|
|
849
1073
|
}
|
|
850
1074
|
}
|
|
851
1075
|
}
|
|
852
|
-
|
|
1076
|
+
|
|
1077
|
+
// Process cycle body components
|
|
1078
|
+
for (const ICFGWTOComp* comp : cycle->getWTOComponents())
|
|
853
1079
|
{
|
|
854
|
-
|
|
855
|
-
|
|
1080
|
+
if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
|
|
1081
|
+
{
|
|
1082
|
+
handleICFGNode(singleton->getICFGNode());
|
|
1083
|
+
}
|
|
1084
|
+
else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
|
|
1085
|
+
{
|
|
1086
|
+
// Handle nested cycle recursively
|
|
1087
|
+
handleLoopOrRecursion(subCycle);
|
|
1088
|
+
}
|
|
856
1089
|
}
|
|
857
|
-
// Handle the cycle body
|
|
858
|
-
handleWTOComponents(cycle->getWTOComponents());
|
|
859
1090
|
}
|
|
860
1091
|
}
|
|
861
1092
|
|
|
@@ -920,7 +1151,8 @@ void AbstractInterpretation::handleSVFStatement(const SVFStmt *stmt)
|
|
|
920
1151
|
!getAbsStateFromTrace(stmt->getICFGNode())[IRGraph::NullPtr].isAddr());
|
|
921
1152
|
}
|
|
922
1153
|
|
|
923
|
-
|
|
1154
|
+
/// Set all store values in a recursive function to TOP (used in TOP mode)
|
|
1155
|
+
void AbstractInterpretation::setTopToObjInRecursion(const CallICFGNode *callNode)
|
|
924
1156
|
{
|
|
925
1157
|
AbstractState& as = getAbsStateFromTrace(callNode);
|
|
926
1158
|
const RetICFGNode *retNode = callNode->getRetICFGNode();
|