svf-tools 1.0.1256 → 1.0.1258

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.1256",
3
+ "version": "1.0.1258",
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
- AbstractValue loadValue(const ValVar* pointer, const ICFGNode* node);
176
- void storeValue(const ValVar* pointer, const AbstractValue& val, const ICFGNode* node);
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
- private:
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
- bool mergeStatesFromPredecessors(const ICFGNode* node);
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
- /// Returns true if the branch is reachable; narrows as in-place.
227
- bool isBranchFeasible(const IntraCFGEdge* edge, AbstractState& as);
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; narrows as in-place.
245
- bool isCmpBranchFeasible(const IntraCFGEdge* edge, AbstractState& as);
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; narrows as in-place.
248
- bool isSwitchBranchFeasible(const IntraCFGEdge* edge, AbstractState& as);
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. ValVar reads currently assert(false) until the
80
- /// SVFG-backed resolution lands (see `doc/plan-full-sparse.md`); cycle
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
- // Full-sparse ValVar resolution will route through the SVFG once
93
- // implemented; fail loudly until then rather than silently inherit
94
- // semi-sparse semantics.
95
- const AbstractValue& getAbsValue(const ValVar* var, const ICFGNode* node) override;
96
- using SemiSparseAbstractInterpretation::getAbsValue;
97
-
98
- bool hasAbsValue(const ValVar* var, const ICFGNode* node) const override;
99
- using SemiSparseAbstractInterpretation::hasAbsValue;
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
- const Set<const ICFGNode*> getUseSitesOfValVar(const ValVar* var) const;
102
- const ICFGNode* getDefSiteOfValVar(const ValVar* var) const;
103
- /// Given an ObjVar and its def-site ICFGNode, find all use-site ICFGNodes
104
- /// by following outgoing IndirectSVFGEdges whose pts contains the ObjVar
105
- const Set<const ICFGNode*> getDefSiteOfObjVar(const ObjVar* obj, const ICFGNode* node) const;
106
- /// Given an ObjVar and its def-site ICFGNode, find all use-site ICFGNodes
107
- /// by following outgoing IndirectSVFGEdges whose pts contains the ObjVar
108
- const Set<const ICFGNode*> getUseSitesOfObjVar(const ObjVar* obj, const ICFGNode* node) const;
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 (isBranchFeasible(intraCfgEdge, predState))
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 isCmpBranchFeasible for each non-constant operand that has a
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::isCmpBranchFeasible(const IntraCFGEdge* edge,
445
+ bool AbstractInterpretation::isCmpBranchEdgeFeasible(const IntraCFGEdge* edge,
446
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
- if (opVal[0].isAddr() || opVal[1].isAddr())
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,202 @@ 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::isSwitchBranchFeasible(const IntraCFGEdge* edge,
505
- AbstractState& as)
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
- if (!as.inVarToValTable(var->getId()) && !as.inVarToAddrsTable(var->getId()))
512
- as[var->getId()] = getAbsValue(var, pred);
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
- FIFOWorkList<const SVFStmt*> stmtList;
519
- for (SVFStmt* stmt : var->getInEdges())
520
- stmtList.push(stmt);
521
- while (!stmtList.empty())
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))
504
+ {
505
+ s32_t predicate = cmpStmt->getPredicate();
506
+
507
+ if (cmpStmt->getOpVarID(0) == IRGraph::NullPtr ||
508
+ cmpStmt->getOpVarID(1) == IRGraph::NullPtr)
509
+ {
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
+
518
+ const bool hasIntervalCmp =
519
+ opVal[0].isInterval() && opVal[1].isInterval();
520
+ if (!hasIntervalCmp && (opVal[0].isAddr() || opVal[1].isAddr()))
521
+ {
522
+ // Pointer-valued cmp: branch feasibility only.
523
+ }
524
+ else
525
+ {
526
+ for (int i = 0; i < 2; i++)
527
+ {
528
+ const int other = 1 - i;
529
+ const LoadStmt* load =
530
+ findBackingLoad(cmpStmt->getOpVar(i));
531
+
532
+ if (opVal[i].getInterval().is_numeral())
533
+ {
534
+ // Example: in x < 5, operand 5 is not refined.
535
+ }
536
+ else if (!opVal[other].getInterval().is_numeral())
537
+ {
538
+ // Example: x < y, neither side has a fixed bound.
539
+ }
540
+ else if (!load)
541
+ {
542
+ // Example: cmp uses a computed temporary, not load p.
543
+ }
544
+ else
545
+ {
546
+ IntervalValue narrowed = computeCmpConstraint(
547
+ predicate, succ, i == 0, opVal[i].getInterval(),
548
+ opVal[other].getInterval());
549
+
550
+ if (narrowed.isTop())
551
+ {
552
+ // != and unsupported predicates reach here.
553
+ }
554
+ else
555
+ {
556
+ const ICFGNode* loadIcfg = load->getICFGNode();
557
+ const AbstractValue& ptrVal =
558
+ getAbsValue(load->getRHSVar(), loadIcfg);
559
+ if (!ptrVal.isAddr())
560
+ {
561
+ // Cannot map load p back to concrete ObjVars.
562
+ }
563
+ else
564
+ {
565
+ for (const auto& addr : ptrVal.getAddrs())
566
+ {
567
+ NodeID objId = as.getIDFromAddr(addr);
568
+ recordBranchRefinement(objId, narrowed, as,
569
+ loadIcfg, succNode);
570
+ }
571
+ }
572
+ }
573
+ }
574
+ }
575
+ }
576
+ }
577
+ }
578
+ else
522
579
  {
523
- const SVFStmt* stmt = stmtList.pop();
524
- if (const LoadStmt* load = SVFUtil::dyn_cast<LoadStmt>(stmt))
580
+ const SVFVar* var = cond;
581
+
582
+ AbstractValue condVal = getAbsValue(var, pred);
583
+ IntervalValue switch_cond = condVal.getInterval();
584
+ switch_cond.meet_with(IntervalValue(succ, succ));
585
+ if (switch_cond.isBottom())
525
586
  {
526
- const AbstractValue& ptrVal = getAbsValue(load->getRHSVar(), pred);
527
- if (ptrVal.isAddr())
587
+ // This case label is not reachable from cond's interval.
588
+ }
589
+ else
590
+ {
591
+ as[var->getId()] = AbstractValue(switch_cond);
592
+
593
+ FIFOWorkList<const SVFStmt*> stmtList;
594
+ for (SVFStmt* stmt : var->getInEdges())
595
+ stmtList.push(stmt);
596
+ while (!stmtList.empty())
528
597
  {
529
- for (const auto& addr : ptrVal.getAddrs())
598
+ const SVFStmt* stmt = stmtList.pop();
599
+ const LoadStmt* load = SVFUtil::dyn_cast<LoadStmt>(stmt);
600
+ if (!load)
530
601
  {
531
- NodeID objId = as.getIDFromAddr(addr);
532
- if (as.inAddrToValTable(objId))
533
- as.load(addr).meet_with(switch_cond);
602
+ // Skip non-load definitions of the switch condition.
603
+ }
604
+ else
605
+ {
606
+ const ICFGNode* loadIcfg = load->getICFGNode();
607
+ const AbstractValue& ptrVal =
608
+ getAbsValue(load->getRHSVar(), loadIcfg);
609
+ if (!ptrVal.isAddr())
610
+ {
611
+ // Cannot map load p back to concrete ObjVars.
612
+ }
613
+ else
614
+ {
615
+ for (const auto& addr : ptrVal.getAddrs())
616
+ {
617
+ NodeID objId = as.getIDFromAddr(addr);
618
+ recordBranchRefinement(objId, switch_cond, as,
619
+ loadIcfg, succNode);
620
+ }
621
+ }
534
622
  }
535
623
  }
536
624
  }
537
625
  }
