svf-tools 1.0.1082 → 1.0.1084

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.1082",
3
+ "version": "1.0.1084",
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": {
@@ -61,8 +61,9 @@ class AbstractState
61
61
  friend class RelationSolver;
62
62
  public:
63
63
  typedef Map<u32_t, AbstractValue> VarToAbsValMap;
64
-
65
64
  typedef VarToAbsValMap AddrToAbsValMap;
65
+ Set<NodeID> _freedAddrs;
66
+
66
67
 
67
68
  public:
68
69
  /// default constructor
@@ -73,7 +74,7 @@ public:
73
74
  AbstractState(VarToAbsValMap&_varToValMap, AddrToAbsValMap&_locToValMap) : _varToAbsVal(_varToValMap), _addrToAbsVal(_locToValMap) {}
74
75
 
75
76
  /// copy constructor
76
- AbstractState(const AbstractState&rhs) : _varToAbsVal(rhs.getVarToVal()), _addrToAbsVal(rhs.getLocToVal())
77
+ AbstractState(const AbstractState&rhs) : _freedAddrs(rhs._freedAddrs), _varToAbsVal(rhs.getVarToVal()), _addrToAbsVal(rhs.getLocToVal())
77
78
  {
78
79
 
79
80
  }
@@ -110,15 +111,10 @@ public:
110
111
  return AddressValue::isVirtualMemAddress(val);
111
112
  }
112
113
 
113
- /// Return the internal index if idx is an address otherwise return the value of idx
114
- static inline u32_t getInternalID(u32_t idx)
115
- {
116
- return AddressValue::getInternalID(idx);
117
- }
118
-
119
- static inline bool isNullPtr(u32_t addr)
114
+ /// Return the internal index if addr is an address otherwise return the value of idx
115
+ inline u32_t getIDFromAddr(u32_t addr)
120
116
  {
121
- return getInternalID(addr) == 0;
117
+ return _freedAddrs.count(addr) ? AddressValue::getInternalID(InvalidMemAddr) : AddressValue::getInternalID(addr);
122
118
  }
123
119
 
124
120
  AbstractState&operator=(const AbstractState&rhs)
@@ -127,6 +123,7 @@ public:
127
123
  {
128
124
  _varToAbsVal = rhs._varToAbsVal;
129
125
  _addrToAbsVal = rhs._addrToAbsVal;
126
+ _freedAddrs = rhs._freedAddrs;
130
127
  }
131
128
  return *this;
132
129
  }
@@ -145,6 +142,7 @@ public:
145
142
  {
146
143
  _varToAbsVal = std::move(rhs._varToAbsVal);
147
144
  _addrToAbsVal = std::move(rhs._addrToAbsVal);
145
+ _freedAddrs = std::move(rhs._freedAddrs);
148
146
  }
149
147
  return *this;
150
148
  }
@@ -184,6 +182,17 @@ public:
184
182
  return inv;
185
183
  }
186
184
 
185
+ static inline bool isNullMem(u32_t addr)
186
+ {
187
+ return AddressValue::getInternalID(addr) == NullMemAddr;
188
+ }
189
+
190
+ static inline bool isInvalidMem(u32_t addr)
191
+ {
192
+ return AddressValue::getInternalID(addr) == InvalidMemAddr;
193
+ }
194
+
195
+
187
196
  protected:
188
197
  VarToAbsValMap _varToAbsVal; ///< Map a variable (symbol) to its abstract value
189
198
  AddrToAbsValMap
@@ -279,10 +288,19 @@ public:
279
288
  /// domain join with other, important! other widen this.
280
289
  void joinWith(const AbstractState&other);
281
290
 
282
-
283
291
  /// domain meet with other, important! other widen this.
284
292
  void meetWith(const AbstractState&other);
285
293
 
