svf-tools 1.0.1083 → 1.0.1085
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 +34 -15
- package/svf/include/AE/Core/AddressValue.h +15 -22
- package/svf/include/AE/Svfexe/AEDetector.h +115 -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 +202 -19
- package/svf/lib/AE/Svfexe/AbsExtAPI.cpp +37 -6
- package/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +32 -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.1085",
|
|
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,20 @@ 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
|
+
{
|
|
296
|
+
_freedAddrs.insert(addr);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
bool isFreedMem(u32_t addr) const
|
|
300
|
+
{
|
|
301
|
+
return _freedAddrs.find(addr) != _freedAddrs.end();
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
|
|
286
305
|
/**
|
|
287
306
|
* if this NodeID in SVFIR is a pointer, get the pointee type
|
|
288
307
|
* e.g arr = (int*) malloc(10*sizeof(int))
|
|
@@ -299,20 +318,19 @@ public:
|
|
|
299
318
|
inline void store(u32_t addr, const AbstractValue &val)
|
|
300
319
|
{
|
|
301
320
|
assert(isVirtualMemAddress(addr) && "not virtual address?");
|
|
302
|
-
|
|
303
|
-
|
|
321
|
+
u32_t objId = getIDFromAddr(addr);
|
|
322
|
+
if (isNullMem(addr)) return;
|
|
304
323
|
_addrToAbsVal[objId] = val;
|
|
305
324
|
}
|
|
306
325
|
|
|
307
326
|
inline virtual AbstractValue &load(u32_t addr)
|
|
308
327
|
{
|
|
309
328
|
assert(isVirtualMemAddress(addr) && "not virtual address?");
|
|
310
|
-
u32_t objId =
|
|
329
|
+
u32_t objId = getIDFromAddr(addr);
|
|
311
330
|
return _addrToAbsVal[objId];
|
|
312
331
|
|
|
313
332
|
}
|
|
314
333
|
|
|
315
|
-
|
|
316
334
|
void printAbstractState() const;
|
|
317
335
|
|
|
318
336
|
std::string toString() const
|
|
@@ -396,6 +414,7 @@ public:
|
|
|
396
414
|
{
|
|
397
415
|
_addrToAbsVal.clear();
|
|
398
416
|
_varToAbsVal.clear();
|
|
417
|
+
_freedAddrs.clear();
|
|
399
418
|
}
|
|
400
419
|
|
|
401
420
|
};
|
|
@@ -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,116 @@ 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
|
+
{
|
|
327
|
+
friend class AbstractInterpretation;
|
|
328
|
+
public:
|
|
329
|
+
NullptrDerefDetector()
|
|
330
|
+
{
|
|
331
|
+
kind = NULL_DEREF;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
~NullptrDerefDetector() = default;
|
|
335
|
+
|
|
336
|
+
static bool classof(const AEDetector* detector)
|
|
337
|
+
{
|
|
338
|
+
return detector->getKind() == AEDetector::NULL_DEREF;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* @brief Detects nullptr dereferences issues within a node.
|
|
343
|
+
* @param as Reference to the abstract state.
|
|
344
|
+
* @param node Pointer to the ICFG node.
|
|
345
|
+
*/
|
|
346
|
+
void detect(AbstractState& as, const ICFGNode* node);
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* @brief Handles external API calls related to nullptr dereferences.
|
|
350
|
+
* @param call Pointer to the call ICFG node.
|
|
351
|
+
*/
|
|
352
|
+
void handleStubFunctions(const CallICFGNode* call);
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* @brief Checks if an Abstract Value is uninitialized.
|
|
356
|
+
* @param v The Abstract Value to check.
|
|
357
|
+
* @return True if the value is uninitialized, false otherwise.
|
|
358
|
+
*/
|
|
359
|
+
bool isUninit(AbstractValue v)
|
|
360
|
+
{
|
|
361
|
+
bool is = v.getAddrs().isBottom() && v.getInterval().isBottom();
|
|
362
|
+
return is;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* @brief Adds a bug to the reporter based on an exception.
|
|
367
|
+
* @param e The exception that was thrown.
|
|
368
|
+
* @param node Pointer to the ICFG node where the bug was detected.
|
|
369
|
+
*/
|
|
370
|
+
void addBugToReporter(const AEException& e, const ICFGNode* node)
|
|
371
|
+
{
|
|
372
|
+
GenericBug::EventStack eventStack;
|
|
373
|
+
SVFBugEvent sourceInstEvent(SVFBugEvent::EventType::SourceInst, node);
|
|
374
|
+
eventStack.push_back(sourceInstEvent); // Add the source instruction event to the event stack
|
|
375
|
+
|
|
376
|
+
if (eventStack.empty())
|
|
377
|
+
{
|
|
378
|
+
return; // If the event stack is empty, return early
|
|
379
|
+
}
|
|
380
|
+
std::string loc = eventStack.back().getEventLoc(); // Get the location of the last event in the stack
|
|
381
|
+
|
|
382
|
+
// Check if the bug at this location has already been reported
|
|
383
|
+
if (bugLoc.find(loc) != bugLoc.end())
|
|
384
|
+
{
|
|
385
|
+
return; // If the bug location is already reported, return early
|
|
386
|
+
}
|
|
387
|
+
else
|
|
388
|
+
{
|
|
389
|
+
bugLoc.insert(loc); // Otherwise, mark this location as reported
|
|
390
|
+
}
|
|
391
|
+
recoder.addAbsExecBug(GenericBug::FULLNULLPTRDEREFERENCE, eventStack, 0, 0, 0, 0);
|
|
392
|
+
nodeToBugInfo[node] = e.what(); // Record the exception information for the node
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* @brief Reports all detected nullptr dereference bugs.
|
|
397
|
+
*/
|
|
398
|
+
void reportBug()
|
|
399
|
+
{
|
|
400
|
+
if (!nodeToBugInfo.empty())
|
|
401
|
+
{
|
|
402
|
+
std::cerr << "###################### Nullptr Dereference (" + std::to_string(nodeToBugInfo.size())
|
|
403
|
+
+ " found)######################\n";
|
|
404
|
+
std::cerr << "---------------------------------------------\n";
|
|
405
|
+
for (const auto& it : nodeToBugInfo)
|
|
406
|
+
{
|
|
407
|
+
std::cerr << it.second << "\n---------------------------------------------\n";
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* @brief Handle external API calls related to nullptr dereferences.
|
|
414
|
+
* @param as Reference to the abstract state.
|
|
415
|
+
* @param call Pointer to the call ICFG node.
|
|
416
|
+
*/
|
|
417
|
+
void detectExtAPI(AbstractState& as, const CallICFGNode* call);
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* @brief Check if an Abstract Value is NULL (or uninitialized).
|
|
422
|
+
*
|
|
423
|
+
* @param v An Abstract Value of loaded from an address in an Abstract State.
|
|
424
|
+
*/
|
|
425
|
+
bool isNull(AbstractValue v)
|
|
426
|
+
{
|
|
427
|
+
return !v.isAddr() && !v.isInterval();
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
bool canSafelyDerefPtr(AbstractState& as, const SVFVar* ptr);
|
|
431
|
+
|
|
432
|
+
private:
|
|
433
|
+
Set<std::string> bugLoc; ///< Set of locations where bugs have been reported.
|
|
434
|
+
SVFBugReport recoder; ///< Recorder for abstract execution bugs.
|
|
435
|
+
Map<const ICFGNode*, std::string> nodeToBugInfo; ///< Maps ICFG nodes to bug information.
|
|
436
|
+
};
|
|
323
437
|
}
|
|
@@ -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,28 @@ 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
|
+
{
|
|
364
|
+
assert(AbstractState::isInvalidMem(gepAddr) && "GEP object is neither a GepObjVar nor an invalid memory address");
|
|
365
|
+
}
|
|
361
366
|
}
|
|
362
367
|
}
|
|
363
368
|
else if (SVFUtil::isa<GepObjVar>(obj))
|
|
@@ -366,17 +371,26 @@ void BufOverflowDetector::updateGepObjOffsetFromBase(SVF::AddressValue gepAddrs,
|
|
|
366
371
|
const GepObjVar* objVar = SVFUtil::cast<GepObjVar>(obj);
|
|
367
372
|
for (const auto& gepAddr : gepAddrs)
|
|
368
373
|
{
|
|
369
|
-
NodeID gepObj =
|
|
370
|
-
const GepObjVar* gepObjVar = SVFUtil::
|
|
371
|
-
if (hasGepObjOffsetFromBase(objVar))
|
|
374
|
+
NodeID gepObj = as.getIDFromAddr(gepAddr);
|
|
375
|
+
if (const GepObjVar* gepObjVar = SVFUtil::dyn_cast<GepObjVar>(svfir->getGNode(gepObj)))
|
|
372
376
|
{
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
377
|
+
if (hasGepObjOffsetFromBase(objVar))
|
|
378
|
+
{
|
|
379
|
+
IntervalValue objOffsetFromBase =
|
|
380
|
+
getGepObjOffsetFromBase(objVar);
|
|
381
|
+
if (!hasGepObjOffsetFromBase(gepObjVar))
|
|
382
|
+
addToGepObjOffsetFromBase(
|
|
383
|
+
gepObjVar, objOffsetFromBase + offset);
|
|
384
|
+
}
|
|
385
|
+
else
|
|
386
|
+
{
|
|
387
|
+
assert(false &&
|
|
388
|
+
"GEP RHS object has no offset from base");
|
|
389
|
+
}
|
|
376
390
|
}
|
|
377
391
|
else
|
|
378
392
|
{
|
|
379
|
-
assert(
|
|
393
|
+
assert(AbstractState::isInvalidMem(gepAddr) && "GEP object is neither a GepObjVar nor an invalid memory address");
|
|
380
394
|
}
|
|
381
395
|
}
|
|
382
396
|
}
|
|
@@ -460,9 +474,8 @@ bool BufOverflowDetector::canSafelyAccessMemory(AbstractState& as, const SVF::SV
|
|
|
460
474
|
assert(as[value_id].isAddr());
|
|
461
475
|
for (const auto& addr : as[value_id].getAddrs())
|
|
462
476
|
{
|
|
463
|
-
NodeID objId =
|
|
477
|
+
NodeID objId = as.getIDFromAddr(addr);
|
|
464
478
|
u32_t size = 0;
|
|
465
|
-
|
|
466
479
|
// if the object is a constant size object, get the size directly
|
|
467
480
|
if (svfir->getBaseObject(objId)->isConstantByteSize())
|
|
468
481
|
{
|
|
@@ -487,11 +500,12 @@ bool BufOverflowDetector::canSafelyAccessMemory(AbstractState& as, const SVF::SV
|
|
|
487
500
|
{
|
|
488
501
|
offset = getGepObjOffsetFromBase(SVFUtil::cast<GepObjVar>(svfir->getGNode(objId))) + len;
|
|
489
502
|
}
|
|
490
|
-
else
|
|
503
|
+
else if (SVFUtil::isa<BaseObjVar>(svfir->getGNode(objId)))
|
|
491
504
|
{
|
|
492
505
|
// if the object is a BaseObjVar, get the offset directly
|
|
493
506
|
offset = len;
|
|
494
507
|
}
|
|
508
|
+
|
|
495
509
|
// if the offset is greater than the size, return false
|
|
496
510
|
if (offset.ub().getIntNumeral() >= size)
|
|
497
511
|
{
|
|
@@ -500,3 +514,172 @@ bool BufOverflowDetector::canSafelyAccessMemory(AbstractState& as, const SVF::SV
|
|
|
500
514
|
}
|
|
501
515
|
return true;
|
|
502
516
|
}
|
|
517
|
+
|
|
518
|
+
void NullptrDerefDetector::detect(AbstractState& as, const ICFGNode* node)
|
|
519
|
+
{
|
|
520
|
+
if (SVFUtil::isa<CallICFGNode>(node))
|
|
521
|
+
{
|
|
522
|
+
const CallICFGNode* callNode = SVFUtil::cast<CallICFGNode>(node);
|
|
523
|
+
if (SVFUtil::isExtCall(callNode->getCalledFunction()))
|
|
524
|
+
{
|
|
525
|
+
detectExtAPI(as, callNode);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
else
|
|
529
|
+
{
|
|
530
|
+
for (const auto& stmt: node->getSVFStmts())
|
|
531
|
+
{
|
|
532
|
+
if (const GepStmt* gep = SVFUtil::dyn_cast<GepStmt>(stmt))
|
|
533
|
+
{
|
|
534
|
+
SVFVar* rhs = gep->getRHSVar();
|
|
535
|
+
if (!canSafelyDerefPtr(as, rhs))
|
|
536
|
+
{
|
|
537
|
+
AEException bug(stmt->toString());
|
|
538
|
+
addBugToReporter(bug, stmt->getICFGNode());
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
else if (const LoadStmt* load = SVFUtil::dyn_cast<LoadStmt>(stmt))
|
|
542
|
+
{
|
|
543
|
+
SVFVar* lhs = load->getLHSVar();
|
|
544
|
+
if ( !canSafelyDerefPtr(as, lhs))
|
|
545
|
+
{
|
|
546
|
+
AEException bug(stmt->toString());
|
|
547
|
+
addBugToReporter(bug, stmt->getICFGNode());
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
void NullptrDerefDetector::handleStubFunctions(const CallICFGNode* callNode)
|
|
556
|
+
{
|
|
557
|
+
std::string funcName = callNode->getCalledFunction()->getName();
|
|
558
|
+
if (funcName == "UNSAFE_LOAD")
|
|
559
|
+
{
|
|
560
|
+
// void UNSAFE_LOAD(void* ptr);
|
|
561
|
+
AbstractInterpretation::getAEInstance().checkpoints.erase(callNode);
|
|
562
|
+
if (callNode->arg_size() < 1)
|
|
563
|
+
return;
|
|
564
|
+
AbstractState& as = AbstractInterpretation::getAEInstance().getAbsStateFromTrace(callNode);
|
|
565
|
+
|
|
566
|
+
const SVFVar* arg0Val = callNode->getArgument(0);
|
|
567
|
+
// opt may directly dereference a null pointer and call UNSAFE_LOAD(null)
|
|
568
|
+
bool isSafe = canSafelyDerefPtr(as, arg0Val) && arg0Val->getId() != 0;
|
|
569
|
+
if (!isSafe)
|
|
570
|
+
{
|
|
571
|
+
std::cout << "detect null pointer deference success: " << callNode->toString() << std::endl;
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
else
|
|
575
|
+
{
|
|
576
|
+
std::string err_msg = "this UNSAFE_LOAD should be a null pointer dereference but not detected. Pos: ";
|
|
577
|
+
err_msg += callNode->getSourceLoc();
|
|
578
|
+
std::cerr << err_msg << std::endl;
|
|
579
|
+
assert(false);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
else if (funcName == "SAFE_LOAD")
|
|
583
|
+
{
|
|
584
|
+
// void SAFE_LOAD(void* ptr);
|
|
585
|
+
AbstractInterpretation::getAEInstance().checkpoints.erase(callNode);
|
|
586
|
+
if (callNode->arg_size() < 1) return;
|
|
587
|
+
AbstractState&as = AbstractInterpretation::getAEInstance().getAbsStateFromTrace(callNode);
|
|
588
|
+
const SVFVar* arg0Val = callNode->getArgument(0);
|
|
589
|
+
// opt may directly dereference a null pointer and call UNSAFE_LOAD(null)ols
|
|
590
|
+
bool isSafe = canSafelyDerefPtr(as, arg0Val) && arg0Val->getId() != 0;
|
|
591
|
+
if (isSafe)
|
|
592
|
+
{
|
|
593
|
+
std::cout << "safe load pointer success: " << callNode->toString() << std::endl;
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
else
|
|
597
|
+
{
|
|
598
|
+
std::string err_msg = "this SAFE_LOAD should be a safe but a null pointer dereference detected. Pos: ";
|
|
599
|
+
err_msg += callNode->getSourceLoc();
|
|
600
|
+
std::cerr << err_msg << std::endl;
|
|
601
|
+
assert(false);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
void NullptrDerefDetector::detectExtAPI(AbstractState& as, const CallICFGNode* call)
|
|
607
|
+
{
|
|
608
|
+
assert(call->getCalledFunction() && "FunObjVar* is nullptr");
|
|
609
|
+
// get ext type
|
|
610
|
+
// get argument index which are nullptr deref checkpoints for extapi
|
|
611
|
+
std::vector<u32_t> tmp_args;
|
|
612
|
+
for (const std::string &annotation: ExtAPI::getExtAPI()->getExtFuncAnnotations(call->getCalledFunction()))
|
|
613
|
+
{
|
|
614
|
+
if (annotation.find("MEMCPY") != std::string::npos)
|
|
615
|
+
{
|
|
616
|
+
if (call->arg_size() < 4)
|
|
617
|
+
{
|
|
618
|
+
// for memcpy(void* dest, const void* src, size_t n)
|
|
619
|
+
tmp_args.push_back(0);
|
|
620
|
+
tmp_args.push_back(1);
|
|
621
|
+
}
|
|
622
|
+
else
|
|
623
|
+
{
|
|
624
|
+
// for unsigned long iconv(void* cd, char **restrict inbuf, unsigned long *restrict inbytesleft, char **restrict outbuf, unsigned long *restrict outbytesleft)
|
|
625
|
+
tmp_args.push_back(1);
|
|
626
|
+
tmp_args.push_back(2);
|
|
627
|
+
tmp_args.push_back(3);
|
|
628
|
+
tmp_args.push_back(4);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
else if (annotation.find("MEMSET") != std::string::npos)
|
|
632
|
+
{
|
|
633
|
+
// for memset(void* dest, elem, sz)
|
|
634
|
+
tmp_args.push_back(0);
|
|
635
|
+
}
|
|
636
|
+
else if (annotation.find("STRCPY") != std::string::npos)
|
|
637
|
+
{
|
|
638
|
+
// for strcpy(void* dest, void* src)
|
|
639
|
+
tmp_args.push_back(0);
|
|
640
|
+
tmp_args.push_back(1);
|
|
641
|
+
}
|
|
642
|
+
else if (annotation.find("STRCAT") != std::string::npos)
|
|
643
|
+
{
|
|
644
|
+
// for strcat(void* dest, const void* src)
|
|
645
|
+
// for strncat(void* dest, const void* src, size_t n)
|
|
646
|
+
tmp_args.push_back(0);
|
|
647
|
+
tmp_args.push_back(1);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
for (const auto &arg: tmp_args)
|
|
652
|
+
{
|
|
653
|
+
if (call->arg_size() <= arg)
|
|
654
|
+
continue;
|
|
655
|
+
const SVFVar* argVal = call->getArgument(arg);
|
|
656
|
+
if (argVal && !canSafelyDerefPtr(as, argVal))
|
|
657
|
+
{
|
|
658
|
+
AEException bug(call->toString());
|
|
659
|
+
addBugToReporter(bug, call);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
bool NullptrDerefDetector::canSafelyDerefPtr(AbstractState& as, const SVFVar* value)
|
|
666
|
+
{
|
|
667
|
+
NodeID value_id = value->getId();
|
|
668
|
+
AbstractValue AbsVal = as[value_id];
|
|
669
|
+
if (isUninit(AbsVal)) return false;
|
|
670
|
+
if (!AbsVal.isAddr()) return true;
|
|
671
|
+
for (const auto &addr: AbsVal.getAddrs())
|
|
672
|
+
{
|
|
673
|
+
if (AbstractState::isInvalidMem(addr))
|
|
674
|
+
{
|
|
675
|
+
return false;
|
|
676
|
+
}
|
|
677
|
+
else if (AbstractState::isNullMem(addr))
|
|
678
|
+
return false;
|
|
679
|
+
else if (as.isFreedMem(addr))
|
|
680
|
+
return false;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
return true;
|
|
685
|
+
}
|
|
@@ -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,37 @@ 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
|
+
{
|
|
351
|
+
if (AbstractState::isInvalidMem(addr))
|
|
352
|
+
{
|
|
353
|
+
// double free here.
|
|
354
|
+
}
|
|
355
|
+
else
|
|
356
|
+
{
|
|
357
|
+
as.addToFreedAddrs(addr);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
// Add all free-related functions to func_map
|
|
362
|
+
std::vector<std::string> freeFunctions =
|
|
363
|
+
{
|
|
364
|
+
"VOS_MemFree", "cfree", "free", "free_all_mem", "freeaddrinfo",
|
|
365
|
+
"gcry_mpi_release", "gcry_sexp_release", "globfree", "nhfree",
|
|
366
|
+
"obstack_free", "safe_cfree", "safe_free", "safefree", "safexfree",
|
|
367
|
+
"sm_free", "vim_free", "xfree", "SSL_CTX_free", "SSL_free", "XFree"
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
for (const auto& name : freeFunctions)
|
|
371
|
+
{
|
|
372
|
+
func_map[name] = sse_free;
|
|
373
|
+
}
|
|
343
374
|
};
|
|
344
375
|
|
|
345
376
|
AbstractState& AbsExtAPI::getAbsStateFromTrace(const SVF::ICFGNode* node)
|
|
@@ -473,7 +504,7 @@ IntervalValue AbsExtAPI::getStrlen(AbstractState& as, const SVF::SVFVar *strValu
|
|
|
473
504
|
u32_t dst_size = 0;
|
|
474
505
|
for (const auto& addr : as[value_id].getAddrs())
|
|
475
506
|
{
|
|
476
|
-
NodeID objId =
|
|
507
|
+
NodeID objId = as.getIDFromAddr(addr);
|
|
477
508
|
if (svfir->getBaseObject(objId)->isConstantByteSize())
|
|
478
509
|
{
|
|
479
510
|
dst_size = svfir->getBaseObject(objId)->getByteSizeOfObj();
|
|
@@ -621,7 +652,7 @@ void AbsExtAPI::handleMemcpy(AbstractState& as, const SVF::SVFVar *dst, const SV
|
|
|
621
652
|
{
|
|
622
653
|
for (const auto &src: expr_src.getAddrs())
|
|
623
654
|
{
|
|
624
|
-
u32_t objId =
|
|
655
|
+
u32_t objId = as.getIDFromAddr(src);
|
|
625
656
|
if (as.inAddrToValTable(objId))
|
|
626
657
|
{
|
|
627
658
|
as.store(dst, as.load(src));
|
|
@@ -670,7 +701,7 @@ void AbsExtAPI::handleMemset(AbstractState& as, const SVF::SVFVar *dst, Interval
|
|
|
670
701
|
AbstractValue lhs_gep = as.getGepObjAddrs(dstId, IntervalValue(index));
|
|
671
702
|
for (const auto &addr: lhs_gep.getAddrs())
|
|
672
703
|
{
|
|
673
|
-
u32_t objId =
|
|
704
|
+
u32_t objId = as.getIDFromAddr(addr);
|
|
674
705
|
if (as.inAddrToValTable(objId))
|
|
675
706
|
{
|
|
676
707
|
AbstractValue tmp = as.load(addr);
|
|
@@ -274,7 +274,12 @@ 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
|
+
{
|
|
280
|
+
as = new_es;
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
278
283
|
// if op0 or op1 is undefined, return;
|
|
279
284
|
// skip address compare
|
|
280
285
|
if (new_es.inVarToAddrsTable(op0) || new_es.inVarToAddrsTable(op1))
|
|
@@ -387,7 +392,7 @@ bool AbstractInterpretation::isCmpBranchFeasible(const CmpStmt* cmpStmt, s64_t s
|
|
|
387
392
|
// if lhs is register value, we should also change its mem obj
|
|
388
393
|
for (const auto &addr: addrs)
|
|
389
394
|
{
|
|
390
|
-
NodeID objId = new_es.
|
|
395
|
+
NodeID objId = new_es.getIDFromAddr(addr);
|
|
391
396
|
if (new_es.inAddrToValTable(objId))
|
|
392
397
|
{
|
|
393
398
|
new_es.load(addr).meet_with(rhs);
|
|
@@ -409,7 +414,7 @@ bool AbstractInterpretation::isCmpBranchFeasible(const CmpStmt* cmpStmt, s64_t s
|
|
|
409
414
|
// if lhs is register value, we should also change its mem obj
|
|
410
415
|
for (const auto &addr: addrs)
|
|
411
416
|
{
|
|
412
|
-
NodeID objId = new_es.
|
|
417
|
+
NodeID objId = new_es.getIDFromAddr(addr);
|
|
413
418
|
if (new_es.inAddrToValTable(objId))
|
|
414
419
|
{
|
|
415
420
|
new_es.load(addr).meet_with(
|
|
@@ -427,7 +432,7 @@ bool AbstractInterpretation::isCmpBranchFeasible(const CmpStmt* cmpStmt, s64_t s
|
|
|
427
432
|
// if lhs is register value, we should also change its mem obj
|
|
428
433
|
for (const auto &addr: addrs)
|
|
429
434
|
{
|
|
430
|
-
NodeID objId = new_es.
|
|
435
|
+
NodeID objId = new_es.getIDFromAddr(addr);
|
|
431
436
|
if (new_es.inAddrToValTable(objId))
|
|
432
437
|
{
|
|
433
438
|
new_es.load(addr).meet_with(
|
|
@@ -446,7 +451,7 @@ bool AbstractInterpretation::isCmpBranchFeasible(const CmpStmt* cmpStmt, s64_t s
|
|
|
446
451
|
// if lhs is register value, we should also change its mem obj
|
|
447
452
|
for (const auto &addr: addrs)
|
|
448
453
|
{
|
|
449
|
-
NodeID objId = new_es.
|
|
454
|
+
NodeID objId = new_es.getIDFromAddr(addr);
|
|
450
455
|
if (new_es.inAddrToValTable(objId))
|
|
451
456
|
{
|
|
452
457
|
new_es.load(addr).meet_with(
|
|
@@ -465,7 +470,7 @@ bool AbstractInterpretation::isCmpBranchFeasible(const CmpStmt* cmpStmt, s64_t s
|
|
|
465
470
|
// if lhs is register value, we should also change its mem obj
|
|
466
471
|
for (const auto &addr: addrs)
|
|
467
472
|
{
|
|
468
|
-
NodeID objId = new_es.
|
|
473
|
+
NodeID objId = new_es.getIDFromAddr(addr);
|
|
469
474
|
if (new_es.inAddrToValTable(objId))
|
|
470
475
|
{
|
|
471
476
|
new_es.load(addr).meet_with(
|
|
@@ -517,7 +522,7 @@ bool AbstractInterpretation::isSwitchBranchFeasible(const SVFVar* var, s64_t suc
|
|
|
517
522
|
AddressValue &addrs = new_es[load->getRHSVarID()].getAddrs();
|
|
518
523
|
for (const auto &addr: addrs)
|
|
519
524
|
{
|
|
520
|
-
NodeID objId = new_es.
|
|
525
|
+
NodeID objId = new_es.getIDFromAddr(addr);
|
|
521
526
|
if (new_es.inAddrToValTable(objId))
|
|
522
527
|
{
|
|
523
528
|
new_es.load(addr).meet_with(switch_cond);
|
|
@@ -748,7 +753,7 @@ void AbstractInterpretation::indirectCallFunPass(const CallICFGNode *callNode)
|
|
|
748
753
|
}
|
|
749
754
|
AbstractValue Addrs = as[call_id];
|
|
750
755
|
NodeID addr = *Addrs.getAddrs().begin();
|
|
751
|
-
SVFVar *func_var = svfir->getGNode(
|
|
756
|
+
SVFVar *func_var = svfir->getGNode(as.getIDFromAddr(addr));
|
|
752
757
|
|
|
753
758
|
if(const FunObjVar* funObjVar = SVFUtil::dyn_cast<FunObjVar>(func_var))
|
|
754
759
|
{
|
|
@@ -928,6 +933,9 @@ void AbstractInterpretation::handleSVFStatement(const SVFStmt *stmt)
|
|
|
928
933
|
}
|
|
929
934
|
else
|
|
930
935
|
assert(false && "implement this part");
|
|
936
|
+
// NullPtr is index 0, it should not be changed
|
|
937
|
+
assert(!getAbsStateFromTrace(stmt->getICFGNode())[IRGraph::NullPtr].isInterval() &&
|
|
938
|
+
!getAbsStateFromTrace(stmt->getICFGNode())[IRGraph::NullPtr].isAddr());
|
|
931
939
|
}
|
|
932
940
|
|
|
933
941
|
void AbstractInterpretation::SkipRecursiveCall(const CallICFGNode *callNode)
|
|
@@ -1078,6 +1086,7 @@ void AbstractInterpretation::collectCheckPoint()
|
|
|
1078
1086
|
// traverse every ICFGNode
|
|
1079
1087
|
Set<std::string> ae_checkpoint_names = {"svf_assert"};
|
|
1080
1088
|
Set<std::string> buf_checkpoint_names = {"UNSAFE_BUFACCESS", "SAFE_BUFACCESS"};
|
|
1089
|
+
Set<std::string> nullptr_checkpoint_names = {"UNSAFE_LOAD", "SAFE_LOAD"};
|
|
1081
1090
|
|
|
1082
1091
|
for (auto it = svfir->getICFG()->begin(); it != svfir->getICFG()->end(); ++it)
|
|
1083
1092
|
{
|
|
@@ -1099,6 +1108,14 @@ void AbstractInterpretation::collectCheckPoint()
|
|
|
1099
1108
|
checkpoints.insert(call);
|
|
1100
1109
|
}
|
|
1101
1110
|
}
|
|
1111
|
+
if (Options::NullDerefCheck())
|
|
1112
|
+
{
|
|
1113
|
+
if (nullptr_checkpoint_names.find(fun->getName()) !=
|
|
1114
|
+
nullptr_checkpoint_names.end())
|
|
1115
|
+
{
|
|
1116
|
+
checkpoints.insert(call);
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1102
1119
|
}
|
|
1103
1120
|
}
|
|
1104
1121
|
}
|
|
@@ -1308,6 +1325,13 @@ void AbstractInterpretation::updateStateOnCmp(const CmpStmt *cmp)
|
|
|
1308
1325
|
}
|
|
1309
1326
|
as[res] = resVal;
|
|
1310
1327
|
}
|
|
1328
|
+
// if op0 or op1 is nullptr, compare abstractValue instead of touching addr or interval
|
|
1329
|
+
else if (op0 == IRGraph::NullPtr || op1 == IRGraph::NullPtr)
|
|
1330
|
+
{
|
|
1331
|
+
u32_t res = cmp->getResID();
|
|
1332
|
+
IntervalValue resVal = (as[op0].equals(as[op1])) ? IntervalValue(1, 1) : IntervalValue(0, 0);
|
|
1333
|
+
as[res] = resVal;
|
|
1334
|
+
}
|
|
1311
1335
|
else
|
|
1312
1336
|
{
|
|
1313
1337
|
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();
|