538
- return true;
539
626
  }
540
627
 
541
- bool AbstractInterpretation::isBranchFeasible(const IntraCFGEdge* edge,
628
+ void AbstractInterpretation::recordBranchRefinement(
629
+ NodeID objId, const IntervalValue& narrowed, AbstractState& as,
630
+ const ICFGNode* loadIcfg, const ICFGNode* /*succ*/)
631
+ {
632
+ // Default (dense / semi-sparse): MEET narrowed onto obj's current
633
+ // value, store back into the local `as`. Caller's joinStates
634
+ // propagates `as` into `merged`, then `updateAbsState(succ, merged)`
635
+ // commits it to trace[succ].
636
+ //
637
+ // We can't go through the polymorphic updateAbsValue here: `as` is
638
+ // a transient per-edge predState copy that lives outside
639
+ // abstractTrace, so it has no node id. Writing via `updateAbsValue`
640
+ // with `succ` as the node would land in trace[succ] but get
641
+ // clobbered by the subsequent `updateAbsState(succ, merged)`; with
642
+ // `loadIcfg` it would corrupt the obj's authoritative value at its
643
+ // load site. AbstractState::store on the transient `as` is the
644
+ // only sound primitive — and recordBranchRefinement itself is the
645
+ // virtual customisation point (FullSparse routes to
646
+ // refinementTrace instead of touching `as`).
647
+ const ObjVar* objVar = SVFUtil::dyn_cast<ObjVar>(svfir->getGNode(objId));
648
+ if (objVar && hasAbsValue(objVar, loadIcfg))
649
+ {
650
+ AbstractValue cur = getAbsValue(objVar, loadIcfg);
651
+ if (cur.isInterval())
652
+ {
653
+ IntervalValue itv = cur.getInterval();
654
+ itv.meet_with(narrowed);
655
+ u32_t addr = AbstractState::getVirtualMemAddress(objId);
656
+ as.store(addr, AbstractValue(itv));
657
+ }
658
+ }
659
+ }
660
+
661
+ bool AbstractInterpretation::isBranchEdgeFeasible(const IntraCFGEdge* edge,
542
662
  AbstractState& as)
543
663
  {
544
664
  const SVFVar* cmpVar = edge->getCondition();
545
665
  assert(!cmpVar->getInEdges().empty() && "branch condition has no defining edge?");
546
666
  if (SVFUtil::isa<CmpStmt>(*cmpVar->getInEdges().begin()))
547
- return isCmpBranchFeasible(edge, as);
548
- return isSwitchBranchFeasible(edge, as);
667
+ return isCmpBranchEdgeFeasible(edge, as);
668
+ return isSwitchBranchEdgeFeasible(edge, as);
549
669
  }
550
670
 
551
671
  /**
@@ -854,7 +974,7 @@ void AbstractInterpretation::updateStateOnPhi(const PhiStmt *phi)
854
974
  const IntraCFGEdge* intraEdge = SVFUtil::cast<IntraCFGEdge>(edge);
855
975
  if (intraEdge->getCondition())
856
976
  {
857
- if (isBranchFeasible(intraEdge, tmpState))
977
+ if (isBranchEdgeFeasible(intraEdge, tmpState))
858
978
  rhs.join_with(opVal);
859
979
  }
860
980
  else
@@ -1200,15 +1320,16 @@ void AbstractInterpretation::updateStateOnCmp(const CmpStmt *cmp)
1200
1320
  void AbstractInterpretation::updateStateOnLoad(const LoadStmt *load)
1201
1321
  {
1202
1322
  const ICFGNode* node = load->getICFGNode();
1203
- updateAbsValue(load->getLHSVar(),
1204
- loadValue(SVFUtil::cast<ValVar>(load->getRHSVar()), node), node);
1323
+ AbstractValue loaded =
1324
+ loadValue(SVFUtil::cast<ValVar>(load->getRHSVar()), node);
1325
+ updateAbsValue(load->getLHSVar(), loaded, node);
1205
1326
  }
1206
1327
 
1207
1328
  void AbstractInterpretation::updateStateOnStore(const StoreStmt *store)
1208
1329
  {
1209
1330
  const ICFGNode* node = store->getICFGNode();
1210
- storeValue(SVFUtil::cast<ValVar>(store->getLHSVar()),
1211
- getAbsValue(store->getRHSVar(), node), node);
1331
+ AbstractValue val = getAbsValue(store->getRHSVar(), node);
1332
+ storeValue(SVFUtil::cast<ValVar>(store->getLHSVar()), val, node);
1212
1333
  }
1213
1334
 
1214
1335
  void AbstractInterpretation::updateStateOnCopy(const CopyStmt *copy)
@@ -1352,5 +1473,3 @@ void AbstractInterpretation::updateStateOnCopy(const CopyStmt *copy)
1352
1473
  else
1353
1474
  assert(false && "undefined copy kind");
1354
1475
  }
1355
-
1356
-