294
+ void addToFreedAddrs(NodeID addr) {
295
+ _freedAddrs.insert(addr);
296
+ }
297
+
298
+ bool isFreedMem(u32_t addr) const
299
+ {
300
+ return _freedAddrs.find(addr) != _freedAddrs.end();
301
+ }
302
+
303
+
286
304
  /**
287
305
  * if this NodeID in SVFIR is a pointer, get the pointee type
288
306
  * e.g arr = (int*) malloc(10*sizeof(int))
@@ -299,20 +317,19 @@ public:
299
317
  inline void store(u32_t addr, const AbstractValue &val)
300
318
  {
301
319
  assert(isVirtualMemAddress(addr) && "not virtual address?");
302
- if (isNullPtr(addr)) return;
303
- u32_t objId = getInternalID(addr);
320
+ u32_t objId = getIDFromAddr(addr);
321
+ if (isNullMem(addr)) return;
304
322
  _addrToAbsVal[objId] = val;
305
323
  }
306
324
 
307
325
  inline virtual AbstractValue &load(u32_t addr)
308
326
  {
309
327
  assert(isVirtualMemAddress(addr) && "not virtual address?");
310
- u32_t objId = getInternalID(addr);
328
+ u32_t objId = getIDFromAddr(addr);
311
329
  return _addrToAbsVal[objId];
312
330
 
313
331
  }
314
332
 
315
-
316
333
  void printAbstractState() const;
317
334
 
318
335
  std::string toString() const
@@ -396,6 +413,7 @@ public:
396
413
  {
397
414
  _addrToAbsVal.clear();
398
415
  _varToAbsVal.clear();
416
+ _freedAddrs.clear();
399
417
  }
400
418
 
401
419
  };
@@ -32,8 +32,12 @@
32
32
 
33
33
  #define AddressMask 0x7f000000
34
34
  #define FlippedAddressMask (AddressMask^0xffffffff)
35
- // the address of the black hole, getVirtualMemAddress(2);
36
- #define BlackHoleAddr 0x7f000000 + 2
35
+ // the address of InvalidMem(the black hole), getVirtualMemAddress(2);
36
+ #define InvalidMemAddr 0x7f000000 + 2
37
+ // the address of NullMem, getVirtualMemAddress(0);
38
+ #define NullMemAddr 0x7f000000
39
+
40
+
37
41
 
38
42
  #include "Util/GeneralType.h"
39
43
  #include <sstream>
@@ -42,10 +46,19 @@ namespace SVF
42
46
  {
43
47
  class AddressValue
44
48
  {
49
+ friend class AbstractState;
50
+ friend class RelExeState;
45
51
  public:
46
52
  typedef Set<u32_t> AddrSet;
47
53
  private:
48
54
  AddrSet _addrs;
55
+
56
+ /// Return the internal index if idx is an address otherwise return the value of idx
57
+ static inline u32_t getInternalID(u32_t idx)
58
+ {
59
+ return (idx & FlippedAddressMask);
60
+ }
61
+
49
62
  public:
50
63
  /// Default constructor
51
64
  AddressValue() {}
@@ -168,26 +181,11 @@ public:
168
181
  return !v.empty();
169
182
  }
170
183
 
171
- inline bool isTop() const
172
- {
173
- return *this->begin() == BlackHoleAddr;
174
- }
175
-
176
184
  inline bool isBottom() const
177
185
  {
178
186
  return empty();
179
187
  }
180
188
 
181
- inline void setTop()
182
- {
183
- *this = AddressValue(BlackHoleAddr);
184
- }
185
-
186
- inline void setBottom()
187
- {
188
- _addrs.clear();
189
- }
190
-
191
189
  const std::string toString() const
192
190
  {
193
191
  std::string str;
@@ -222,11 +220,6 @@ public:
222
220
  return (val & 0xff000000) == AddressMask && val != AddressMask + 0;
223
221
  }
224
222
 
225
- /// Return the internal index if idx is an address otherwise return the value of idx
226
- static inline u32_t getInternalID(u32_t idx)
227
- {
228
- return (idx & FlippedAddressMask);
229
- }
230
223
  };
231
224
  } // end namespace SVF
232
225
  #endif //Z3_EXAMPLE_ADDRESSVALUE_H
@@ -45,6 +45,7 @@ public:
45
45
  enum DetectorKind
46
46
  {
47
47
  BUF_OVERFLOW, ///< Detector for buffer overflow issues.
48
+ NULL_DEREF, ///< Detector for nullptr dereference issues.
48
49
  UNKNOWN, ///< Default type if the kind is not specified.
49
50
  };
50
51
 
@@ -164,7 +165,8 @@ public:
164
165
  * @param objAddrs Address value for the object.
165
166
  * @param offset The interval value of the offset.
166
167
  */
167
- void updateGepObjOffsetFromBase(AddressValue gepAddrs,
168
+ void updateGepObjOffsetFromBase(AbstractState& as,
169
+ AddressValue gepAddrs,
168
170
  AddressValue objAddrs,
169
171
  IntervalValue offset);
170
172
 
@@ -320,4 +322,115 @@ private:
320
322
  SVFBugReport recoder; ///< Recorder for abstract execution bugs.
321
323
  Map<const ICFGNode*, std::string> nodeToBugInfo; ///< Maps ICFG nodes to bug information.
322
324
  };
325
+ class NullptrDerefDetector : public AEDetector{
326
+ friend class AbstractInterpretation;
327
+ public:
328
+ NullptrDerefDetector()
329
+ {
330
+ kind = NULL_DEREF;
331
+ }
332
+
333
+ ~NullptrDerefDetector() = default;
334
+
335
+ static bool classof(const AEDetector* detector)
336
+ {
337
+ return detector->getKind() == AEDetector::NULL_DEREF;
338
+ }
339
+
340
+ /**
341
+ * @brief Detects nullptr dereferences issues within a node.
342
+ * @param as Reference to the abstract state.
343
+ * @param node Pointer to the ICFG node.
344
+ */
345
+ void detect(AbstractState& as, const ICFGNode* node);
346
+
347
+ /**
348
+ * @brief Handles external API calls related to nullptr dereferences.
349
+ * @param call Pointer to the call ICFG node.
350
+ */
351
+ void handleStubFunctions(const CallICFGNode* call);
352
+
353
+ /**
354
+ * @brief Checks if an Abstract Value is uninitialized.
355
+ * @param v The Abstract Value to check.
356
+ * @return True if the value is uninitialized, false otherwise.
357
+ */
358
+ bool isUninit(AbstractValue v)
359
+ {
360
+ bool is = v.getAddrs().isBottom() && v.getInterval().isBottom();
361
+ return is;
362
+ }
363
+
364
+ /**
365
+ * @brief Adds a bug to the reporter based on an exception.
366
+ * @param e The exception that was thrown.
367
+ * @param node Pointer to the ICFG node where the bug was detected.
368
+ */
369
+ void addBugToReporter(const AEException& e, const ICFGNode* node)
370
+ {
371
+ GenericBug::EventStack eventStack;
372
+ SVFBugEvent sourceInstEvent(SVFBugEvent::EventType::SourceInst, node);
373
+ eventStack.push_back(sourceInstEvent); // Add the source instruction event to the event stack
374
+
375
+ if (eventStack.empty())
376
+ {
377
+ return; // If the event stack is empty, return early
378
+ }
379
+ std::string loc = eventStack.back().getEventLoc(); // Get the location of the last event in the stack
380
+
381
+ // Check if the bug at this location has already been reported
382
+ if (bugLoc.find(loc) != bugLoc.end())
383
+ {
384
+ return; // If the bug location is already reported, return early
385
+ }
386
+ else
387
+ {
388
+ bugLoc.insert(loc); // Otherwise, mark this location as reported
389
+ }
390
+ recoder.addAbsExecBug(GenericBug::FULLNULLPTRDEREFERENCE, eventStack, 0, 0, 0, 0);
391
+ nodeToBugInfo[node] = e.what(); // Record the exception information for the node
392
+ }
393
+
394
+ /**
395
+ * @brief Reports all detected nullptr dereference bugs.
396
+ */
397
+ void reportBug()
398
+ {
399
+ if (!nodeToBugInfo.empty())
400
+ {
401
+ std::cerr << "###################### Nullptr Dereference (" + std::to_string(nodeToBugInfo.size())
402
+ + " found)######################\n";
403
+ std::cerr << "---------------------------------------------\n";
404
+ for (const auto& it : nodeToBugInfo)
405
+ {
406
+ std::cerr << it.second << "\n---------------------------------------------\n";
407
+ }
408
+ }
409
+ }
410
+
411
+ /**
412
+ * @brief Handle external API calls related to nullptr dereferences.
413
+ * @param as Reference to the abstract state.
414
+ * @param call Pointer to the call ICFG node.
415
+ */
416
+ void detectExtAPI(AbstractState& as, const CallICFGNode* call);
417
+
418
+
419
+ /**
420
+ * @brief Check if an Abstract Value is NULL (or uninitialized).
421
+ *
422
+ * @param v An Abstract Value of loaded from an address in an Abstract State.
423
+ */
424
+ bool isNull(AbstractValue v)
425
+ {
426
+ return !v.isAddr() && !v.isInterval();
427
+ }
428
+
429
+ bool canSafelyDerefPtr(AbstractState& as, const SVFVar* ptr);
430
+
431
+ private:
432
+ Set<std::string> bugLoc; ///< Set of locations where bugs have been reported.
433
+ SVFBugReport recoder; ///< Recorder for abstract execution bugs.
434
+ Map<const ICFGNode*, std::string> nodeToBugInfo; ///< Maps ICFG nodes to bug information.
435
+ };
323
436
  }
