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 +1 -1
- package/svf/include/AE/Core/AbstractState.h +33 -15
- package/svf/include/AE/Core/AddressValue.h +15 -22
- package/svf/include/AE/Svfexe/AEDetector.h +114 -1
- package/svf/include/AE/Svfexe/AbstractInterpretation.h +20 -13
- package/svf/include/Util/Options.h +2 -0
- package/svf/lib/AE/Core/AbstractState.cpp +8 -2
- package/svf/lib/AE/Svfexe/AEDetector.cpp +183 -21
- package/svf/lib/AE/Svfexe/AbsExtAPI.cpp +32 -6
- package/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +30 -8
- package/svf/lib/Util/Options.cpp +2 -0
- package/svf-llvm/tools/AE/ae.cpp +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svf-tools",
|
|
3
|
-
"version": "1.0.
|
|
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
|
|
114
|
-
|
|
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
|
|
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
|
-
|
|
303
|
-
|
|
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 =
|
|
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
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
359
|
-
const GepObjVar* gepObjVar = SVFUtil::
|
|
360
|
-
|
|
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 =
|
|
370
|
-
const GepObjVar* gepObjVar = SVFUtil::
|
|
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
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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(
|
|
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))
|
package/svf/lib/Util/Options.cpp
CHANGED
|
@@ -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(
|
package/svf-llvm/tools/AE/ae.cpp
CHANGED
|
@@ -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();
|