svf-tools 1.0.1209 → 1.0.1211
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.1211",
|
|
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": {
|
|
@@ -138,15 +138,13 @@ private:
|
|
|
138
138
|
/// Initialize abstract state for the global ICFG node and process global statements
|
|
139
139
|
virtual void handleGlobalNode();
|
|
140
140
|
|
|
141
|
-
///
|
|
142
|
-
|
|
141
|
+
/// Propagate the post-state of a node to all its intra-procedural successors
|
|
142
|
+
void propagateToSuccessor(const ICFGNode* node,
|
|
143
|
+
const Set<const ICFGNode*>* withinSet = nullptr);
|
|
143
144
|
|
|
144
145
|
/// Check if the branch on intraEdge is feasible under abstract state as
|
|
145
146
|
bool isBranchFeasible(const IntraCFGEdge* intraEdge, AbstractState& as);
|
|
146
147
|
|
|
147
|
-
/// Process all SVF statements in a singleton WTO component (single basic block)
|
|
148
|
-
virtual void handleSingletonWTO(const ICFGSingletonWTO *icfgSingletonWto);
|
|
149
|
-
|
|
150
148
|
/// Handle a call site node: dispatch to ext-call, direct-call, or indirect-call handling
|
|
151
149
|
virtual void handleCallSite(const ICFGNode* node);
|
|
152
150
|
|
|
@@ -156,15 +154,9 @@ private:
|
|
|
156
154
|
/// Handle a function body via worklist-driven WTO traversal starting from funEntry
|
|
157
155
|
void handleFunction(const ICFGNode* funEntry, const CallICFGNode* caller = nullptr);
|
|
158
156
|
|
|
159
|
-
///
|
|
157
|
+
/// Handle an ICFG node: execute statements; return true if state changed
|
|
160
158
|
bool handleICFGNode(const ICFGNode* node);
|
|
161
159
|
|
|
162
|
-
/// Get intra-procedural successor nodes (including call-to-ret shortcut) within the same function
|
|
163
|
-
std::vector<const ICFGNode*> getNextNodes(const ICFGNode* node) const;
|
|
164
|
-
|
|
165
|
-
/// Get successor nodes that exit the given WTO cycle (skipping inner sub-cycles)
|
|
166
|
-
std::vector<const ICFGNode*> getNextNodesOfCycle(const ICFGCycleWTO* cycle) const;
|
|
167
|
-
|
|
168
160
|
/// Dispatch an SVF statement (Addr/Binary/Cmp/Load/Store/Copy/Gep/Select/Phi/Call/Ret) to its handler
|
|
169
161
|
virtual void handleSVFStatement(const SVFStmt* stmt);
|
|
170
162
|
|
|
@@ -232,7 +224,7 @@ private:
|
|
|
232
224
|
// there data should be shared with subclasses
|
|
233
225
|
Map<std::string, std::function<void(const CallICFGNode*)>> func_map;
|
|
234
226
|
|
|
235
|
-
Map<const ICFGNode*, AbstractState> abstractTrace; // abstract states
|
|
227
|
+
Map<const ICFGNode*, AbstractState> abstractTrace; // abstract states for nodes (pre-state before execution, post-state after)
|
|
236
228
|
Set<const ICFGNode*> allAnalyzedNodes; // All nodes ever analyzed (across all entry points)
|
|
237
229
|
std::string moduleName;
|
|
238
230
|
|
|
@@ -255,76 +255,60 @@ void AbstractInterpretation::handleGlobalNode()
|
|
|
255
255
|
AddressValue(BlackHoleObjAddr);
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
///
|
|
259
|
-
///
|
|
260
|
-
///
|
|
261
|
-
|
|
258
|
+
/// Propagate the post-state of a node to all its intra-procedural successors.
|
|
259
|
+
/// For conditional branches, the state is refined along each outgoing edge via
|
|
260
|
+
/// isBranchFeasible. The refined state is joined into the successor's abstractTrace entry.
|
|
261
|
+
/// Call/return edges are handled explicitly by handleFunCall, not here.
|
|
262
|
+
///
|
|
263
|
+
/// If withinSet is non-null, only propagate to successors contained in that set.
|
|
264
|
+
/// This is used during cycle iteration to avoid propagating to nodes outside the
|
|
265
|
+
/// cycle, which would cause stale accumulation and lose narrowing precision.
|
|
266
|
+
/// Additionally, when withinSet is provided and a RetCFGEdge target is within the set,
|
|
267
|
+
/// the state is also propagated along that edge. This handles the FunExit -> RetNode
|
|
268
|
+
/// path inside recursive function cycles, where the recursive call is skipped and
|
|
269
|
+
/// the RetCFGEdge effectively acts as intra-procedural control flow.
|
|
270
|
+
void AbstractInterpretation::propagateToSuccessor(const ICFGNode* node,
|
|
271
|
+
const Set<const ICFGNode*>* withinSet)
|
|
262
272
|
{
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
273
|
+
if (!hasAbsStateFromTrace(node))
|
|
274
|
+
return;
|
|
275
|
+
|
|
276
|
+
for (auto& edge : node->getOutEdges())
|
|
266
277
|
{
|
|
267
|
-
if (
|
|
278
|
+
if (const IntraCFGEdge* intraEdge =
|
|
279
|
+
SVFUtil::dyn_cast<IntraCFGEdge>(edge))
|
|
268
280
|
{
|
|
281
|
+
const ICFGNode* dst = intraEdge->getDstNode();
|
|
282
|
+
// If a filter set is provided, skip successors not in the set
|
|
283
|
+
if (withinSet && withinSet->find(dst) == withinSet->end())
|
|
284
|
+
continue;
|
|
269
285
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
{
|
|
273
|
-
AbstractState tmpEs = abstractTrace[edge->getSrcNode()];
|
|
274
|
-
if (intraCfgEdge->getCondition())
|
|
275
|
-
{
|
|
276
|
-
if (isBranchFeasible(intraCfgEdge, tmpEs))
|
|
277
|
-
{
|
|
278
|
-
workList.push_back(tmpEs);
|
|
279
|
-
}
|
|
280
|
-
else
|
|
281
|
-
{
|
|
282
|
-
// do nothing
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
else
|
|
286
|
-
{
|
|
287
|
-
workList.push_back(tmpEs);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
else if (const CallCFGEdge *callCfgEdge =
|
|
291
|
-
SVFUtil::dyn_cast<CallCFGEdge>(edge))
|
|
292
|
-
{
|
|
293
|
-
|
|
294
|
-
// context insensitive implementation
|
|
295
|
-
workList.push_back(
|
|
296
|
-
abstractTrace[callCfgEdge->getSrcNode()]);
|
|
297
|
-
}
|
|
298
|
-
else if (const RetCFGEdge *retCfgEdge =
|
|
299
|
-
SVFUtil::dyn_cast<RetCFGEdge>(edge))
|
|
286
|
+
AbstractState state = abstractTrace[node];
|
|
287
|
+
if (intraEdge->getCondition())
|
|
300
288
|
{
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
if (hasAbsStateFromTrace(retNode->getCallICFGNode()))
|
|
305
|
-
{
|
|
306
|
-
workList.push_back(abstractTrace[retCfgEdge->getSrcNode()]);
|
|
307
|
-
}
|
|
289
|
+
if (!isBranchFeasible(intraEdge, state))
|
|
290
|
+
continue; // infeasible branch, do not propagate
|
|
291
|
+
// state has been refined in-place by isBranchFeasible
|
|
308
292
|
}
|
|
293
|
+
if (hasAbsStateFromTrace(dst))
|
|
294
|
+
abstractTrace[dst].joinWith(state);
|
|
309
295
|
else
|
|
310
|
-
|
|
296
|
+
abstractTrace[dst] = state;
|
|
311
297
|
}
|
|
312
|
-
|
|
313
|
-
if (workList.size() == 0)
|
|
314
|
-
{
|
|
315
|
-
return false;
|
|
316
|
-
}
|
|
317
|
-
else
|
|
318
|
-
{
|
|
319
|
-
while (!workList.empty())
|
|
298
|
+
else if (withinSet && SVFUtil::isa<RetCFGEdge>(edge))
|
|
320
299
|
{
|
|
321
|
-
|
|
322
|
-
|
|
300
|
+
// In recursive cycles, FunExit and RetNode are both inside the cycle.
|
|
301
|
+
// Propagate along RetCFGEdge when both endpoints are in the cycle.
|
|
302
|
+
const ICFGNode* dst = edge->getDstNode();
|
|
303
|
+
if (withinSet->find(dst) != withinSet->end())
|
|
304
|
+
{
|
|
305
|
+
if (hasAbsStateFromTrace(dst))
|
|
306
|
+
abstractTrace[dst].joinWith(abstractTrace[node]);
|
|
307
|
+
else
|
|
308
|
+
abstractTrace[dst] = abstractTrace[node];
|
|
309
|
+
}
|
|
323
310
|
}
|
|
324
|
-
//
|
|
325
|
-
// update post as
|
|
326
|
-
abstractTrace[icfgNode] = preAs;
|
|
327
|
-
return true;
|
|
311
|
+
// CallCFGEdge is handled by handleFunCall
|
|
328
312
|
}
|
|
329
313
|
}
|
|
330
314
|
|
|
@@ -626,68 +610,36 @@ bool AbstractInterpretation::isBranchFeasible(const IntraCFGEdge* intraEdge,
|
|
|
626
610
|
}
|
|
627
611
|
return true;
|
|
628
612
|
}
|
|
629
|
-
/// handle instructions in svf basic blocks
|
|
630
|
-
void AbstractInterpretation::handleSingletonWTO(const ICFGSingletonWTO *icfgSingletonWto)
|
|
631
|
-
{
|
|
632
|
-
const ICFGNode* node = icfgSingletonWto->getICFGNode();
|
|
633
|
-
stat->getBlockTrace()++;
|
|
634
|
-
|
|
635
|
-
std::deque<const ICFGNode*> worklist;
|
|
636
|
-
|
|
637
|
-
stat->getICFGNodeTrace()++;
|
|
638
|
-
// handle SVF Stmt
|
|
639
|
-
for (const SVFStmt *stmt: node->getSVFStmts())
|
|
640
|
-
{
|
|
641
|
-
handleSVFStatement(stmt);
|
|
642
|
-
}
|
|
643
|
-
// inlining the callee by calling handleFunc for the callee function
|
|
644
|
-
if (const CallICFGNode* callnode = SVFUtil::dyn_cast<CallICFGNode>(node))
|
|
645
|
-
{
|
|
646
|
-
handleCallSite(callnode);
|
|
647
|
-
}
|
|
648
|
-
for (auto& detector: detectors)
|
|
649
|
-
detector->detect(getAbstractState(node), node);
|
|
650
|
-
stat->countStateSize();
|
|
651
|
-
}
|
|
652
|
-
|
|
653
613
|
/**
|
|
654
|
-
* Handle an ICFG node
|
|
655
|
-
*
|
|
614
|
+
* Handle an ICFG node: execute statements and propagate post-state to successors.
|
|
615
|
+
* The node's pre-state must already be in abstractTrace (set by predecessors'
|
|
616
|
+
* propagateToSuccessor, or by handleGlobalNode/handleFunction for entry nodes).
|
|
617
|
+
* Returns true if the abstract state has changed, false if fixpoint reached or unreachable.
|
|
656
618
|
*/
|
|
657
619
|
bool AbstractInterpretation::handleICFGNode(const ICFGNode* node)
|
|
658
620
|
{
|
|
659
|
-
//
|
|
660
|
-
AbstractState prevState;
|
|
661
|
-
bool hadPrevState = hasAbsStateFromTrace(node);
|
|
662
|
-
if (hadPrevState)
|
|
663
|
-
prevState = abstractTrace[node];
|
|
664
|
-
|
|
665
|
-
// For function entry nodes, initialize state from predecessors or global
|
|
621
|
+
// Check reachability: pre-state must have been propagated by predecessors
|
|
666
622
|
bool isFunEntry = SVFUtil::isa<FunEntryICFGNode>(node);
|
|
667
|
-
if (
|
|
623
|
+
if (!hasAbsStateFromTrace(node))
|
|
668
624
|
{
|
|
669
|
-
|
|
670
|
-
if (!mergeStatesFromPredecessors(node))
|
|
625
|
+
if (isFunEntry)
|
|
671
626
|
{
|
|
672
|
-
//
|
|
627
|
+
// Entry point with no callers: inherit from global node
|
|
673
628
|
const ICFGNode* globalNode = icfg->getGlobalICFGNode();
|
|
674
629
|
if (hasAbsStateFromTrace(globalNode))
|
|
675
|
-
{
|
|
676
630
|
abstractTrace[node] = abstractTrace[globalNode];
|
|
677
|
-
}
|
|
678
631
|
else
|
|
679
|
-
{
|
|
680
632
|
abstractTrace[node] = AbstractState();
|
|
681
|
-
}
|
|
682
633
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
if (!mergeStatesFromPredecessors(node))
|
|
688
|
-
return false;
|
|
634
|
+
else
|
|
635
|
+
{
|
|
636
|
+
return false; // unreachable node
|
|
637
|
+
}
|
|
689
638
|
}
|
|
690
639
|
|
|
640
|
+
// Store the previous state for fixpoint detection
|
|
641
|
+
AbstractState prevState = abstractTrace[node];
|
|
642
|
+
|
|
691
643
|
stat->getBlockTrace()++;
|
|
692
644
|
stat->getICFGNodeTrace()++;
|
|
693
645
|
|
|
@@ -711,95 +663,12 @@ bool AbstractInterpretation::handleICFGNode(const ICFGNode* node)
|
|
|
711
663
|
// Track this node as analyzed (for coverage statistics across all entry points)
|
|
712
664
|
allAnalyzedNodes.insert(node);
|
|
713
665
|
|
|
714
|
-
// Check if state changed (for fixpoint detection)
|
|
715
|
-
// For entry nodes on first visit, always return true to process successors
|
|
716
|
-
if (isFunEntry && !hadPrevState)
|
|
717
|
-
return true;
|
|
718
|
-
|
|
719
666
|
if (abstractTrace[node] == prevState)
|
|
720
667
|
return false;
|
|
721
668
|
|
|
722
669
|
return true;
|
|
723
670
|
}
|
|
724
671
|
|
|
725
|
-
/**
|
|
726
|
-
* Get the next nodes of a node within the same function
|
|
727
|
-
* For CallICFGNode, includes shortcut to RetICFGNode
|
|
728
|
-
*/
|
|
729
|
-
std::vector<const ICFGNode*> AbstractInterpretation::getNextNodes(const ICFGNode* node) const
|
|
730
|
-
{
|
|
731
|
-
std::vector<const ICFGNode*> outEdges;
|
|
732
|
-
for (const ICFGEdge* edge : node->getOutEdges())
|
|
733
|
-
{
|
|
734
|
-
const ICFGNode* dst = edge->getDstNode();
|
|
735
|
-
// Only nodes inside the same function are included
|
|
736
|
-
if (dst->getFun() == node->getFun())
|
|
737
|
-
{
|
|
738
|
-
outEdges.push_back(dst);
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
if (const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(node))
|
|
742
|
-
{
|
|
743
|
-
// Shortcut to the RetICFGNode
|
|
744
|
-
const ICFGNode* retNode = callNode->getRetICFGNode();
|
|
745
|
-
outEdges.push_back(retNode);
|
|
746
|
-
}
|
|
747
|
-
return outEdges;
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
/**
|
|
751
|
-
* Get the next nodes outside a cycle
|
|
752
|
-
* Inner cycles are skipped since their next nodes cannot be outside the outer cycle
|
|
753
|
-
*/
|
|
754
|
-
std::vector<const ICFGNode*> AbstractInterpretation::getNextNodesOfCycle(const ICFGCycleWTO* cycle) const
|
|
755
|
-
{
|
|
756
|
-
Set<const ICFGNode*> cycleNodes;
|
|
757
|
-
// Insert the head of the cycle and all component nodes
|
|
758
|
-
cycleNodes.insert(cycle->head()->getICFGNode());
|
|
759
|
-
for (const ICFGWTOComp* comp : cycle->getWTOComponents())
|
|
760
|
-
{
|
|
761
|
-
if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
|
|
762
|
-
{
|
|
763
|
-
cycleNodes.insert(singleton->getICFGNode());
|
|
764
|
-
}
|
|
765
|
-
else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
|
|
766
|
-
{
|
|
767
|
-
cycleNodes.insert(subCycle->head()->getICFGNode());
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
std::vector<const ICFGNode*> outEdges;
|
|
772
|
-
|
|
773
|
-
// Check head's successors
|
|
774
|
-
std::vector<const ICFGNode*> nextNodes = getNextNodes(cycle->head()->getICFGNode());
|
|
775
|
-
for (const ICFGNode* nextNode : nextNodes)
|
|
776
|
-
{
|
|
777
|
-
// Only nodes that point outside the cycle are included
|
|
778
|
-
if (cycleNodes.find(nextNode) == cycleNodes.end())
|
|
779
|
-
{
|
|
780
|
-
outEdges.push_back(nextNode);
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
// Check each component's successors
|
|
785
|
-
for (const ICFGWTOComp* comp : cycle->getWTOComponents())
|
|
786
|
-
{
|
|
787
|
-
if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
|
|
788
|
-
{
|
|
789
|
-
std::vector<const ICFGNode*> compNextNodes = getNextNodes(singleton->getICFGNode());
|
|
790
|
-
for (const ICFGNode* nextNode : compNextNodes)
|
|
791
|
-
{
|
|
792
|
-
if (cycleNodes.find(nextNode) == cycleNodes.end())
|
|
793
|
-
{
|
|
794
|
-
outEdges.push_back(nextNode);
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
// Skip inner cycles - they are handled within the outer cycle
|
|
799
|
-
}
|
|
800
|
-
return outEdges;
|
|
801
|
-
}
|
|
802
|
-
|
|
803
672
|
/**
|
|
804
673
|
* Handle a function using worklist algorithm guided by WTO order.
|
|
805
674
|
* All top-level WTO components are pushed into the worklist upfront,
|
|
@@ -821,7 +690,9 @@ void AbstractInterpretation::handleFunction(const ICFGNode* funEntry, const Call
|
|
|
821
690
|
|
|
822
691
|
if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
|
|
823
692
|
{
|
|
824
|
-
|
|
693
|
+
const ICFGNode* node = singleton->getICFGNode();
|
|
694
|
+
handleICFGNode(node);
|
|
695
|
+
propagateToSuccessor(node);
|
|
825
696
|
}
|
|
826
697
|
else if (const ICFGCycleWTO* cycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
|
|
827
698
|
{
|
|
@@ -981,37 +852,78 @@ void AbstractInterpretation::handleFunCall(const CallICFGNode *callNode)
|
|
|
981
852
|
{
|
|
982
853
|
AbstractState& as = getAbstractState(callNode);
|
|
983
854
|
abstractTrace[callNode] = as;
|
|
984
|
-
|
|
985
|
-
//
|
|
855
|
+
// Skip recursive callsites (within SCC); entry calls are not skipped.
|
|
856
|
+
// For skipped recursive calls, propagate the caller's state to the callee
|
|
857
|
+
// entry as a back-edge contribution. This is needed because the IntraCFGEdge
|
|
858
|
+
// from CallNode to RetNode was removed by the ICFG builder (replaced by
|
|
859
|
+
// CallCFGEdge + RetCFGEdge), so propagateToSuccessor cannot propagate the
|
|
860
|
+
// back-edge. We manually push the CallPE parameters to the callee entry.
|
|
986
861
|
if (skipRecursiveCall(callNode))
|
|
862
|
+
{
|
|
863
|
+
const FunObjVar* callee = getCallee(callNode);
|
|
864
|
+
const ICFGNode* calleeEntry = icfg->getFunEntryICFGNode(callee);
|
|
865
|
+
// Push caller's state to callee entry (back-edge of recursive cycle)
|
|
866
|
+
if (hasAbsStateFromTrace(calleeEntry))
|
|
867
|
+
abstractTrace[calleeEntry].joinWith(abstractTrace[callNode]);
|
|
868
|
+
else
|
|
869
|
+
abstractTrace[calleeEntry] = abstractTrace[callNode];
|
|
987
870
|
return;
|
|
871
|
+
}
|
|
988
872
|
|
|
989
873
|
// Direct call: callee is known
|
|
990
874
|
if (const FunObjVar* callee = callNode->getCalledFunction())
|
|
991
875
|
{
|
|
992
876
|
const ICFGNode* calleeEntry = icfg->getFunEntryICFGNode(callee);
|
|
877
|
+
const ICFGNode* calleeExit = icfg->getFunExitICFGNode(callee);
|
|
878
|
+
// Push caller's state to callee entry so CallPE can copy parameters
|
|
879
|
+
abstractTrace[calleeEntry] = abstractTrace[callNode];
|
|
993
880
|
handleFunction(calleeEntry, callNode);
|
|
881
|
+
// Use callee exit state (which has memory side effects) for the return node
|
|
994
882
|
const RetICFGNode* retNode = callNode->getRetICFGNode();
|
|
995
|
-
|
|
883
|
+
if (hasAbsStateFromTrace(calleeExit))
|
|
884
|
+
abstractTrace[retNode] = abstractTrace[calleeExit];
|
|
885
|
+
else
|
|
886
|
+
abstractTrace[retNode] = abstractTrace[callNode];
|
|
996
887
|
return;
|
|
997
888
|
}
|
|
998
889
|
|
|
999
890
|
// Indirect call: use Andersen's call graph to get all resolved callees.
|
|
1000
891
|
// The call graph was built during PreAnalysis::initWTO() by running Andersen's pointer analysis,
|
|
1001
892
|
// which over-approximates the set of possible targets for each indirect callsite.
|
|
893
|
+
const RetICFGNode* retNode = callNode->getRetICFGNode();
|
|
1002
894
|
if (callGraph->hasIndCSCallees(callNode))
|
|
1003
895
|
{
|
|
1004
896
|
const auto& callees = callGraph->getIndCSCallees(callNode);
|
|
897
|
+
bool firstCallee = true;
|
|
1005
898
|
for (const FunObjVar* callee : callees)
|
|
1006
899
|
{
|
|
1007
900
|
if (callee->isDeclaration())
|
|
1008
901
|
continue;
|
|
1009
902
|
const ICFGNode* calleeEntry = icfg->getFunEntryICFGNode(callee);
|
|
903
|
+
const ICFGNode* calleeExit = icfg->getFunExitICFGNode(callee);
|
|
904
|
+
// Push caller's state to callee entry
|
|
905
|
+
abstractTrace[calleeEntry] = abstractTrace[callNode];
|
|
1010
906
|
handleFunction(calleeEntry, callNode);
|
|
907
|
+
// Use callee exit state for retNode (first callee assigns, rest join)
|
|
908
|
+
if (hasAbsStateFromTrace(calleeExit))
|
|
909
|
+
{
|
|
910
|
+
if (firstCallee)
|
|
911
|
+
{
|
|
912
|
+
abstractTrace[retNode] = abstractTrace[calleeExit];
|
|
913
|
+
firstCallee = false;
|
|
914
|
+
}
|
|
915
|
+
else
|
|
916
|
+
abstractTrace[retNode].joinWith(abstractTrace[calleeExit]);
|
|
917
|
+
}
|
|
1011
918
|
}
|
|
919
|
+
// If no callee was processed, fall back to caller's state
|
|
920
|
+
if (firstCallee)
|
|
921
|
+
abstractTrace[retNode] = abstractTrace[callNode];
|
|
922
|
+
}
|
|
923
|
+
else
|
|
924
|
+
{
|
|
925
|
+
abstractTrace[retNode] = abstractTrace[callNode];
|
|
1012
926
|
}
|
|
1013
|
-
const RetICFGNode* retNode = callNode->getRetICFGNode();
|
|
1014
|
-
abstractTrace[retNode] = abstractTrace[callNode];
|
|
1015
927
|
}
|
|
1016
928
|
|
|
1017
929
|
/// Handle WTO cycle (loop or recursive function) using widening/narrowing iteration.
|
|
@@ -1069,67 +981,140 @@ void AbstractInterpretation::handleLoopOrRecursion(const ICFGCycleWTO* cycle, co
|
|
|
1069
981
|
return;
|
|
1070
982
|
}
|
|
1071
983
|
|
|
1072
|
-
//
|
|
984
|
+
// Snapshot the external pre-state of the cycle head (contributions from
|
|
985
|
+
// outside the cycle, already propagated before we enter the cycle).
|
|
986
|
+
// This is fixed across all iterations; only back-edge contributions change.
|
|
987
|
+
AbstractState externalPre;
|
|
988
|
+
if (hasAbsStateFromTrace(cycle_head))
|
|
989
|
+
externalPre = abstractTrace[cycle_head];
|
|
990
|
+
|
|
991
|
+
// Collect all cycle body node pointers (excluding head) for clearing,
|
|
992
|
+
// and build cycleNodes set (head + body) for restricting propagation.
|
|
993
|
+
std::vector<const ICFGNode*> bodyNodes;
|
|
994
|
+
Set<const ICFGNode*> cycleNodes;
|
|
995
|
+
cycleNodes.insert(cycle_head);
|
|
996
|
+
for (const ICFGWTOComp* comp : cycle->getWTOComponents())
|
|
997
|
+
{
|
|
998
|
+
if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
|
|
999
|
+
{
|
|
1000
|
+
bodyNodes.push_back(singleton->getICFGNode());
|
|
1001
|
+
cycleNodes.insert(singleton->getICFGNode());
|
|
1002
|
+
}
|
|
1003
|
+
// Sub-cycle heads are handled recursively; their state is managed
|
|
1004
|
+
// by their own handleLoopOrRecursion call, but we still need to
|
|
1005
|
+
// clear them to avoid stale accumulation from previous outer iterations.
|
|
1006
|
+
else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
|
|
1007
|
+
{
|
|
1008
|
+
bodyNodes.push_back(subCycle->head()->getICFGNode());
|
|
1009
|
+
cycleNodes.insert(subCycle->head()->getICFGNode());
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// WIDEN_ONLY / WIDEN_NARROW modes (and regular loops): iterate until fixpoint.
|
|
1014
|
+
// Widening/narrowing is applied to the head's PRE-state (before execution).
|
|
1015
|
+
// Each iteration: widen/narrow pre-state -> execute head -> propagate ->
|
|
1016
|
+
// execute body -> collect back-edge -> rebuild pre-state for next iteration.
|
|
1073
1017
|
bool increasing = true;
|
|
1074
1018
|
u32_t widen_delay = Options::WidenDelay();
|
|
1019
|
+
AbstractState prev_head_pre;
|
|
1020
|
+
if (hasAbsStateFromTrace(cycle_head))
|
|
1021
|
+
prev_head_pre = abstractTrace[cycle_head];
|
|
1075
1022
|
|
|
1076
1023
|
for (u32_t cur_iter = 0;; cur_iter++)
|
|
1077
1024
|
{
|
|
1078
|
-
//
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
prev_head_state = abstractTrace[cycle_head];
|
|
1082
|
-
|
|
1083
|
-
// Process the cycle head node
|
|
1084
|
-
handleICFGNode(cycle_head);
|
|
1085
|
-
AbstractState cur_head_state = abstractTrace[cycle_head];
|
|
1025
|
+
// Clear cycle body states to prevent stale accumulation from previous iteration.
|
|
1026
|
+
for (const ICFGNode* bodyNode : bodyNodes)
|
|
1027
|
+
abstractTrace.erase(bodyNode);
|
|
1086
1028
|
|
|
1087
|
-
//
|
|
1029
|
+
// Apply widening or narrowing to the head's pre-state.
|
|
1030
|
+
// Compare current pre-state with the previous iteration's pre-state.
|
|
1088
1031
|
if (cur_iter >= widen_delay)
|
|
1089
1032
|
{
|
|
1033
|
+
AbstractState cur_head_pre = abstractTrace[cycle_head];
|
|
1090
1034
|
if (increasing)
|
|
1091
1035
|
{
|
|
1092
|
-
|
|
1093
|
-
abstractTrace[cycle_head]
|
|
1094
|
-
|
|
1095
|
-
if (abstractTrace[cycle_head] == prev_head_state)
|
|
1036
|
+
abstractTrace[cycle_head] = prev_head_pre.widening(cur_head_pre);
|
|
1037
|
+
if (abstractTrace[cycle_head] == prev_head_pre)
|
|
1096
1038
|
{
|
|
1097
|
-
// Widening fixpoint reached, switch to narrowing phase
|
|
1098
1039
|
increasing = false;
|
|
1099
|
-
|
|
1040
|
+
// Recompute with the stabilized state
|
|
1100
1041
|
}
|
|
1101
1042
|
}
|
|
1102
1043
|
else
|
|
1103
1044
|
{
|
|
1104
|
-
// Narrowing phase - check if narrowing should be applied
|
|
1105
1045
|
if (!shouldApplyNarrowing(cycle_head->getFun()))
|
|
1106
|
-
{
|
|
1107
1046
|
break;
|
|
1108
|
-
}
|
|
1109
1047
|
|
|
1110
|
-
|
|
1111
|
-
abstractTrace[cycle_head]
|
|
1112
|
-
if (abstractTrace[cycle_head] == prev_head_state)
|
|
1113
|
-
{
|
|
1114
|
-
// Narrowing fixpoint reached, exit loop
|
|
1048
|
+
abstractTrace[cycle_head] = prev_head_pre.narrowing(cur_head_pre);
|
|
1049
|
+
if (abstractTrace[cycle_head] == prev_head_pre)
|
|
1115
1050
|
break;
|
|
1116
|
-
}
|
|
1117
1051
|
}
|
|
1118
1052
|
}
|
|
1053
|
+
// Save this iteration's pre-state for next iteration's comparison
|
|
1054
|
+
prev_head_pre = abstractTrace[cycle_head];
|
|
1055
|
+
|
|
1056
|
+
// Process the cycle head node (execute stmts, transforms pre to post)
|
|
1057
|
+
handleICFGNode(cycle_head);
|
|
1119
1058
|
|
|
1120
|
-
//
|
|
1059
|
+
// Propagate head's post-state to body successor nodes within the cycle,
|
|
1060
|
+
// then erase head so the back-edge from body tail lands in a clean slot
|
|
1061
|
+
// (not mixed with head's post-state).
|
|
1062
|
+
propagateToSuccessor(cycle_head, &cycleNodes);
|
|
1063
|
+
abstractTrace.erase(cycle_head);
|
|
1064
|
+
|
|
1065
|
+
// Process cycle body components (propagation restricted to cycle nodes)
|
|
1121
1066
|
for (const ICFGWTOComp* comp : cycle->getWTOComponents())
|
|
1122
1067
|
{
|
|
1123
1068
|
if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
|
|
1124
1069
|
{
|
|
1125
|
-
|
|
1070
|
+
const ICFGNode* bodyNode = singleton->getICFGNode();
|
|
1071
|
+
handleICFGNode(bodyNode);
|
|
1072
|
+
propagateToSuccessor(bodyNode, &cycleNodes);
|
|
1126
1073
|
}
|
|
1127
1074
|
else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
|
|
1128
1075
|
{
|
|
1129
|
-
// Handle nested cycle recursively
|
|
1130
1076
|
handleLoopOrRecursion(subCycle, caller);
|
|
1131
1077
|
}
|
|
1132
1078
|
}
|
|
1079
|
+
|
|
1080
|
+
// After body processing, abstractTrace[cycle_head] holds only the
|
|
1081
|
+
// back-edge contribution (if any). Rebuild head's pre-state for the
|
|
1082
|
+
// next iteration: externalPre joined with back-edge.
|
|
1083
|
+
if (hasAbsStateFromTrace(cycle_head))
|
|
1084
|
+
{
|
|
1085
|
+
AbstractState backEdge = abstractTrace[cycle_head];
|
|
1086
|
+
abstractTrace[cycle_head] = externalPre;
|
|
1087
|
+
abstractTrace[cycle_head].joinWith(backEdge);
|
|
1088
|
+
}
|
|
1089
|
+
else
|
|
1090
|
+
{
|
|
1091
|
+
abstractTrace[cycle_head] = externalPre;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
// Final pass: re-execute all cycle nodes and propagate without restriction.
|
|
1096
|
+
// During iterations, propagation was restricted to cycle-internal nodes.
|
|
1097
|
+
// Now re-execute and propagate from all cycle nodes without restriction,
|
|
1098
|
+
// so that exit edges (outside the cycle) receive the converged states.
|
|
1099
|
+
// We must clear body nodes first and re-process them from scratch using
|
|
1100
|
+
// the converged head pre-state, since body nodes may contain stale states
|
|
1101
|
+
// from the last iteration that predate the final head re-execution.
|
|
1102
|
+
for (const ICFGNode* bodyNode : bodyNodes)
|
|
1103
|
+
abstractTrace.erase(bodyNode);
|
|
1104
|
+
handleICFGNode(cycle_head);
|
|
1105
|
+
propagateToSuccessor(cycle_head);
|
|
1106
|
+
for (const ICFGWTOComp* comp : cycle->getWTOComponents())
|
|
1107
|
+
{
|
|
1108
|
+
if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
|
|
1109
|
+
{
|
|
1110
|
+
const ICFGNode* bodyNode = singleton->getICFGNode();
|
|
1111
|
+
handleICFGNode(bodyNode);
|
|
1112
|
+
propagateToSuccessor(bodyNode);
|
|
1113
|
+
}
|
|
1114
|
+
else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
|
|
1115
|
+
{
|
|
1116
|
+
handleLoopOrRecursion(subCycle, caller);
|
|
1117
|
+
}
|
|
1133
1118
|
}
|
|
1134
1119
|
}
|
|
1135
1120
|
|