svf-tools 1.0.1255 → 1.0.1257
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 +1 -1
- package/svf/include/AE/Svfexe/AbstractInterpretation.h +39 -14
- package/svf/include/AE/Svfexe/SparseAbstractInterpretation.h +78 -21
- package/svf/lib/AE/Svfexe/AEDetector.cpp +4 -3
- package/svf/lib/AE/Svfexe/AEStat.cpp +0 -1
- package/svf/lib/AE/Svfexe/AbsExtAPI.cpp +1 -39
- package/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +183 -65
- package/svf/lib/AE/Svfexe/AbstractStateManager.cpp +5 -69
- package/svf/lib/AE/Svfexe/SparseAbstractInterpretation.cpp +590 -50
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svf-tools",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1257",
|
|
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": {
|
|
@@ -172,8 +172,11 @@ public:
|
|
|
172
172
|
IntervalValue getGepByteOffset(const GepStmt* gep);
|
|
173
173
|
AddressValue getGepObjAddrs(const ValVar* pointer, IntervalValue offset);
|
|
174
174
|
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
/// Virtual so full-sparse can layer the GepObj overlay on top.
|
|
176
|
+
virtual AbstractValue loadValue(const ValVar* pointer,
|
|
177
|
+
const ICFGNode* node);
|
|
178
|
+
virtual void storeValue(const ValVar* pointer, const AbstractValue& val,
|
|
179
|
+
const ICFGNode* node);
|
|
177
180
|
|
|
178
181
|
const SVFType* getPointeeElement(const ObjVar* var, const ICFGNode* node);
|
|
179
182
|
u32_t getAllocaInstByteSize(const AddrStmt* addr);
|
|
@@ -214,17 +217,38 @@ protected:
|
|
|
214
217
|
virtual bool narrowCycleState(const AbstractState& prev, const AbstractState& cur,
|
|
215
218
|
const ICFGCycleWTO* cycle);
|
|
216
219
|
|
|
217
|
-
|
|
218
|
-
/// Initialize abstract state for the global ICFG node and process global statements
|
|
219
|
-
virtual void handleGlobalNode();
|
|
220
|
-
|
|
220
|
+
protected:
|
|
221
221
|
/// Pull-based state merge: read abstractTrace[pred] for each predecessor,
|
|
222
222
|
/// apply branch refinement for conditional IntraCFGEdges, and join into
|
|
223
223
|
/// abstractTrace[node]. Returns true if at least one predecessor had state.
|
|
224
|
-
|
|
224
|
+
/// Virtual so full-sparse can layer per-MRSVFGNode obj pulls on top of the
|
|
225
|
+
/// base ICFG-edge merge.
|
|
226
|
+
virtual bool mergeStatesFromPredecessors(const ICFGNode* node);
|
|
227
|
+
|
|
228
|
+
/// Returns true if the branch edge is reachable under the current state.
|
|
229
|
+
/// Pure query: does not update `as` or branch refinement traces.
|
|
230
|
+
bool isBranchEdgeFeasible(const IntraCFGEdge* edge, AbstractState& as);
|
|
231
|
+
|
|
232
|
+
/// Collect branch-induced interval refinement after a feasible edge has
|
|
233
|
+
/// been selected for normal CFG-state merging.
|
|
234
|
+
void collectBranchRefinement(const IntraCFGEdge* edge, AbstractState& as);
|
|
235
|
+
|
|
236
|
+
/// Hook called by collectBranchRefinement for each obj that the
|
|
237
|
+
/// branch narrows. Default (dense/semi): MEET `narrowed` onto
|
|
238
|
+
/// obj's value (read at `loadIcfg` where sparse keeps it) and
|
|
239
|
+
/// write the result into the local `as` (per-edge predState copy)
|
|
240
|
+
/// so joinStates carries it to `succ`. FullSparse overrides to
|
|
241
|
+
/// capture into refinementTrace[succ] instead.
|
|
242
|
+
virtual void recordBranchRefinement(NodeID objId,
|
|
243
|
+
const IntervalValue& narrowed,
|
|
244
|
+
AbstractState& as,
|
|
245
|
+
const ICFGNode* loadIcfg,
|
|
246
|
+
const ICFGNode* succ);
|
|
225
247
|
|
|
226
|
-
|
|
227
|
-
|
|
248
|
+
private:
|
|
249
|
+
/// Initialize abstract state for the global ICFG node and process global
|
|
250
|
+
/// statements
|
|
251
|
+
virtual void handleGlobalNode();
|
|
228
252
|
|
|
229
253
|
/// Handle a call site node: dispatch to ext-call, direct-call, or indirect-call handling
|
|
230
254
|
virtual void handleCallSite(const ICFGNode* node);
|
|
@@ -241,11 +265,12 @@ private:
|
|
|
241
265
|
/// Dispatch an SVF statement (Addr/Binary/Cmp/Load/Store/Copy/Gep/Select/Phi/Call/Ret) to its handler
|
|
242
266
|
virtual void handleSVFStatement(const SVFStmt* stmt);
|
|
243
267
|
|
|
244
|
-
/// Returns true if the cmp-conditional branch is feasible
|
|
245
|
-
bool
|
|
268
|
+
/// Returns true if the cmp-conditional branch is feasible.
|
|
269
|
+
bool isCmpBranchEdgeFeasible(const IntraCFGEdge* edge, AbstractState& as);
|
|
246
270
|
|
|
247
|
-
/// Returns true if the switch branch is feasible
|
|
248
|
-
bool
|
|
271
|
+
/// Returns true if the switch branch is feasible.
|
|
272
|
+
bool isSwitchBranchEdgeFeasible(const IntraCFGEdge* edge,
|
|
273
|
+
AbstractState& as);
|
|
249
274
|
|
|
250
275
|
void updateStateOnAddr(const AddrStmt *addr);
|
|
251
276
|
|
|
@@ -309,4 +334,4 @@ protected:
|
|
|
309
334
|
|
|
310
335
|
bool shouldApplyNarrowing(const FunObjVar* fun);
|
|
311
336
|
};
|
|
312
|
-
}
|
|
337
|
+
} // namespace SVF
|
|
@@ -24,11 +24,15 @@
|
|
|
24
24
|
#define INCLUDE_AE_SVFEXE_SPARSEABSTRACTINTERPRETATION_H_
|
|
25
25
|
|
|
26
26
|
#include "AE/Svfexe/AbstractInterpretation.h"
|
|
27
|
+
#include <memory>
|
|
27
28
|
|
|
28
29
|
namespace SVF
|
|
29
30
|
{
|
|
30
31
|
|
|
31
32
|
class SVFG;
|
|
33
|
+
class SVFGBuilder;
|
|
34
|
+
class IndirectSVFGEdge;
|
|
35
|
+
class VFGNode;
|
|
32
36
|
|
|
33
37
|
/// Abstract Interpretation for `Options::AESparsity::SemiSparse`.
|
|
34
38
|
///
|
|
@@ -76,10 +80,8 @@ protected:
|
|
|
76
80
|
///
|
|
77
81
|
/// In full-sparse mode both ValVars and ObjVars live at their SVFG
|
|
78
82
|
/// def-sites; reads query the SVFG for the reaching-def site, writes
|
|
79
|
-
/// happen at def-sites.
|
|
80
|
-
///
|
|
81
|
-
/// helpers are inherited from the semi-sparse parent and will also need
|
|
82
|
-
/// extension for ObjVars.
|
|
83
|
+
/// happen at def-sites. See `doc/plan-full-sparse.md` for the
|
|
84
|
+
/// phase plan; Phase 1 routes ValVar and ObjVar reads through the SVFG.
|
|
83
85
|
class FullSparseAbstractInterpretation : public SemiSparseAbstractInterpretation
|
|
84
86
|
{
|
|
85
87
|
public:
|
|
@@ -89,28 +91,83 @@ public:
|
|
|
89
91
|
}
|
|
90
92
|
~FullSparseAbstractInterpretation() override;
|
|
91
93
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
94
|
+
protected:
|
|
95
|
+
/// Full-sparse does not merge normal value-flow state along ICFG
|
|
96
|
+
/// edges. The ICFG join carries only side-channel state that is not
|
|
97
|
+
/// represented as MemorySSA def-use flow: GepObjVar field snapshots
|
|
98
|
+
/// and `_freedAddrs`. Base/Dummy ObjVars are populated later by
|
|
99
|
+
/// pullObjValueFlows from SVFG indirect in-edges; ValVars stay at their
|
|
100
|
+
/// def-sites.
|
|
101
|
+
void joinStates(AbstractState& dst, const AbstractState& src) override;
|
|
100
102
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
///
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
///
|
|
108
|
-
|
|
103
|
+
/// After a store overwrites an ObjVar, clear any branch refinement
|
|
104
|
+
/// for that ObjVar at the store's node so stale branch constraints
|
|
105
|
+
/// don't propagate past the redefinition.
|
|
106
|
+
void storeValue(const ValVar* pointer, const AbstractValue& val,
|
|
107
|
+
const ICFGNode* node) override;
|
|
108
|
+
|
|
109
|
+
/// Thin wrapper: defer to base for ICFG-edge bookkeeping
|
|
110
|
+
/// (predecessor iteration, branch feasibility, joinStates,
|
|
111
|
+
/// updateAbsState, reachability return). For reachable nodes,
|
|
112
|
+
/// additionally run pullObjValueFlows to populate trace[node] with obj
|
|
113
|
+
/// values from SVFG def-sites.
|
|
114
|
+
bool mergeStatesFromPredecessors(const ICFGNode* node) override;
|
|
115
|
+
|
|
116
|
+
/// Capture branch narrowings into refinementTrace[succ] instead of
|
|
117
|
+
/// writing them into the local `as`: in FullSparse `as` would be
|
|
118
|
+
/// discarded by joinStates (no-op for ObjVar), so we route the
|
|
119
|
+
/// narrowing to refinementTrace and let propagateAndApplyRefinement
|
|
120
|
+
/// bake it into trace at the end of mergeStatesFromPredecessors.
|
|
121
|
+
void recordBranchRefinement(NodeID objId, const IntervalValue& narrowed,
|
|
122
|
+
AbstractState& as, const ICFGNode* loadIcfg,
|
|
123
|
+
const ICFGNode* succ) override;
|
|
124
|
+
|
|
125
|
+
private:
|
|
126
|
+
/// SVFG-pull helper: walk each VFG node's indirect SVFG in-edges
|
|
127
|
+
/// and pull obj values from upstream def-site traces into
|
|
128
|
+
/// trace[node]. Multiple sources (e.g. mphi operands) JOIN.
|
|
129
|
+
void pullObjValueFlows(const ICFGNode* node);
|
|
130
|
+
|
|
131
|
+
/// Return whether an indirect SVFG edge should be pulled into dst.
|
|
132
|
+
/// Besides branch-feasible ICFG reachability, this rejects paths where
|
|
133
|
+
/// another store to the same points-to object kills the edge's value.
|
|
134
|
+
bool isIndirectSVFGEdgeFeasible(const IndirectSVFGEdge* edge,
|
|
135
|
+
const VFGNode* dst);
|
|
136
|
+
|
|
137
|
+
/// Return whether a branch-feasible ICFG path exists from src to dst.
|
|
138
|
+
/// Conditional edges are checked with a pure branch-feasibility query,
|
|
139
|
+
/// so path probing does not create branch-refinement side effects.
|
|
140
|
+
bool isICFGPathFeasible(const ICFGNode* src, const ICFGNode* dst);
|
|
141
|
+
|
|
142
|
+
/// Return whether this intra edge is allowed by the current branch state.
|
|
143
|
+
bool isIntraEdgeBranchFeasible(const IntraCFGEdge* edge,
|
|
144
|
+
const ICFGNode* src);
|
|
145
|
+
|
|
146
|
+
/// Compose pred-inherited refinement into refinementTrace[node]
|
|
147
|
+
/// (single-pred linear copy / multi-pred intersect-JOIN; any pred
|
|
148
|
+
/// without refinement drops the inheritance), then MEET the final
|
|
149
|
+
/// refinementTrace[node] into trace[node]._addrToAbsVal so the
|
|
150
|
+
/// inherited base getAbsValue(ObjVar*, node) returns the narrowed
|
|
151
|
+
/// value directly — no read-time override or cache. Called once
|
|
152
|
+
/// per merge as the last step.
|
|
153
|
+
void propagateAndApplyRefinement(const ICFGNode* node);
|
|
154
|
+
|
|
155
|
+
/// Path-refined obj values produced by branch narrowing. Each
|
|
156
|
+
/// entry is the *interval constraint* (not effective value) so
|
|
157
|
+
/// base trace can widen/narrow independently. Cached at branch
|
|
158
|
+
/// successors by recordBranchRefinement; propagated and applied by
|
|
159
|
+
/// propagateAndApplyRefinement at the end of
|
|
160
|
+
/// mergeStatesFromPredecessors.
|
|
161
|
+
Map<const ICFGNode*, Map<NodeID, IntervalValue>> refinementTrace;
|
|
109
162
|
|
|
110
|
-
protected:
|
|
111
163
|
/// Build the SVFG on top of the semi-sparse precompute.
|
|
112
164
|
void buildSVFG();
|
|
113
165
|
|
|
166
|
+
/// Owns the SVFG (via SVFGBuilder's internal unique_ptr). Without
|
|
167
|
+
/// this, SVFGBuilder would be a local in buildSVFG() and free the
|
|
168
|
+
/// graph at scope exit, leaving `svfg` dangling.
|
|
169
|
+
std::unique_ptr<SVFGBuilder> svfgBuilder;
|
|
170
|
+
/// View pointer into svfgBuilder's graph; non-null after buildSVFG().
|
|
114
171
|
SVFG* svfg{nullptr};
|
|
115
172
|
};
|
|
116
173
|
|
|
@@ -568,6 +568,9 @@ void NullptrDerefDetector::handleStubFunctions(const CallICFGNode* callNode)
|
|
|
568
568
|
const ValVar* arg0Val = callNode->getArgument(0);
|
|
569
569
|
// opt may directly dereference a null pointer and call UNSAFE_LOAD(null)
|
|
570
570
|
bool isSafe = canSafelyDerefPtr(arg0Val, callNode) && arg0Val->getId() != 0;
|
|
571
|
+
SVFUtil::outs() << "[UNSAFE_LOAD] node=" << callNode->getId()
|
|
572
|
+
<< " arg0=" << arg0Val->getId() << " isSafe=" << isSafe
|
|
573
|
+
<< "\n";
|
|
571
574
|
if (!isSafe)
|
|
572
575
|
{
|
|
573
576
|
SVFUtil::outs() << SVFUtil::sucMsg("success: expected null dereference at UNSAFE_LOAD")
|
|
@@ -667,9 +670,7 @@ bool NullptrDerefDetector::canSafelyDerefPtr(const ValVar* value, const ICFGNode
|
|
|
667
670
|
{
|
|
668
671
|
auto& ae = AbstractInterpretation::getAEInstance();
|
|
669
672
|
const AbstractValue& AbsVal = ae.getAbsValue(value, node);
|
|
670
|
-
// uninit value cannot be dereferenced, return unsafe
|
|
671
673
|
if (isUninit(AbsVal)) return false;
|
|
672
|
-
// Interval Value (non-addr) is not the checkpoint of nullptr dereference, return safe
|
|
673
674
|
if (!AbsVal.isAddr()) return true;
|
|
674
675
|
for (const auto &addr: AbsVal.getAddrs())
|
|
675
676
|
{
|
|
@@ -684,4 +685,4 @@ bool NullptrDerefDetector::canSafelyDerefPtr(const ValVar* value, const ICFGNode
|
|
|
684
685
|
return false;
|
|
685
686
|
}
|
|
686
687
|
return true;
|
|
687
|
-
}
|
|
688
|
+
}
|
|
@@ -115,7 +115,6 @@ void AEStat::finializeStat()
|
|
|
115
115
|
generalNumMap["EXT_CallSite_Num"] = extCallSiteNum;
|
|
116
116
|
generalNumMap["NonEXT_CallSite_Num"] = callSiteNum;
|
|
117
117
|
timeStatMap["Total_Time(sec)"] = (double)(endTime - startTime) / TIMEINTERVAL;
|
|
118
|
-
|
|
119
118
|
}
|
|
120
119
|
|
|
121
120
|
void AEStat::performStat()
|
|
@@ -145,44 +145,6 @@ void AbsExtAPI::initExtFunMap()
|
|
|
145
145
|
};
|
|
146
146
|
func_map["set_value"] = svf_set_value;
|
|
147
147
|
|
|
148
|
-
auto sse_scanf = [&](const CallICFGNode* callNode)
|
|
149
|
-
{
|
|
150
|
-
//scanf("%d", &data);
|
|
151
|
-
if (callNode->arg_size() < 2) return;
|
|
152
|
-
AbstractState& as = getAbsState(callNode);
|
|
153
|
-
const AbstractValue& dstVal = ae->getAbsValue(callNode->getArgument(1), callNode);
|
|
154
|
-
if (!dstVal.isAddr()) return;
|
|
155
|
-
for (auto vaddr: dstVal.getAddrs())
|
|
156
|
-
{
|
|
157
|
-
u32_t objId = as.getIDFromAddr(vaddr);
|
|
158
|
-
AbstractValue range = getRangeLimitFromType(svfir->getSVFVar(objId)->getType());
|
|
159
|
-
as.store(vaddr, range);
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
auto sse_fscanf = [&](const CallICFGNode* callNode)
|
|
163
|
-
{
|
|
164
|
-
//fscanf(stdin, "%d", &data);
|
|
165
|
-
if (callNode->arg_size() < 3) return;
|
|
166
|
-
AbstractState& as = getAbsState(callNode);
|
|
167
|
-
const AbstractValue& dstVal = ae->getAbsValue(callNode->getArgument(2), callNode);
|
|
168
|
-
if (!dstVal.isAddr()) return;
|
|
169
|
-
for (auto vaddr: dstVal.getAddrs())
|
|
170
|
-
{
|
|
171
|
-
u32_t objId = as.getIDFromAddr(vaddr);
|
|
172
|
-
AbstractValue range = getRangeLimitFromType(svfir->getSVFVar(objId)->getType());
|
|
173
|
-
as.store(vaddr, range);
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
func_map["__isoc99_fscanf"] = sse_fscanf;
|
|
178
|
-
func_map["__isoc99_scanf"] = sse_scanf;
|
|
179
|
-
func_map["__isoc99_vscanf"] = sse_scanf;
|
|
180
|
-
func_map["fscanf"] = sse_fscanf;
|
|
181
|
-
func_map["scanf"] = sse_scanf;
|
|
182
|
-
func_map["sscanf"] = sse_scanf;
|
|
183
|
-
func_map["__isoc99_sscanf"] = sse_scanf;
|
|
184
|
-
func_map["vscanf"] = sse_scanf;
|
|
185
|
-
|
|
186
148
|
auto sse_fread = [&](const CallICFGNode *callNode)
|
|
187
149
|
{
|
|
188
150
|
if (callNode->arg_size() < 3) return;
|
|
@@ -737,4 +699,4 @@ IntervalValue AbsExtAPI::getRangeLimitFromType(const SVFType* type)
|
|
|
737
699
|
return IntervalValue::top();
|
|
738
700
|
// other types, return top interval
|
|
739
701
|
}
|
|
740
|
-
}
|
|
702
|
+
}
|
|
@@ -236,8 +236,9 @@ bool AbstractInterpretation::mergeStatesFromPredecessors(const ICFGNode* node)
|
|
|
236
236
|
if (intraCfgEdge->getCondition())
|
|
237
237
|
{
|
|
238
238
|
AbstractState predState = getAbsState(pred);
|
|
239
|
-
if (
|
|
239
|
+
if (isBranchEdgeFeasible(intraCfgEdge, predState))
|
|
240
240
|
{
|
|
241
|
+
collectBranchRefinement(intraCfgEdge, predState);
|
|
241
242
|
joinStates(merged, predState);
|
|
242
243
|
hasFeasiblePred = true;
|
|
243
244
|
}
|
|
@@ -285,7 +286,6 @@ bool AbstractInterpretation::mergeStatesFromPredecessors(const ICFGNode* node)
|
|
|
285
286
|
return true;
|
|
286
287
|
}
|
|
287
288
|
|
|
288
|
-
|
|
289
289
|
/// Given a cmp operand, walk its SSA def edge to find the LoadStmt that
|
|
290
290
|
/// produced it. This lets us trace back to the ObjVar in memory so that
|
|
291
291
|
/// branch narrowing can refine the stored value.
|
|
@@ -316,7 +316,7 @@ static const LoadStmt* findBackingLoad(const SVFVar* var)
|
|
|
316
316
|
/// branch direction (succ), which side it is on, and the other operand's
|
|
317
317
|
/// interval. Returns top if no useful narrowing is possible.
|
|
318
318
|
///
|
|
319
|
-
/// Called from
|
|
319
|
+
/// Called from collectBranchRefinement for each non-constant operand that has a
|
|
320
320
|
/// backing load. Given a branch condition like:
|
|
321
321
|
///
|
|
322
322
|
/// %cmp = icmp sgt %a, 5 ; a > 5
|
|
@@ -442,14 +442,13 @@ static IntervalValue computeCmpConstraint(s32_t predicate, s64_t succ,
|
|
|
442
442
|
return result;
|
|
443
443
|
}
|
|
444
444
|
|
|
445
|
-
bool AbstractInterpretation::
|
|
446
|
-
|
|
445
|
+
bool AbstractInterpretation::isCmpBranchEdgeFeasible(const IntraCFGEdge* edge,
|
|
446
|
+
AbstractState& as)
|
|
447
447
|
{
|
|
448
448
|
const ICFGNode* pred = edge->getSrcNode();
|
|
449
449
|
s64_t succ = edge->getSuccessorCondValue();
|
|
450
450
|
const CmpStmt* cmpStmt = SVFUtil::cast<CmpStmt>(
|
|
451
451
|
*edge->getCondition()->getInEdges().begin());
|
|
452
|
-
s32_t predicate = cmpStmt->getPredicate();
|
|
453
452
|
|
|
454
453
|
if (cmpStmt->getOpVarID(0) == IRGraph::NullPtr ||
|
|
455
454
|
cmpStmt->getOpVarID(1) == IRGraph::NullPtr)
|
|
@@ -460,7 +459,9 @@ bool AbstractInterpretation::isCmpBranchFeasible(const IntraCFGEdge* edge,
|
|
|
460
459
|
getAbsValue(cmpStmt->getOpVar(0), pred),
|
|
461
460
|
getAbsValue(cmpStmt->getOpVar(1), pred)
|
|
462
461
|
};
|
|
463
|
-
|
|
462
|
+
|
|
463
|
+
const bool hasIntervalCmp = opVal[0].isInterval() && opVal[1].isInterval();
|
|
464
|
+
if (!hasIntervalCmp && (opVal[0].isAddr() || opVal[1].isAddr()))
|
|
464
465
|
return true;
|
|
465
466
|
|
|
466
467
|
// Feasibility check: cmp result must be compatible with branch successor
|
|
@@ -469,83 +470,201 @@ bool AbstractInterpretation::isCmpBranchFeasible(const IntraCFGEdge* edge,
|
|
|
469
470
|
if (resVal.isBottom())
|
|
470
471
|
return false;
|
|
471
472
|
|
|
472
|
-
// For each operand: if it is the variable side (non-constant), has a
|
|
473
|
-
// backing load, and the branch imposes a useful constraint, narrow the
|
|
474
|
-
// ObjVar behind the load.
|
|
475
|
-
for (int i = 0; i < 2; i++)
|
|
476
|
-
{
|
|
477
|
-
if (opVal[i].getInterval().is_numeral() || !opVal[1-i].getInterval().is_numeral())
|
|
478
|
-
continue; // skip constant operands and var-vs-var
|
|
479
|
-
|
|
480
|
-
if (const LoadStmt* load = findBackingLoad(cmpStmt->getOpVar(i)))
|
|
481
|
-
{
|
|
482
|
-
IntervalValue narrowed = computeCmpConstraint(
|
|
483
|
-
predicate, succ, i == 0,
|
|
484
|
-
opVal[i].getInterval(), opVal[1-i].getInterval());
|
|
485
|
-
|
|
486
|
-
if (!narrowed.isTop())
|
|
487
|
-
{
|
|
488
|
-
const AbstractValue& ptrVal = getAbsValue(load->getRHSVar(), pred);
|
|
489
|
-
if (ptrVal.isAddr())
|
|
490
|
-
{
|
|
491
|
-
for (const auto& addr : ptrVal.getAddrs())
|
|
492
|
-
{
|
|
493
|
-
NodeID objId = as.getIDFromAddr(addr);
|
|
494
|
-
if (as.inAddrToValTable(objId))
|
|
495
|
-
as.load(addr).meet_with(narrowed);
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
473
|
return true;
|
|
502
474
|
}
|
|
503
475
|
|
|
504
|
-
bool AbstractInterpretation::
|
|
505
|
-
|
|
476
|
+
bool AbstractInterpretation::isSwitchBranchEdgeFeasible(
|
|
477
|
+
const IntraCFGEdge* edge, AbstractState& as)
|
|
506
478
|
{
|
|
507
479
|
const ICFGNode* pred = edge->getSrcNode();
|
|
508
480
|
s64_t succ = edge->getSuccessorCondValue();
|
|
509
481
|
const SVFVar* var = edge->getCondition();
|
|
510
482
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
IntervalValue& switch_cond = as[var->getId()].getInterval();
|
|
483
|
+
AbstractValue condVal = getAbsValue(var, pred);
|
|
484
|
+
IntervalValue switch_cond = condVal.getInterval();
|
|
514
485
|
switch_cond.meet_with(IntervalValue(succ, succ));
|
|
515
486
|
if (switch_cond.isBottom())
|
|
516
487
|
return false;
|
|
488
|
+
return true;
|
|
489
|
+
}
|
|
517
490
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
491
|
+
void AbstractInterpretation::collectBranchRefinement(const IntraCFGEdge* edge,
|
|
492
|
+
AbstractState& as)
|
|
493
|
+
{
|
|
494
|
+
const SVFVar* cond = edge->getCondition();
|
|
495
|
+
const ICFGNode* pred = edge->getSrcNode();
|
|
496
|
+
const ICFGNode* succNode = edge->getDstNode();
|
|
497
|
+
s64_t succ = edge->getSuccessorCondValue();
|
|
498
|
+
|
|
499
|
+
assert(!cond->getInEdges().empty() &&
|
|
500
|
+
"branch condition has no defining edge?");
|
|
501
|
+
const SVFStmt* condDef = *cond->getInEdges().begin();
|
|
502
|
+
|
|
503
|
+
if (const CmpStmt* cmpStmt = SVFUtil::dyn_cast<CmpStmt>(condDef))
|
|
522
504
|
{
|
|
523
|
-
|
|
524
|
-
|
|
505
|
+
s32_t predicate = cmpStmt->getPredicate();
|
|
506
|
+
|
|
507
|
+
if (cmpStmt->getOpVarID(0) == IRGraph::NullPtr ||
|
|
508
|
+
cmpStmt->getOpVarID(1) == IRGraph::NullPtr)
|
|
525
509
|
{
|
|
526
|
-
|
|
527
|
-
|
|
510
|
+
// p == NULL / p != NULL: no interval obj to refine.
|
|
511
|
+
}
|
|
512
|
+
else
|
|
513
|
+
{
|
|
514
|
+
AbstractValue opVal[2] = {getAbsValue(cmpStmt->getOpVar(0), pred),
|
|
515
|
+
getAbsValue(cmpStmt->getOpVar(1), pred)};
|
|
516
|
+
|
|
517
|
+
const bool hasIntervalCmp =
|
|
518
|
+
opVal[0].isInterval() && opVal[1].isInterval();
|
|
519
|
+
if (!hasIntervalCmp && (opVal[0].isAddr() || opVal[1].isAddr()))
|
|
520
|
+
{
|
|
521
|
+
// Pointer-valued cmp: branch feasibility only.
|
|
522
|
+
}
|
|
523
|
+
else
|
|
528
524
|
{
|
|
529
|
-
for (
|
|
525
|
+
for (int i = 0; i < 2; i++)
|
|
530
526
|
{
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
527
|
+
const int other = 1 - i;
|
|
528
|
+
const LoadStmt* load =
|
|
529
|
+
findBackingLoad(cmpStmt->getOpVar(i));
|
|
530
|
+
|
|
531
|
+
if (opVal[i].getInterval().is_numeral())
|
|
532
|
+
{
|
|
533
|
+
// Example: in x < 5, operand 5 is not refined.
|
|
534
|
+
}
|
|
535
|
+
else if (!opVal[other].getInterval().is_numeral())
|
|
536
|
+
{
|
|
537
|
+
// Example: x < y, neither side has a fixed bound.
|
|
538
|
+
}
|
|
539
|
+
else if (!load)
|
|
540
|
+
{
|
|
541
|
+
// Example: cmp uses a computed temporary, not load p.
|
|
542
|
+
}
|
|
543
|
+
else
|
|
544
|
+
{
|
|
545
|
+
IntervalValue narrowed = computeCmpConstraint(
|
|
546
|
+
predicate, succ, i == 0, opVal[i].getInterval(),
|
|
547
|
+
opVal[other].getInterval());
|
|
548
|
+
|
|
549
|
+
if (narrowed.isTop())
|
|
550
|
+
{
|
|
551
|
+
// != and unsupported predicates reach here.
|
|
552
|
+
}
|
|
553
|
+
else
|
|
554
|
+
{
|
|
555
|
+
const ICFGNode* loadIcfg = load->getICFGNode();
|
|
556
|
+
const AbstractValue& ptrVal =
|
|
557
|
+
getAbsValue(load->getRHSVar(), loadIcfg);
|
|
558
|
+
if (!ptrVal.isAddr())
|
|
559
|
+
{
|
|
560
|
+
// Cannot map load p back to concrete ObjVars.
|
|
561
|
+
}
|
|
562
|
+
else
|
|
563
|
+
{
|
|
564
|
+
for (const auto& addr : ptrVal.getAddrs())
|
|
565
|
+
{
|
|
566
|
+
NodeID objId = as.getIDFromAddr(addr);
|
|
567
|
+
recordBranchRefinement(objId, narrowed, as,
|
|
568
|
+
loadIcfg, succNode);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
534
573
|
}
|
|
535
574
|
}
|
|
536
575
|
}
|
|
537
576
|
}
|
|
538
|
-
|
|
577
|
+
else
|
|
578
|
+
{
|
|
579
|
+
const SVFVar* var = cond;
|
|
580
|
+
|
|
581
|
+
AbstractValue condVal = getAbsValue(var, pred);
|
|
582
|
+
IntervalValue switch_cond = condVal.getInterval();
|
|
583
|
+
switch_cond.meet_with(IntervalValue(succ, succ));
|
|
584
|
+
if (switch_cond.isBottom())
|
|
585
|
+
{
|
|
586
|
+
// This case label is not reachable from cond's interval.
|
|
587
|
+
}
|
|
588
|
+
else
|
|
589
|
+
{
|
|
590
|
+
as[var->getId()] = AbstractValue(switch_cond);
|
|
591
|
+
|
|
592
|
+
FIFOWorkList<const SVFStmt*> stmtList;
|
|
593
|
+
for (SVFStmt* stmt : var->getInEdges())
|
|
594
|
+
stmtList.push(stmt);
|
|
595
|
+
while (!stmtList.empty())
|
|
596
|
+
{
|
|
597
|
+
const SVFStmt* stmt = stmtList.pop();
|
|
598
|
+
const LoadStmt* load = SVFUtil::dyn_cast<LoadStmt>(stmt);
|
|
599
|
+
if (!load)
|
|
600
|
+
{
|
|
601
|
+
// Skip non-load definitions of the switch condition.
|
|
602
|
+
}
|
|
603
|
+
else
|
|
604
|
+
{
|
|
605
|
+
const ICFGNode* loadIcfg = load->getICFGNode();
|
|
606
|
+
const AbstractValue& ptrVal =
|
|
607
|
+
getAbsValue(load->getRHSVar(), loadIcfg);
|
|
608
|
+
if (!ptrVal.isAddr())
|
|
609
|
+
{
|
|
610
|
+
// Cannot map load p back to concrete ObjVars.
|
|
611
|
+
}
|
|
612
|
+
else
|
|
613
|
+
{
|
|
614
|
+
for (const auto& addr : ptrVal.getAddrs())
|
|
615
|
+
{
|
|
616
|
+
NodeID objId = as.getIDFromAddr(addr);
|
|
617
|
+
recordBranchRefinement(objId, switch_cond, as,
|
|
618
|
+
loadIcfg, succNode);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
void AbstractInterpretation::recordBranchRefinement(
|
|
628
|
+
NodeID objId, const IntervalValue& narrowed, AbstractState& as,
|
|
629
|
+
const ICFGNode* loadIcfg, const ICFGNode* /*succ*/)
|
|
630
|
+
{
|
|
631
|
+
// Default (dense / semi-sparse): MEET narrowed onto obj's current
|
|
632
|
+
// value, store back into the local `as`. Caller's joinStates
|
|
633
|
+
// propagates `as` into `merged`, then `updateAbsState(succ, merged)`
|
|
634
|
+
// commits it to trace[succ].
|
|
635
|
+
//
|
|
636
|
+
// We can't go through the polymorphic updateAbsValue here: `as` is
|
|
637
|
+
// a transient per-edge predState copy that lives outside
|
|
638
|
+
// abstractTrace, so it has no node id. Writing via `updateAbsValue`
|
|
639
|
+
// with `succ` as the node would land in trace[succ] but get
|
|
640
|
+
// clobbered by the subsequent `updateAbsState(succ, merged)`; with
|
|
641
|
+
// `loadIcfg` it would corrupt the obj's authoritative value at its
|
|
642
|
+
// load site. AbstractState::store on the transient `as` is the
|
|
643
|
+
// only sound primitive — and recordBranchRefinement itself is the
|
|
644
|
+
// virtual customisation point (FullSparse routes to
|
|
645
|
+
// refinementTrace instead of touching `as`).
|
|
646
|
+
const ObjVar* objVar = SVFUtil::dyn_cast<ObjVar>(svfir->getGNode(objId));
|
|
647
|
+
if (objVar && hasAbsValue(objVar, loadIcfg))
|
|
648
|
+
{
|
|
649
|
+
AbstractValue cur = getAbsValue(objVar, loadIcfg);
|
|
650
|
+
if (cur.isInterval())
|
|
651
|
+
{
|
|
652
|
+
IntervalValue itv = cur.getInterval();
|
|
653
|
+
itv.meet_with(narrowed);
|
|
654
|
+
u32_t addr = AbstractState::getVirtualMemAddress(objId);
|
|
655
|
+
as.store(addr, AbstractValue(itv));
|
|
656
|
+
}
|
|
657
|
+
}
|
|
539
658
|
}
|
|
540
659
|
|
|
541
|
-
bool AbstractInterpretation::
|
|
542
|
-
|
|
660
|
+
bool AbstractInterpretation::isBranchEdgeFeasible(const IntraCFGEdge* edge,
|
|
661
|
+
AbstractState& as)
|
|
543
662
|
{
|
|
544
663
|
const SVFVar* cmpVar = edge->getCondition();
|
|
545
664
|
assert(!cmpVar->getInEdges().empty() && "branch condition has no defining edge?");
|
|
546
665
|
if (SVFUtil::isa<CmpStmt>(*cmpVar->getInEdges().begin()))
|
|
547
|
-
return
|
|
548
|
-
return
|
|
666
|
+
return isCmpBranchEdgeFeasible(edge, as);
|
|
667
|
+
return isSwitchBranchEdgeFeasible(edge, as);
|
|
549
668
|
}
|
|
550
669
|
|
|
551
670
|
/**
|
|
@@ -854,7 +973,7 @@ void AbstractInterpretation::updateStateOnPhi(const PhiStmt *phi)
|
|
|
854
973
|
const IntraCFGEdge* intraEdge = SVFUtil::cast<IntraCFGEdge>(edge);
|
|
855
974
|
if (intraEdge->getCondition())
|
|
856
975
|
{
|
|
857
|
-
if (
|
|
976
|
+
if (isBranchEdgeFeasible(intraEdge, tmpState))
|
|
858
977
|
rhs.join_with(opVal);
|
|
859
978
|
}
|
|
860
979
|
else
|
|
@@ -1200,15 +1319,16 @@ void AbstractInterpretation::updateStateOnCmp(const CmpStmt *cmp)
|
|
|
1200
1319
|
void AbstractInterpretation::updateStateOnLoad(const LoadStmt *load)
|
|
1201
1320
|
{
|
|
1202
1321
|
const ICFGNode* node = load->getICFGNode();
|
|
1203
|
-
|
|
1204
|
-
|
|
1322
|
+
AbstractValue loaded =
|
|
1323
|
+
loadValue(SVFUtil::cast<ValVar>(load->getRHSVar()), node);
|
|
1324
|
+
updateAbsValue(load->getLHSVar(), loaded, node);
|
|
1205
1325
|
}
|
|
1206
1326
|
|
|
1207
1327
|
void AbstractInterpretation::updateStateOnStore(const StoreStmt *store)
|
|
1208
1328
|
{
|
|
1209
1329
|
const ICFGNode* node = store->getICFGNode();
|
|
1210
|
-
|
|
1211
|
-
|
|
1330
|
+
AbstractValue val = getAbsValue(store->getRHSVar(), node);
|
|
1331
|
+
storeValue(SVFUtil::cast<ValVar>(store->getLHSVar()), val, node);
|
|
1212
1332
|
}
|
|
1213
1333
|
|
|
1214
1334
|
void AbstractInterpretation::updateStateOnCopy(const CopyStmt *copy)
|
|
@@ -1352,5 +1472,3 @@ void AbstractInterpretation::updateStateOnCopy(const CopyStmt *copy)
|
|
|
1352
1472
|
else
|
|
1353
1473
|
assert(false && "undefined copy kind");
|
|
1354
1474
|
}
|
|
1355
|
-
|
|
1356
|
-
|