@@ -105,6 +105,7 @@ class AbstractInterpretation
105
105
  friend class AEStat;
106
106
  friend class AEAPI;
107
107
  friend class BufOverflowDetector;
108
+ friend class NullptrDerefDetector;
108
109
 
109
110
  public:
110
111
  typedef SCCDetection<CallGraph*> CallGraphSCC;
@@ -155,6 +156,25 @@ public:
155
156
 
156
157
  Set<const CallICFGNode*> checkpoints; // for CI check
157
158
 
159
+ /**
160
+ * @brief Retrieves the abstract state from the trace for a given ICFG node.
161
+ * @param node Pointer to the ICFG node.
162
+ * @return Reference to the abstract state.
163
+ * @throws Assertion if no trace exists for the node.
164
+ */
165
+ AbstractState& getAbsStateFromTrace(const ICFGNode* node)
166
+ {
167
+ const ICFGNode* repNode = icfg->getRepNode(node);
168
+ if (abstractTrace.count(repNode) == 0)
169
+ {
170
+ assert(false && "No preAbsTrace for this node");
171
+ }
172
+ else
173
+ {
174
+ return abstractTrace[repNode];
175
+ }
176
+ }
177
+
158
178
  private:
159
179
  /// Global ICFGNode is handled at the entry of the program,
160
180
  virtual void handleGlobalNode();
@@ -280,19 +300,6 @@ private:
280
300
  Set<const FunObjVar*> recursiveFuns;
281
301
 
282
302
 
283
- AbstractState& getAbsStateFromTrace(const ICFGNode* node)
284
- {
285
- const ICFGNode* repNode = icfg->getRepNode(node);
286
- if (abstractTrace.count(repNode) == 0)
287
- {
288
- assert(0 && "No preAbsTrace for this node");
289
- }
290
- else
291
- {
292
- return abstractTrace[repNode];
293
- }
294
- }
295
-
296
303
  bool hasAbsStateFromTrace(const ICFGNode* node)
297
304
  {
298
305
  const ICFGNode* repNode = icfg->getRepNode(node);
@@ -253,6 +253,8 @@ public:
253
253
  static const Option<std::string> OutputName;
254
254
  /// buffer overflow checker, Default: false
255
255
  static const Option<bool> BufferOverflowCheck;
256
+ /// nullptr dereference checker, Default: false
257
+ static const Option<bool> NullDerefCheck;
256
258
  /// memory leak check, Default: false
257
259
  static const Option<bool> MemoryLeakCheck;
258
260
  /// file open close checker, Default: false
@@ -127,6 +127,7 @@ void AbstractState::joinWith(const AbstractState& other)
127
127
  _addrToAbsVal.emplace(key, it->second);
128
128
  }
129
129
  }
130
+ _freedAddrs.insert(other._freedAddrs.begin(), other._freedAddrs.end());
130
131
  }
131
132
 
132
133
  /// domain meet with other, important! other widen this.
@@ -150,6 +151,11 @@ void AbstractState::meetWith(const AbstractState& other)
150
151
  oit->second.meet_with(it->second);
151
152
  }
152
153
  }
154
+ Set<NodeID> intersection;
155
+ std::set_intersection(_freedAddrs.begin(), _freedAddrs.end(),
156
+ other._freedAddrs.begin(), other._freedAddrs.end(),
157
+ std::inserter(intersection, intersection.begin()));
158
+ _freedAddrs = std::move(intersection);
153
159
  }
154
160
 
155
161
  // getGepObjAddrs
@@ -165,7 +171,7 @@ AddressValue AbstractState::getGepObjAddrs(u32_t pointer, IntervalValue offset)
165
171
  AbstractValue addrs = (*this)[pointer];
166
172
  for (const auto& addr : addrs.getAddrs())
167
173
  {
168
- s64_t baseObj = AbstractState::getInternalID(addr);
174
+ s64_t baseObj = getIDFromAddr(addr);
169
175
  assert(SVFUtil::isa<ObjVar>(PAG::getPAG()->getGNode(baseObj)) && "Fail to get the base object address!");
170
176
  NodeID gepObj = PAG::getPAG()->getGepObjVar(baseObj, i);
171
177
  (*this)[gepObj] = AddressValue(AbstractState::getVirtualMemAddress(gepObj));
@@ -470,7 +476,7 @@ const SVFType* AbstractState::getPointeeElement(NodeID id)
470
476
  const AbstractValue& addrs = (*this)[id];
471
477
  for (auto addr: addrs.getAddrs())
472
478
  {
473
- NodeID addr_id = AbstractState::getInternalID(addr);
479
+ NodeID addr_id = getIDFromAddr(addr);
474
480
  if (addr_id == 0) // nullptr skip
475
481
  continue;
476
482
  return svfir->getBaseObject(addr_id)->getType();
@@ -28,9 +28,9 @@
28
28
  #include <AE/Svfexe/AEDetector.h>
29
29
  #include <AE/Svfexe/AbsExtAPI.h>
30
30
  #include <AE/Svfexe/AbstractInterpretation.h>
31
+ #include "AE/Core/AddressValue.h"
31
32
 
32
33
  using namespace SVF;
33
-
34
34
  /**
35
35
  * @brief Detects buffer overflow issues within a given ICFG node.
36
36
  *
@@ -55,15 +55,14 @@ void BufOverflowDetector::detect(AbstractState& as, const ICFGNode* node)
55
55
  NodeID rhs = gep->getRHSVarID();
56
56
 
57
57
  // Update the GEP object offset from its base
58
- updateGepObjOffsetFromBase(as[lhs].getAddrs(), as[rhs].getAddrs(), as.getByteOffset(gep));
58
+ updateGepObjOffsetFromBase(as, as[lhs].getAddrs(), as[rhs].getAddrs(), as.getByteOffset(gep));
59
59
 
60
60
  IntervalValue baseObjSize = IntervalValue::bottom();
61
61
  AddressValue objAddrs = as[gep->getRHSVarID()].getAddrs();
62
62
  for (const auto& addr : objAddrs)
63
63
  {
64
- NodeID objId = AbstractState::getInternalID(addr);
64
+ NodeID objId = as.getIDFromAddr(addr);
65
65
  u32_t size = 0;
66
-
67
66
  if (svfir->getBaseObject(objId)->isConstantByteSize())
68
67
  {
69
68
  size = svfir->getBaseObject(objId)->getByteSizeOfObj();
@@ -342,22 +341,27 @@ IntervalValue BufOverflowDetector::getAccessOffset(SVF::AbstractState& as, SVF::
342
341
  * @param objAddrs The addresses of the base objects.
343
342
  * @param offset The interval value of the offset.
344
343
  */
345
- void BufOverflowDetector::updateGepObjOffsetFromBase(SVF::AddressValue gepAddrs, SVF::AddressValue objAddrs, SVF::IntervalValue offset)
344
+ void BufOverflowDetector::updateGepObjOffsetFromBase(AbstractState& as, SVF::AddressValue gepAddrs, SVF::AddressValue objAddrs, SVF::IntervalValue offset)
346
345
  {
347
346
  SVFIR* svfir = PAG::getPAG();
348
347
 
349
348
  for (const auto& objAddr : objAddrs)
350
349
  {
351
- NodeID objId = AbstractState::getInternalID(objAddr);
350
+ NodeID objId = as.getIDFromAddr(objAddr);
352
351
  auto obj = svfir->getGNode(objId);
353
352
  // if the object is a BaseObjVar, add the offset directly
354
353
  if (SVFUtil::isa<BaseObjVar>(obj))
355
354
  {
356
355
  for (const auto& gepAddr : gepAddrs)
357
356
  {
358
- NodeID gepObj = AbstractState::getInternalID(gepAddr);
359
- const GepObjVar* gepObjVar = SVFUtil::cast<GepObjVar>(svfir->getGNode(gepObj));
360
- addToGepObjOffsetFromBase(gepObjVar, offset);
357
+ NodeID gepObj = as.getIDFromAddr(gepAddr);
358
+ if (const GepObjVar* gepObjVar = SVFUtil::dyn_cast<GepObjVar>(svfir->getGNode(gepObj)))
359
+ {
360
+ addToGepObjOffsetFromBase(gepObjVar, offset);
361
+ }
362
+ else {
363
+ assert(AbstractState::isInvalidMem(gepAddr) && "GEP object is neither a GepObjVar nor an invalid memory address");
364
+ }
361
365
  }
362
366
  }
363
367
  else if (SVFUtil::isa<GepObjVar>(obj))
@@ -366,17 +370,25 @@ void BufOverflowDetector::updateGepObjOffsetFromBase(SVF::AddressValue gepAddrs,
366
370
  const GepObjVar* objVar = SVFUtil::cast<GepObjVar>(obj);
367
371
  for (const auto& gepAddr : gepAddrs)
368
372
  {
369
- NodeID gepObj = AbstractState::getInternalID(gepAddr);
370
- const GepObjVar* gepObjVar = SVFUtil::cast<GepObjVar>(svfir->getGNode(gepObj));
371
- if (hasGepObjOffsetFromBase(objVar))
373
+ NodeID gepObj = as.getIDFromAddr(gepAddr);
374
+ if (const GepObjVar* gepObjVar = SVFUtil::dyn_cast<GepObjVar>(svfir->getGNode(gepObj)))
372
375
  {
373
- IntervalValue objOffsetFromBase = getGepObjOffsetFromBase(objVar);
374
- if (!hasGepObjOffsetFromBase(gepObjVar))
375
- addToGepObjOffsetFromBase(gepObjVar, objOffsetFromBase + offset);
376
+ if (hasGepObjOffsetFromBase(objVar))
377
+ {
378
+ IntervalValue objOffsetFromBase =
379
+ getGepObjOffsetFromBase(objVar);
380
+ if (!hasGepObjOffsetFromBase(gepObjVar))
381
+ addToGepObjOffsetFromBase(
382
+ gepObjVar, objOffsetFromBase + offset);
383
+ }
384
+ else
385
+ {
386
+ assert(false &&
387
+ "GEP RHS object has no offset from base");
388
+ }
376
389
  }
377
- else
378
- {
379
- assert(false && "GEP RHS object has no offset from base");
390
+ else {
391
+ assert(AbstractState::isInvalidMem(gepAddr) && "GEP object is neither a GepObjVar nor an invalid memory address");
380
392
  }
381
393
  }
382
394
  }
@@ -460,9 +472,8 @@ bool BufOverflowDetector::canSafelyAccessMemory(AbstractState& as, const SVF::SV
460
472
  assert(as[value_id].isAddr());
461
473
  for (const auto& addr : as[value_id].getAddrs())
462
474
  {
463
- NodeID objId = AbstractState::getInternalID(addr);
475
+ NodeID objId = as.getIDFromAddr(addr);
464
476
  u32_t size = 0;
465
-
466
477
  // if the object is a constant size object, get the size directly
467
478
  if (svfir->getBaseObject(objId)->isConstantByteSize())
468
479
  {
@@ -487,11 +498,12 @@ bool BufOverflowDetector::canSafelyAccessMemory(AbstractState& as, const SVF::SV
487
498
  {
488
499
  offset = getGepObjOffsetFromBase(SVFUtil::cast<GepObjVar>(svfir->getGNode(objId))) + len;
489
500
  }
490
- else
501
+ else if (SVFUtil::isa<BaseObjVar>(svfir->getGNode(objId)))
491
502
  {
492
503
  // if the object is a BaseObjVar, get the offset directly
493
504
  offset = len;
494
505
  }
506
+
495
507
  // if the offset is greater than the size, return false
496
508
  if (offset.ub().getIntNumeral() >= size)
497
509
  {
@@ -500,3 +512,153 @@ bool BufOverflowDetector::canSafelyAccessMemory(AbstractState& as, const SVF::SV
500
512
  }
501
513
  return true;
502
514
  }
515
+
516
+ void NullptrDerefDetector::detect(AbstractState& as, const ICFGNode* node) {
517
+ if (SVFUtil::isa<CallICFGNode>(node)){
518
+ const CallICFGNode* callNode = SVFUtil::cast<CallICFGNode>(node);
519
+ if (SVFUtil::isExtCall(callNode->getCalledFunction()))
520
+ {
521
+ detectExtAPI(as, callNode);
522
+ }
523
+ }
524
+ else {
525
+ for (const auto& stmt: node->getSVFStmts()) {
526
+ if (const GepStmt* gep = SVFUtil::dyn_cast<GepStmt>(stmt)) {
527
+ SVFVar* rhs = gep->getRHSVar();
528
+ if (!canSafelyDerefPtr(as, rhs)) {
529
+ AEException bug(stmt->toString());
530
+ addBugToReporter(bug, stmt->getICFGNode());
531
+ }
532
+ }
533
+ else if (const LoadStmt* load = SVFUtil::dyn_cast<LoadStmt>(stmt)) {
534
+ SVFVar* lhs = load->getLHSVar();
535
+ if ( !canSafelyDerefPtr(as, lhs)) {
536
+ AEException bug(stmt->toString());
537
+ addBugToReporter(bug, stmt->getICFGNode());
538
+ }
539
+ }
540
+ }
541
+ }
542
+ }
543
+
544
+
545
+ void NullptrDerefDetector::handleStubFunctions(const CallICFGNode* callNode){
546
+ std::string funcName = callNode->getCalledFunction()->getName();
547
+ if (funcName == "UNSAFE_LOAD")
548
+ {
549
+ // void UNSAFE_LOAD(void* ptr);
550
+ AbstractInterpretation::getAEInstance().checkpoints.erase(callNode);
551
+ if (callNode->arg_size() < 1)
552
+ return;
553
+ AbstractState& as = AbstractInterpretation::getAEInstance().getAbsStateFromTrace(callNode);
554
+
555
+ const SVFVar* arg0Val = callNode->getArgument(0);
556
+ // opt may directly dereference a null pointer and call UNSAFE_LOAD(null)
557
+ bool isSafe = canSafelyDerefPtr(as, arg0Val) && arg0Val->getId() != 0;
558
+ if (!isSafe) {
559
+ std::cout << "detect null pointer deference success: " << callNode->toString() << std::endl;
560
+ return;
561
+ }
562
+ else
563
+ {
564
+ std::string err_msg = "this UNSAFE_LOAD should be a null pointer dereference but not detected. Pos: ";
565
+ err_msg += callNode->getSourceLoc();
566
+ std::cerr << err_msg << std::endl;
567
+ assert(false);
568
+ }
569
+ }
570
+ else if (funcName == "SAFE_LOAD")
571
+ {
572
+ // void SAFE_LOAD(void* ptr);
573
+ AbstractInterpretation::getAEInstance().checkpoints.erase(callNode);
574
+ if (callNode->arg_size() < 1) return;
575
+ AbstractState&as = AbstractInterpretation::getAEInstance().getAbsStateFromTrace(callNode);
576
+ const SVFVar* arg0Val = callNode->getArgument(0);
577
+ // opt may directly dereference a null pointer and call UNSAFE_LOAD(null)ols
578
+ bool isSafe = canSafelyDerefPtr(as, arg0Val) && arg0Val->getId() != 0;
579
+ if (isSafe) {
580
+ std::cout << "safe load pointer success: " << callNode->toString() << std::endl;
581
+ return;
582
+ }
583
+ else
584
+ {
585
+ std::string err_msg = "this SAFE_LOAD should be a safe but a null pointer dereference detected. Pos: ";
586
+ err_msg += callNode->getSourceLoc();
587
+ std::cerr << err_msg << std::endl;
588
+ assert(false);
589
+ }
590
+ }
591
+ }
592
+
593
+ void NullptrDerefDetector::detectExtAPI(AbstractState& as, const CallICFGNode* call) {
594
+ assert(call->getCalledFunction() && "FunObjVar* is nullptr");
595
+ // get ext type
596
+ // get argument index which are nullptr deref checkpoints for extapi
597
+ std::vector<u32_t> tmp_args;
598
+ for (const std::string &annotation: ExtAPI::getExtAPI()->getExtFuncAnnotations(call->getCalledFunction())){
599
+ if (annotation.find("MEMCPY") != std::string::npos)
600
+ {
601
+ if (call->arg_size() < 4) {
602
+ // for memcpy(void* dest, const void* src, size_t n)
603
+ tmp_args.push_back(0);
604
+ tmp_args.push_back(1);
605
+ }
606
+ else {
607
+ // for unsigned long iconv(void* cd, char **restrict inbuf, unsigned long *restrict inbytesleft, char **restrict outbuf, unsigned long *restrict outbytesleft)
608
+ tmp_args.push_back(1);
609
+ tmp_args.push_back(2);
610
+ tmp_args.push_back(3);
611
+ tmp_args.push_back(4);
612
+ }
613
+ }
614
+ else if (annotation.find("MEMSET") != std::string::npos)
615
+ {
616
+ // for memset(void* dest, elem, sz)
617
+ tmp_args.push_back(0);
618
+ }
619
+ else if (annotation.find("STRCPY") != std::string::npos)
620
+ {
621
+ // for strcpy(void* dest, void* src)
622
+ tmp_args.push_back(0);
623
+ tmp_args.push_back(1);
624
+ }
625
+ else if (annotation.find("STRCAT") != std::string::npos)
626
+ {
627
+ // for strcat(void* dest, const void* src)
628
+ // for strncat(void* dest, const void* src, size_t n)
629
+ tmp_args.push_back(0);
630
+ tmp_args.push_back(1);
631
+ }
632
+ }
633
+
634
+ for (const auto &arg: tmp_args) {
635
+ if (call->arg_size() <= arg)
636
+ continue;
637
+ const SVFVar* argVal = call->getArgument(arg);
638
+ if (argVal && !canSafelyDerefPtr(as, argVal)) {
639
+ AEException bug(call->toString());
640
+ addBugToReporter(bug, call);
641
+ }
642
+ }
643
+ }
644
+
645
+
646
+ bool NullptrDerefDetector::canSafelyDerefPtr(AbstractState& as, const SVFVar* value)
647
+ {
648
+ NodeID value_id = value->getId();
649
+ AbstractValue AbsVal = as[value_id];
650
+ if (isUninit(AbsVal)) return false;
651
+ if (!AbsVal.isAddr()) return true;
652
+ for (const auto &addr: AbsVal.getAddrs()) {
653
+ if (AbstractState::isInvalidMem(addr)) {
654
+ return false;
655
+ }
656
+ else if (AbstractState::isNullMem(addr))
657
+ return false;
658
+ else if (as.isFreedMem(addr))
659
+ return false;
660
+ }
661
+
662
+
663
+ return true;
664
+ }
@@ -162,7 +162,7 @@ void AbsExtAPI::initExtFunMap()
162
162
  AbstractValue Addrs = as[dst_id];
163
163
  for (auto vaddr: Addrs.getAddrs())
164
164
  {
165
- u32_t objId = AbstractState::getInternalID(vaddr);
165
+ u32_t objId = as.getIDFromAddr(vaddr);
166
166
  AbstractValue range = getRangeLimitFromType(svfir->getGNode(objId)->getType());
167
167
  as.store(vaddr, range);
168
168
  }
@@ -182,7 +182,7 @@ void AbsExtAPI::initExtFunMap()
182
182
  AbstractValue Addrs = as[dst_id];
183
183
  for (auto vaddr: Addrs.getAddrs())
184
184
  {
185
- u32_t objId = AbstractState::getInternalID(vaddr);
185
+ u32_t objId = as.getIDFromAddr(vaddr);
186
186
  AbstractValue range = getRangeLimitFromType(svfir->getGNode(objId)->getType());
187
187
  as.store(vaddr, range);
188
188
  }
@@ -279,7 +279,7 @@ void AbsExtAPI::initExtFunMap()
279
279
  u32_t dst_size = 0;
280
280
  for (const auto& addr : as[value_id].getAddrs())
281
281
  {
282
- NodeID objId = AbstractState::getInternalID(addr);
282
+ NodeID objId = as.getIDFromAddr(addr);
283
283
  if (svfir->getBaseObject(objId)->isConstantByteSize())
284
284
  {
285
285
  dst_size = svfir->getBaseObject(objId)->getByteSizeOfObj();
@@ -340,6 +340,32 @@ void AbsExtAPI::initExtFunMap()
340
340
  };
341
341
  func_map["recv"] = sse_recv;
342
342
  func_map["__recv"] = sse_recv;
343
+
344
+ auto sse_free = [&](const CallICFGNode *callNode)
345
+ {
346
+ if (callNode->arg_size() < 1) return;
347
+ AbstractState& as = getAbsStateFromTrace(callNode);
348
+ const u32_t freePtr = callNode->getArgument(0)->getId();
349
+ for (auto addr: as[freePtr].getAddrs()) {
350
+ if (AbstractState::isInvalidMem(addr)) {
351
+ // double free here.
352
+ } else
353
+ {
354
+ as.addToFreedAddrs(addr);
355
+ }
356
+ }
357
+ };
358
+ // Add all free-related functions to func_map
359
+ std::vector<std::string> freeFunctions = {
360
+ "VOS_MemFree", "cfree", "free", "free_all_mem", "freeaddrinfo",
361
+ "gcry_mpi_release", "gcry_sexp_release", "globfree", "nhfree",
362
+ "obstack_free", "safe_cfree", "safe_free", "safefree", "safexfree",
363
+ "sm_free", "vim_free", "xfree", "SSL_CTX_free", "SSL_free", "XFree"
364
+ };
365
+
366
+ for (const auto& name : freeFunctions) {
367
+ func_map[name] = sse_free;
368
+ }
343
369
  };
344
370
 
345
371
  AbstractState& AbsExtAPI::getAbsStateFromTrace(const SVF::ICFGNode* node)
@@ -473,7 +499,7 @@ IntervalValue AbsExtAPI::getStrlen(AbstractState& as, const SVF::SVFVar *strValu
473
499
  u32_t dst_size = 0;
474
500
  for (const auto& addr : as[value_id].getAddrs())
475
501
  {
476
- NodeID objId = AbstractState::getInternalID(addr);
502
+ NodeID objId = as.getIDFromAddr(addr);
477
503
  if (svfir->getBaseObject(objId)->isConstantByteSize())
478
504
  {
479
505
  dst_size = svfir->getBaseObject(objId)->getByteSizeOfObj();
@@ -621,7 +647,7 @@ void AbsExtAPI::handleMemcpy(AbstractState& as, const SVF::SVFVar *dst, const SV
621
647
  {
622
648
  for (const auto &src: expr_src.getAddrs())
623
649
  {
624
- u32_t objId = AbstractState::getInternalID(src);
650
+ u32_t objId = as.getIDFromAddr(src);
625
651
  if (as.inAddrToValTable(objId))
626
652
  {
627
653
  as.store(dst, as.load(src));
@@ -670,7 +696,7 @@ void AbsExtAPI::handleMemset(AbstractState& as, const SVF::SVFVar *dst, Interval
670
696
  AbstractValue lhs_gep = as.getGepObjAddrs(dstId, IntervalValue(index));
671
697
  for (const auto &addr: lhs_gep.getAddrs())
672
698
  {
673
- u32_t objId = AbstractState::getInternalID(addr);
699
+ u32_t objId = as.getIDFromAddr(addr);
674
700
  if (as.inAddrToValTable(objId))
675
701
  {
676
702
  AbstractValue tmp = as.load(addr);
@@ -274,7 +274,11 @@ bool AbstractInterpretation::isCmpBranchFeasible(const CmpStmt* cmpStmt, s64_t s
274
274
  NodeID op1 = cmpStmt->getOpVarID(1);
275
275
  NodeID res_id = cmpStmt->getResID();
276
276
  s32_t predicate = cmpStmt->getPredicate();
277
-
277
+ // if op0 or op1 is nullptr, no need to change value, just copy the state
278
+ if (op0 == IRGraph::NullPtr || op1 == IRGraph::NullPtr) {
279
+ as = new_es;
280
+ return true;
281
+ }
278
282
  // if op0 or op1 is undefined, return;
279
283
  // skip address compare
280
284
  if (new_es.inVarToAddrsTable(op0) || new_es.inVarToAddrsTable(op1))
@@ -387,7 +391,7 @@ bool AbstractInterpretation::isCmpBranchFeasible(const CmpStmt* cmpStmt, s64_t s
387
391
  // if lhs is register value, we should also change its mem obj
388
392
  for (const auto &addr: addrs)
389
393
  {
390
- NodeID objId = new_es.getInternalID(addr);
394
+ NodeID objId = new_es.getIDFromAddr(addr);
391
395
  if (new_es.inAddrToValTable(objId))
392
396
  {
393
397
  new_es.load(addr).meet_with(rhs);
@@ -409,7 +413,7 @@ bool AbstractInterpretation::isCmpBranchFeasible(const CmpStmt* cmpStmt, s64_t s
409
413
  // if lhs is register value, we should also change its mem obj
410
414
  for (const auto &addr: addrs)
411
415
  {
412
- NodeID objId = new_es.getInternalID(addr);
416
+ NodeID objId = new_es.getIDFromAddr(addr);
413
417
  if (new_es.inAddrToValTable(objId))
414
418
  {
415
419
  new_es.load(addr).meet_with(
@@ -427,7 +431,7 @@ bool AbstractInterpretation::isCmpBranchFeasible(const CmpStmt* cmpStmt, s64_t s
427
431
  // if lhs is register value, we should also change its mem obj
428
432
  for (const auto &addr: addrs)
429
433
  {
430
- NodeID objId = new_es.getInternalID(addr);
434
+ NodeID objId = new_es.getIDFromAddr(addr);
431
435
  if (new_es.inAddrToValTable(objId))
432
436
  {
433
437
  new_es.load(addr).meet_with(
@@ -446,7 +450,7 @@ bool AbstractInterpretation::isCmpBranchFeasible(const CmpStmt* cmpStmt, s64_t s
446
450
  // if lhs is register value, we should also change its mem obj
447
451
  for (const auto &addr: addrs)
448
452
  {
449
- NodeID objId = new_es.getInternalID(addr);
453
+ NodeID objId = new_es.getIDFromAddr(addr);
450
454
  if (new_es.inAddrToValTable(objId))
451
455
  {
452
456
  new_es.load(addr).meet_with(
@@ -465,7 +469,7 @@ bool AbstractInterpretation::isCmpBranchFeasible(const CmpStmt* cmpStmt, s64_t s
465
469
  // if lhs is register value, we should also change its mem obj
466
470
  for (const auto &addr: addrs)
467
471
  {
468
- NodeID objId = new_es.getInternalID(addr);
472
+ NodeID objId = new_es.getIDFromAddr(addr);
469
473
  if (new_es.inAddrToValTable(objId))
470
474
  {
471
475
  new_es.load(addr).meet_with(
@@ -517,7 +521,7 @@ bool AbstractInterpretation::isSwitchBranchFeasible(const SVFVar* var, s64_t suc
517
521
  AddressValue &addrs = new_es[load->getRHSVarID()].getAddrs();
518
522
  for (const auto &addr: addrs)
519
523
  {
520
- NodeID objId = new_es.getInternalID(addr);
524
+ NodeID objId = new_es.getIDFromAddr(addr);
521
525
  if (new_es.inAddrToValTable(objId))
522
526
  {
523
527
  new_es.load(addr).meet_with(switch_cond);
@@ -748,7 +752,7 @@ void AbstractInterpretation::indirectCallFunPass(const CallICFGNode *callNode)
748
752
  }
749
753
  AbstractValue Addrs = as[call_id];
750
754
  NodeID addr = *Addrs.getAddrs().begin();
751
- SVFVar *func_var = svfir->getGNode(AbstractState::getInternalID(addr));
755
+ SVFVar *func_var = svfir->getGNode(as.getIDFromAddr(addr));
752
756
 
753
757
  if(const FunObjVar* funObjVar = SVFUtil::dyn_cast<FunObjVar>(func_var))
754
758
  {
@@ -928,6 +932,9 @@ void AbstractInterpretation::handleSVFStatement(const SVFStmt *stmt)
928
932
  }
929
933
  else
930
934
  assert(false && "implement this part");
935
+ // NullPtr is index 0, it should not be changed
936
+ assert(!getAbsStateFromTrace(stmt->getICFGNode())[IRGraph::NullPtr].isInterval() &&
937
+ !getAbsStateFromTrace(stmt->getICFGNode())[IRGraph::NullPtr].isAddr());
931
938
  }
932
939
 
933
940
  void AbstractInterpretation::SkipRecursiveCall(const CallICFGNode *callNode)
@@ -1078,6 +1085,7 @@ void AbstractInterpretation::collectCheckPoint()
1078
1085
  // traverse every ICFGNode
1079
1086
  Set<std::string> ae_checkpoint_names = {"svf_assert"};
1080
1087
  Set<std::string> buf_checkpoint_names = {"UNSAFE_BUFACCESS", "SAFE_BUFACCESS"};
1088
+ Set<std::string> nullptr_checkpoint_names = {"UNSAFE_LOAD", "SAFE_LOAD"};
1081
1089
 
1082
1090
  for (auto it = svfir->getICFG()->begin(); it != svfir->getICFG()->end(); ++it)
1083
1091
  {
@@ -1099,6 +1107,14 @@ void AbstractInterpretation::collectCheckPoint()
1099
1107
  checkpoints.insert(call);
1100
1108
  }
1101
1109
  }
1110
+ if (Options::NullDerefCheck())
1111
+ {
1112
+ if (nullptr_checkpoint_names.find(fun->getName()) !=
1113
+ nullptr_checkpoint_names.end())
1114
+ {
1115
+ checkpoints.insert(call);
1116
+ }
1117
+ }
1102
1118
  }
1103
1119
  }
1104
1120
  }
@@ -1308,6 +1324,12 @@ void AbstractInterpretation::updateStateOnCmp(const CmpStmt *cmp)
1308
1324
  }
1309
1325
  as[res] = resVal;
1310
1326
  }
1327
+ // if op0 or op1 is nullptr, compare abstractValue instead of touching addr or interval
1328
+ else if (op0 == IRGraph::NullPtr || op1 == IRGraph::NullPtr) {
1329
+ u32_t res = cmp->getResID();
1330
+ IntervalValue resVal = (as[op0].equals(as[op1])) ? IntervalValue(1, 1) : IntervalValue(0, 0);
1331
+ as[res] = resVal;
1332
+ }
1311
1333
  else
1312
1334
  {
1313
1335
  if (!as.inVarToValTable(op0))
@@ -804,6 +804,8 @@ const Option<std::string> Options::OutputName(
804
804
  "output","output db file","output.db");
805
805
  const Option<bool> Options::BufferOverflowCheck(
806
806
  "overflow","Buffer Overflow Detection",false);
807
+ const Option<bool> Options::NullDerefCheck(
808
+ "null-deref","Null Pointer Dereference Detection",false);
807
809
  const Option<bool> Options::MemoryLeakCheck(
808
810
  "leak", "Memory Leak Detection",false);
809
811
  const Option<bool> Options::FileCheck(
@@ -885,6 +885,8 @@ int main(int argc, char** argv)
885
885
  AbstractInterpretation& ae = AbstractInterpretation::getAEInstance();
886
886
  if (Options::BufferOverflowCheck())
887
887
  ae.addDetector(std::make_unique<BufOverflowDetector>());
888
+ if (Options::NullDerefCheck())
889
+ ae.addDetector(std::make_unique<NullptrDerefDetector>());
888
890
  ae.runOnModule(pag->getICFG());
889
891
 
890
892
  AndersenWaveDiff::releaseAndersenWaveDiff();