svf-tools 1.0.1193 → 1.0.1195
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 +3 -3
- package/svf/include/AE/Core/AddressValue.h +2 -2
- package/svf/include/AE/Svfexe/AbsExtAPI.h +12 -32
- package/svf/include/AE/Svfexe/AbstractInterpretation.h +13 -3
- package/svf/lib/AE/Svfexe/AEDetector.cpp +20 -4
- package/svf/lib/AE/Svfexe/AbsExtAPI.cpp +153 -193
- package/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +189 -56
- package/svf-llvm/lib/CHGBuilder.cpp +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svf-tools",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1195",
|
|
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": {
|
|
@@ -114,7 +114,7 @@ public:
|
|
|
114
114
|
/// Return the internal index if addr is an address otherwise return the value of idx
|
|
115
115
|
inline u32_t getIDFromAddr(u32_t addr)
|
|
116
116
|
{
|
|
117
|
-
return _freedAddrs.count(addr) ? AddressValue::getInternalID(
|
|
117
|
+
return _freedAddrs.count(addr) ? AddressValue::getInternalID(BlackHoleObjAddr) : AddressValue::getInternalID(addr);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
AbstractState&operator=(const AbstractState&rhs)
|
|
@@ -187,9 +187,9 @@ public:
|
|
|
187
187
|
return addr == NullMemAddr;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
static inline bool
|
|
190
|
+
static inline bool isBlackHoleObjAddr(u32_t addr)
|
|
191
191
|
{
|
|
192
|
-
return addr ==
|
|
192
|
+
return addr == BlackHoleObjAddr;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
|
|
33
33
|
#define AddressMask 0x7f000000
|
|
34
34
|
#define FlippedAddressMask (AddressMask^0xffffffff)
|
|
35
|
-
// the address of
|
|
36
|
-
#define
|
|
35
|
+
// the address of BlackHole object, getVirtualMemAddress(2);
|
|
36
|
+
#define BlackHoleObjAddr 0x7f000000 + 2
|
|
37
37
|
// the address of NullMem, getVirtualMemAddress(0);
|
|
38
38
|
#define NullMemAddr 0x7f000000
|
|
39
39
|
|
|
@@ -74,43 +74,23 @@ public:
|
|
|
74
74
|
*/
|
|
75
75
|
void handleExtAPI(const CallICFGNode *call);
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
* @brief Handles the strcpy API call.
|
|
79
|
-
* @param call Pointer to the call ICFG node.
|
|
80
|
-
*/
|
|
81
|
-
void handleStrcpy(const CallICFGNode *call);
|
|
77
|
+
// --- Shared primitives used by string/memory handlers ---
|
|
82
78
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
79
|
+
/// Get the byte size of each element for a pointer/array variable.
|
|
80
|
+
u32_t getElementSize(AbstractState& as, const SVFVar* var);
|
|
81
|
+
|
|
82
|
+
/// Check if an interval length is usable (not bottom, not unbounded).
|
|
83
|
+
static bool isValidLength(const IntervalValue& len);
|
|
84
|
+
|
|
85
|
+
/// Calculate the length of a null-terminated string in abstract state.
|
|
89
86
|
IntervalValue getStrlen(AbstractState& as, const SVF::SVFVar *strValue);
|
|
90
87
|
|
|
91
|
-
|
|
92
|
-
* @brief Handles the strcat API call.
|
|
93
|
-
* @param call Pointer to the call ICFG node.
|
|
94
|
-
*/
|
|
95
|
-
void handleStrcat(const SVF::CallICFGNode *call);
|
|
88
|
+
// --- String/memory operation handlers ---
|
|
96
89
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
* @param dst Pointer to the destination SVF variable.
|
|
101
|
-
* @param src Pointer to the source SVF variable.
|
|
102
|
-
* @param len The interval value representing the length to copy.
|
|
103
|
-
* @param start_idx The starting index for copying.
|
|
104
|
-
*/
|
|
90
|
+
void handleStrcpy(const CallICFGNode *call);
|
|
91
|
+
void handleStrcat(const CallICFGNode *call);
|
|
92
|
+
void handleStrncat(const CallICFGNode *call);
|
|
105
93
|
void handleMemcpy(AbstractState& as, const SVF::SVFVar *dst, const SVF::SVFVar *src, IntervalValue len, u32_t start_idx);
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* @brief Handles the memset API call.
|
|
109
|
-
* @param as Reference to the abstract state.
|
|
110
|
-
* @param dst Pointer to the destination SVF variable.
|
|
111
|
-
* @param elem The interval value representing the element to set.
|
|
112
|
-
* @param len The interval value representing the length to set.
|
|
113
|
-
*/
|
|
114
94
|
void handleMemset(AbstractState& as, const SVFVar* dst, IntervalValue elem, IntervalValue len);
|
|
115
95
|
|
|
116
96
|
/**
|
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
#include "Util/SVFBugReport.h"
|
|
37
37
|
#include "Util/SVFStat.h"
|
|
38
38
|
#include "Graphs/SCC.h"
|
|
39
|
+
#include "Graphs/CallGraph.h"
|
|
40
|
+
#include <deque>
|
|
39
41
|
|
|
40
42
|
namespace SVF
|
|
41
43
|
{
|
|
@@ -144,6 +146,13 @@ public:
|
|
|
144
146
|
/// Program entry
|
|
145
147
|
void analyse();
|
|
146
148
|
|
|
149
|
+
/// Analyze all entry points (functions without callers)
|
|
150
|
+
void analyzeFromAllProgEntries();
|
|
151
|
+
|
|
152
|
+
/// Get all entry point functions (functions without callers)
|
|
153
|
+
std::deque<const FunObjVar*> collectProgEntryFuns();
|
|
154
|
+
|
|
155
|
+
|
|
147
156
|
static AbstractInterpretation& getAEInstance()
|
|
148
157
|
{
|
|
149
158
|
static AbstractInterpretation instance;
|
|
@@ -218,14 +227,14 @@ private:
|
|
|
218
227
|
*
|
|
219
228
|
* @param cycle WTOCycle which has weak topo order of basic blocks and nested cycles
|
|
220
229
|
*/
|
|
221
|
-
virtual void handleLoopOrRecursion(const ICFGCycleWTO* cycle);
|
|
230
|
+
virtual void handleLoopOrRecursion(const ICFGCycleWTO* cycle, const CallICFGNode* caller = nullptr);
|
|
222
231
|
|
|
223
232
|
/**
|
|
224
233
|
* Handle a function using worklist algorithm
|
|
225
234
|
*
|
|
226
235
|
* @param funEntry The entry node of the function to handle
|
|
227
236
|
*/
|
|
228
|
-
void handleFunction(const ICFGNode* funEntry);
|
|
237
|
+
void handleFunction(const ICFGNode* funEntry, const CallICFGNode* caller = nullptr);
|
|
229
238
|
|
|
230
239
|
/**
|
|
231
240
|
* Handle an ICFG node by merging states and processing statements
|
|
@@ -322,9 +331,9 @@ private:
|
|
|
322
331
|
AEAPI* api{nullptr};
|
|
323
332
|
|
|
324
333
|
ICFG* icfg;
|
|
334
|
+
CallGraph* callGraph;
|
|
325
335
|
AEStat* stat;
|
|
326
336
|
|
|
327
|
-
std::vector<const CallICFGNode*> callSiteStack;
|
|
328
337
|
Map<const FunObjVar*, const ICFGWTO*> funcToWTO;
|
|
329
338
|
Set<std::pair<const CallICFGNode*, NodeID>> nonRecursiveCallSites;
|
|
330
339
|
Set<const FunObjVar*> recursiveFuns;
|
|
@@ -358,6 +367,7 @@ private:
|
|
|
358
367
|
Map<std::string, std::function<void(const CallICFGNode*)>> func_map;
|
|
359
368
|
|
|
360
369
|
Map<const ICFGNode*, AbstractState> abstractTrace; // abstract states immediately after nodes
|
|
370
|
+
Set<const ICFGNode*> allAnalyzedNodes; // All nodes ever analyzed (across all entry points)
|
|
361
371
|
std::string moduleName;
|
|
362
372
|
|
|
363
373
|
std::vector<std::unique_ptr<AEDetector>> detectors;
|
|
@@ -367,7 +367,7 @@ void BufOverflowDetector::updateGepObjOffsetFromBase(AbstractState& as, SVF::Add
|
|
|
367
367
|
}
|
|
368
368
|
else
|
|
369
369
|
{
|
|
370
|
-
assert(AbstractState::
|
|
370
|
+
assert(AbstractState::isBlackHoleObjAddr(gepAddr) && "GEP object is neither a GepObjVar nor an invalid memory address");
|
|
371
371
|
}
|
|
372
372
|
}
|
|
373
373
|
}
|
|
@@ -398,7 +398,7 @@ void BufOverflowDetector::updateGepObjOffsetFromBase(AbstractState& as, SVF::Add
|
|
|
398
398
|
}
|
|
399
399
|
else
|
|
400
400
|
{
|
|
401
|
-
assert(AbstractState::
|
|
401
|
+
assert(AbstractState::isBlackHoleObjAddr(gepAddr) && "GEP object is neither a GepObjVar nor an invalid memory address");
|
|
402
402
|
}
|
|
403
403
|
}
|
|
404
404
|
}
|
|
@@ -479,7 +479,23 @@ bool BufOverflowDetector::canSafelyAccessMemory(AbstractState& as, const SVF::SV
|
|
|
479
479
|
SVFIR* svfir = PAG::getPAG();
|
|
480
480
|
NodeID value_id = value->getId();
|
|
481
481
|
|
|
482
|
-
|
|
482
|
+
// Lazy initialization for uninitialized pointer parameters in multi-entry analysis.
|
|
483
|
+
// When analyzing a function as an entry point (e.g., not called from main),
|
|
484
|
+
// pointer parameters may not have been initialized via AddrStmt.
|
|
485
|
+
//
|
|
486
|
+
// Example:
|
|
487
|
+
// void process_buffer(char* buf, int len) {
|
|
488
|
+
// buf[0] = 'a'; // accessing buf
|
|
489
|
+
// }
|
|
490
|
+
// When analyzing process_buffer as an entry point, 'buf' is a function parameter
|
|
491
|
+
// with no AddrStmt, so it has no address information in the abstract state.
|
|
492
|
+
// We lazily initialize it to point to the black hole object (BlkPtr), representing
|
|
493
|
+
// an unknown but valid memory location. This allows the analysis to continue
|
|
494
|
+
// while being conservatively sound.
|
|
495
|
+
if (!as[value_id].isAddr())
|
|
496
|
+
{
|
|
497
|
+
as[value_id] = AddressValue(BlackHoleObjAddr);
|
|
498
|
+
}
|
|
483
499
|
for (const auto& addr : as[value_id].getAddrs())
|
|
484
500
|
{
|
|
485
501
|
NodeID objId = as.getIDFromAddr(addr);
|
|
@@ -687,7 +703,7 @@ bool NullptrDerefDetector::canSafelyDerefPtr(AbstractState& as, const SVFVar* va
|
|
|
687
703
|
for (const auto &addr: AbsVal.getAddrs())
|
|
688
704
|
{
|
|
689
705
|
// if the addr itself is invalid mem, report unsafe
|
|
690
|
-
if (AbstractState::
|
|
706
|
+
if (AbstractState::isBlackHoleObjAddr(addr))
|
|
691
707
|
return false;
|
|
692
708
|
// if nullptr is detected, return unsafe
|
|
693
709
|
else if (AbstractState::isNullMem(addr))
|
|
@@ -272,60 +272,19 @@ void AbsExtAPI::initExtFunMap()
|
|
|
272
272
|
|
|
273
273
|
auto sse_strlen = [&](const CallICFGNode *callNode)
|
|
274
274
|
{
|
|
275
|
-
// check the arg size
|
|
276
275
|
if (callNode->arg_size() < 1) return;
|
|
277
|
-
const SVFVar* strValue = callNode->getArgument(0);
|
|
278
276
|
AbstractState& as = getAbsStateFromTrace(callNode);
|
|
279
|
-
NodeID value_id = strValue->getId();
|
|
280
277
|
u32_t lhsId = callNode->getRetICFGNode()->getActualRet()->getId();
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
else
|
|
290
|
-
{
|
|
291
|
-
const ICFGNode* addrNode = svfir->getBaseObject(objId)->getICFGNode();
|
|
292
|
-
for (const SVFStmt* stmt2: addrNode->getSVFStmts())
|
|
293
|
-
{
|
|
294
|
-
if (const AddrStmt* addrStmt = SVFUtil::dyn_cast<AddrStmt>(stmt2))
|
|
295
|
-
{
|
|
296
|
-
dst_size = as.getAllocaInstByteSize(addrStmt);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
u32_t len = 0;
|
|
302
|
-
NodeID dstid = strValue->getId();
|
|
303
|
-
if (as.inVarToAddrsTable(dstid))
|
|
304
|
-
{
|
|
305
|
-
for (u32_t index = 0; index < dst_size; index++)
|
|
306
|
-
{
|
|
307
|
-
AbstractValue expr0 =
|
|
308
|
-
as.getGepObjAddrs(dstid, IntervalValue(index));
|
|
309
|
-
AbstractValue val;
|
|
310
|
-
for (const auto &addr: expr0.getAddrs())
|
|
311
|
-
{
|
|
312
|
-
val.join_with(as.load(addr));
|
|
313
|
-
}
|
|
314
|
-
if (val.getInterval().is_numeral() && (char) val.getInterval().getIntNumeral() == '\0')
|
|
315
|
-
{
|
|
316
|
-
break;
|
|
317
|
-
}
|
|
318
|
-
++len;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
if (len == 0)
|
|
322
|
-
{
|
|
323
|
-
as[lhsId] = IntervalValue((s64_t)0, (s64_t)Options::MaxFieldLimit());
|
|
324
|
-
}
|
|
278
|
+
// strlen/wcslen return the number of characters (not bytes).
|
|
279
|
+
// getStrlen returns byte-scaled length (len * elemSize) for use
|
|
280
|
+
// by memcpy/strcpy. Here we need the raw character count, so
|
|
281
|
+
// divide back by elemSize.
|
|
282
|
+
IntervalValue byteLen = getStrlen(as, callNode->getArgument(0));
|
|
283
|
+
u32_t elemSize = getElementSize(as, callNode->getArgument(0));
|
|
284
|
+
if (byteLen.is_numeral() && elemSize > 1)
|
|
285
|
+
as[lhsId] = IntervalValue(byteLen.getIntNumeral() / (s64_t)elemSize);
|
|
325
286
|
else
|
|
326
|
-
|
|
327
|
-
as[lhsId] = IntervalValue(len);
|
|
328
|
-
}
|
|
287
|
+
as[lhsId] = byteLen;
|
|
329
288
|
};
|
|
330
289
|
func_map["strlen"] = sse_strlen;
|
|
331
290
|
func_map["wcslen"] = sse_strlen;
|
|
@@ -350,7 +309,7 @@ void AbsExtAPI::initExtFunMap()
|
|
|
350
309
|
const u32_t freePtr = callNode->getArgument(0)->getId();
|
|
351
310
|
for (auto addr: as[freePtr].getAddrs())
|
|
352
311
|
{
|
|
353
|
-
if (AbstractState::
|
|
312
|
+
if (AbstractState::isBlackHoleObjAddr(addr))
|
|
354
313
|
{
|
|
355
314
|
// Detected a double free — the address has already been freed.
|
|
356
315
|
// No action is taken at this point.
|
|
@@ -480,7 +439,13 @@ void AbsExtAPI::handleExtAPI(const CallICFGNode *call)
|
|
|
480
439
|
}
|
|
481
440
|
else if (extType == STRCAT)
|
|
482
441
|
{
|
|
483
|
-
|
|
442
|
+
// Both strcat and strncat are annotated as STRCAT.
|
|
443
|
+
// Distinguish by name: strncat/wcsncat contain "ncat".
|
|
444
|
+
const std::string& name = fun->getName();
|
|
445
|
+
if (name.find("ncat") != std::string::npos)
|
|
446
|
+
handleStrncat(call);
|
|
447
|
+
else
|
|
448
|
+
handleStrcat(call);
|
|
484
449
|
}
|
|
485
450
|
else
|
|
486
451
|
{
|
|
@@ -489,21 +454,50 @@ void AbsExtAPI::handleExtAPI(const CallICFGNode *call)
|
|
|
489
454
|
return;
|
|
490
455
|
}
|
|
491
456
|
|
|
492
|
-
|
|
457
|
+
// ===----------------------------------------------------------------------===//
|
|
458
|
+
// Shared primitives for string/memory handlers
|
|
459
|
+
// ===----------------------------------------------------------------------===//
|
|
460
|
+
|
|
461
|
+
/// Get the byte size of each element for a pointer/array variable.
|
|
462
|
+
/// Shared by handleMemcpy, handleMemset, and getStrlen to avoid duplication.
|
|
463
|
+
u32_t AbsExtAPI::getElementSize(AbstractState& as, const SVFVar* var)
|
|
493
464
|
{
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
465
|
+
if (var->getType()->isArrayTy())
|
|
466
|
+
{
|
|
467
|
+
return SVFUtil::dyn_cast<SVFArrayType>(var->getType())
|
|
468
|
+
->getTypeOfElement()->getByteSize();
|
|
469
|
+
}
|
|
470
|
+
if (var->getType()->isPointerTy())
|
|
471
|
+
{
|
|
472
|
+
if (const SVFType* elemType = as.getPointeeElement(var->getId()))
|
|
473
|
+
{
|
|
474
|
+
if (elemType->isArrayTy())
|
|
475
|
+
return SVFUtil::dyn_cast<SVFArrayType>(elemType)
|
|
476
|
+
->getTypeOfElement()->getByteSize();
|
|
477
|
+
return elemType->getByteSize();
|
|
478
|
+
}
|
|
479
|
+
return 1;
|
|
480
|
+
}
|
|
481
|
+
assert(false && "unsupported type for element size");
|
|
482
|
+
return 1;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/// Check if an interval length is usable for memory operations.
|
|
486
|
+
/// Returns false for bottom (no information) or unbounded lower bound
|
|
487
|
+
/// (cannot determine a concrete start for iteration).
|
|
488
|
+
bool AbsExtAPI::isValidLength(const IntervalValue& len)
|
|
489
|
+
{
|
|
490
|
+
return !len.isBottom() && !len.lb().is_minus_infinity();
|
|
502
491
|
}
|
|
503
492
|
|
|
493
|
+
/// Calculate the length of a null-terminated string in abstract state.
|
|
494
|
+
/// Scans memory from the base of strValue looking for a '\0' byte.
|
|
495
|
+
/// Returns an IntervalValue: exact length if '\0' found, otherwise [0, MaxFieldLimit].
|
|
504
496
|
IntervalValue AbsExtAPI::getStrlen(AbstractState& as, const SVF::SVFVar *strValue)
|
|
505
497
|
{
|
|
506
498
|
NodeID value_id = strValue->getId();
|
|
499
|
+
|
|
500
|
+
// Step 1: determine the buffer size (in bytes) backing this pointer
|
|
507
501
|
u32_t dst_size = 0;
|
|
508
502
|
for (const auto& addr : as[value_id].getAddrs())
|
|
509
503
|
{
|
|
@@ -524,8 +518,9 @@ IntervalValue AbsExtAPI::getStrlen(AbstractState& as, const SVF::SVFVar *strValu
|
|
|
524
518
|
}
|
|
525
519
|
}
|
|
526
520
|
}
|
|
521
|
+
|
|
522
|
+
// Step 2: scan for '\0' terminator
|
|
527
523
|
u32_t len = 0;
|
|
528
|
-
u32_t elemSize = 1;
|
|
529
524
|
if (as.inVarToAddrsTable(value_id))
|
|
530
525
|
{
|
|
531
526
|
for (u32_t index = 0; index < dst_size; index++)
|
|
@@ -537,188 +532,153 @@ IntervalValue AbsExtAPI::getStrlen(AbstractState& as, const SVF::SVFVar *strValu
|
|
|
537
532
|
{
|
|
538
533
|
val.join_with(as.load(addr));
|
|
539
534
|
}
|
|
540
|
-
if (val.getInterval().is_numeral() &&
|
|
535
|
+
if (val.getInterval().is_numeral() &&
|
|
536
|
+
(char) val.getInterval().getIntNumeral() == '\0')
|
|
541
537
|
{
|
|
542
538
|
break;
|
|
543
539
|
}
|
|
544
540
|
++len;
|
|
545
541
|
}
|
|
546
|
-
if (strValue->getType()->isArrayTy())
|
|
547
|
-
{
|
|
548
|
-
elemSize = SVFUtil::dyn_cast<SVFArrayType>(strValue->getType())->getTypeOfElement()->getByteSize();
|
|
549
|
-
}
|
|
550
|
-
else if (strValue->getType()->isPointerTy())
|
|
551
|
-
{
|
|
552
|
-
if (const SVFType* elemType = as.getPointeeElement(value_id))
|
|
553
|
-
{
|
|
554
|
-
if (elemType->isArrayTy())
|
|
555
|
-
elemSize = SVFUtil::dyn_cast<SVFArrayType>(elemType)->getTypeOfElement()->getByteSize();
|
|
556
|
-
else
|
|
557
|
-
elemSize = elemType->getByteSize();
|
|
558
|
-
}
|
|
559
|
-
else
|
|
560
|
-
{
|
|
561
|
-
elemSize = 1;
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
else
|
|
565
|
-
{
|
|
566
|
-
assert(false && "we cannot support this type");
|
|
567
|
-
}
|
|
568
542
|
}
|
|
543
|
+
|
|
544
|
+
// Step 3: scale by element size and return
|
|
545
|
+
u32_t elemSize = getElementSize(as, strValue);
|
|
569
546
|
if (len == 0)
|
|
570
|
-
{
|
|
571
547
|
return IntervalValue((s64_t)0, (s64_t)Options::MaxFieldLimit());
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
548
|
+
return IntervalValue(len * elemSize);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// ===----------------------------------------------------------------------===//
|
|
552
|
+
// String/memory operation handlers
|
|
553
|
+
// ===----------------------------------------------------------------------===//
|
|
554
|
+
|
|
555
|
+
/// strcpy(dst, src): copy all of src (including '\0') into dst.
|
|
556
|
+
/// Covers: strcpy, __strcpy_chk, stpcpy, wcscpy, __wcscpy_chk
|
|
557
|
+
void AbsExtAPI::handleStrcpy(const CallICFGNode *call)
|
|
558
|
+
{
|
|
559
|
+
AbstractState& as = getAbsStateFromTrace(call);
|
|
560
|
+
const SVFVar* dst = call->getArgument(0);
|
|
561
|
+
const SVFVar* src = call->getArgument(1);
|
|
562
|
+
IntervalValue srcLen = getStrlen(as, src);
|
|
563
|
+
// no need to -1, since srcLen includes up to (but not past) '\0'
|
|
564
|
+
if (!isValidLength(srcLen)) return;
|
|
565
|
+
handleMemcpy(as, dst, src, srcLen, 0);
|
|
577
566
|
}
|
|
578
567
|
|
|
568
|
+
/// strcat(dst, src): append all of src after the end of dst.
|
|
569
|
+
/// Covers: strcat, __strcat_chk, wcscat, __wcscat_chk
|
|
570
|
+
void AbsExtAPI::handleStrcat(const CallICFGNode *call)
|
|
571
|
+
{
|
|
572
|
+
AbstractState& as = getAbsStateFromTrace(call);
|
|
573
|
+
const SVFVar* dst = call->getArgument(0);
|
|
574
|
+
const SVFVar* src = call->getArgument(1);
|
|
575
|
+
IntervalValue dstLen = getStrlen(as, dst);
|
|
576
|
+
IntervalValue srcLen = getStrlen(as, src);
|
|
577
|
+
if (!isValidLength(dstLen)) return;
|
|
578
|
+
handleMemcpy(as, dst, src, srcLen, dstLen.lb().getIntNumeral());
|
|
579
|
+
}
|
|
579
580
|
|
|
580
|
-
|
|
581
|
+
/// strncat(dst, src, n): append at most n bytes of src after the end of dst.
|
|
582
|
+
/// Covers: strncat, __strncat_chk, wcsncat, __wcsncat_chk
|
|
583
|
+
void AbsExtAPI::handleStrncat(const CallICFGNode *call)
|
|
581
584
|
{
|
|
582
|
-
// __strcat_chk, strcat, __wcscat_chk, wcscat, __strncat_chk, strncat, __wcsncat_chk, wcsncat
|
|
583
|
-
// to check it is strcat group or strncat group
|
|
584
585
|
AbstractState& as = getAbsStateFromTrace(call);
|
|
585
|
-
const
|
|
586
|
-
const
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
const SVFVar* arg1Val = call->getArgument(1);
|
|
592
|
-
IntervalValue strLen0 = getStrlen(as, arg0Val);
|
|
593
|
-
IntervalValue strLen1 = getStrlen(as, arg1Val);
|
|
594
|
-
IntervalValue totalLen = strLen0 + strLen1;
|
|
595
|
-
handleMemcpy(as, arg0Val, arg1Val, strLen1, strLen0.lb().getIntNumeral());
|
|
596
|
-
// do memcpy
|
|
597
|
-
}
|
|
598
|
-
else if (std::find(strncatGroup.begin(), strncatGroup.end(), fun->getName()) != strncatGroup.end())
|
|
599
|
-
{
|
|
600
|
-
const SVFVar* arg0Val = call->getArgument(0);
|
|
601
|
-
const SVFVar* arg1Val = call->getArgument(1);
|
|
602
|
-
const SVFVar* arg2Val = call->getArgument(2);
|
|
603
|
-
IntervalValue arg2Num = as[arg2Val->getId()].getInterval();
|
|
604
|
-
IntervalValue strLen0 = getStrlen(as, arg0Val);
|
|
605
|
-
IntervalValue totalLen = strLen0 + arg2Num;
|
|
606
|
-
handleMemcpy(as, arg0Val, arg1Val, arg2Num, strLen0.lb().getIntNumeral());
|
|
607
|
-
// do memcpy
|
|
608
|
-
}
|
|
609
|
-
else
|
|
610
|
-
{
|
|
611
|
-
assert(false && "unknown strcat function, please add it to strcatGroup or strncatGroup");
|
|
612
|
-
}
|
|
586
|
+
const SVFVar* dst = call->getArgument(0);
|
|
587
|
+
const SVFVar* src = call->getArgument(1);
|
|
588
|
+
IntervalValue n = as[call->getArgument(2)->getId()].getInterval();
|
|
589
|
+
IntervalValue dstLen = getStrlen(as, dst);
|
|
590
|
+
if (!isValidLength(dstLen)) return;
|
|
591
|
+
handleMemcpy(as, dst, src, n, dstLen.lb().getIntNumeral());
|
|
613
592
|
}
|
|
614
593
|
|
|
615
|
-
|
|
594
|
+
/// Core memcpy: copy `len` bytes from src to dst starting at dst[start_idx].
|
|
595
|
+
void AbsExtAPI::handleMemcpy(AbstractState& as, const SVF::SVFVar *dst,
|
|
596
|
+
const SVF::SVFVar *src, IntervalValue len,
|
|
597
|
+
u32_t start_idx)
|
|
616
598
|
{
|
|
617
|
-
|
|
599
|
+
if (!isValidLength(len)) return;
|
|
600
|
+
|
|
601
|
+
u32_t dstId = dst->getId();
|
|
618
602
|
u32_t srcId = src->getId();
|
|
619
|
-
u32_t elemSize =
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
elemSize = SVFUtil::dyn_cast<SVFArrayType>(dst->getType())->getTypeOfElement()->getByteSize();
|
|
623
|
-
}
|
|
624
|
-
// memcpy(i32*, i32*, 40)
|
|
625
|
-
else if (dst->getType()->isPointerTy())
|
|
626
|
-
{
|
|
627
|
-
if (const SVFType* elemType = as.getPointeeElement(dstId))
|
|
628
|
-
{
|
|
629
|
-
if (elemType->isArrayTy())
|
|
630
|
-
elemSize = SVFUtil::dyn_cast<SVFArrayType>(elemType)->getTypeOfElement()->getByteSize();
|
|
631
|
-
else
|
|
632
|
-
elemSize = elemType->getByteSize();
|
|
633
|
-
}
|
|
634
|
-
else
|
|
635
|
-
{
|
|
636
|
-
elemSize = 1;
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
else
|
|
640
|
-
{
|
|
641
|
-
assert(false && "we cannot support this type");
|
|
642
|
-
}
|
|
643
|
-
u32_t size = std::min((u32_t)Options::MaxFieldLimit(), (u32_t) len.lb().getIntNumeral());
|
|
603
|
+
u32_t elemSize = getElementSize(as, dst);
|
|
604
|
+
u32_t size = std::min((u32_t)Options::MaxFieldLimit(),
|
|
605
|
+
(u32_t)len.lb().getIntNumeral());
|
|
644
606
|
u32_t range_val = size / elemSize;
|
|
645
|
-
|
|
607
|
+
|
|
608
|
+
if (!as.inVarToAddrsTable(srcId) || !as.inVarToAddrsTable(dstId))
|
|
609
|
+
return;
|
|
610
|
+
|
|
611
|
+
for (u32_t index = 0; index < range_val; index++)
|
|
646
612
|
{
|
|
647
|
-
|
|
613
|
+
AbstractValue expr_src =
|
|
614
|
+
as.getGepObjAddrs(srcId, IntervalValue(index));
|
|
615
|
+
AbstractValue expr_dst =
|
|
616
|
+
as.getGepObjAddrs(dstId, IntervalValue(index + start_idx));
|
|
617
|
+
for (const auto &dstAddr: expr_dst.getAddrs())
|
|
648
618
|
{
|
|
649
|
-
|
|
650
|
-
AbstractValue expr_src =
|
|
651
|
-
as.getGepObjAddrs(srcId, IntervalValue(index));
|
|
652
|
-
AbstractValue expr_dst =
|
|
653
|
-
as.getGepObjAddrs(dstId, IntervalValue(index + start_idx));
|
|
654
|
-
for (const auto &dst: expr_dst.getAddrs())
|
|
619
|
+
for (const auto &srcAddr: expr_src.getAddrs())
|
|
655
620
|
{
|
|
656
|
-
|
|
621
|
+
u32_t objId = as.getIDFromAddr(srcAddr);
|
|
622
|
+
if (as.inAddrToValTable(objId) || as.inAddrToAddrsTable(objId))
|
|
657
623
|
{
|
|
658
|
-
|
|
659
|
-
if (as.inAddrToValTable(objId))
|
|
660
|
-
{
|
|
661
|
-
as.store(dst, as.load(src));
|
|
662
|
-
}
|
|
663
|
-
else if (as.inAddrToAddrsTable(objId))
|
|
664
|
-
{
|
|
665
|
-
as.store(dst, as.load(src));
|
|
666
|
-
}
|
|
624
|
+
as.store(dstAddr, as.load(srcAddr));
|
|
667
625
|
}
|
|
668
626
|
}
|
|
669
627
|
}
|
|
670
628
|
}
|
|
671
629
|
}
|
|
672
630
|
|
|
673
|
-
|
|
631
|
+
/// Core memset: fill dst with `elem` for `len` bytes.
|
|
632
|
+
/// Note: elemSize here uses the pointee type's full size (not array element size)
|
|
633
|
+
/// to match how LLVM memset/wmemset intrinsics measure `len`. For a pointer to
|
|
634
|
+
/// wchar_t[100], elemSize = sizeof(wchar_t[100]), so range_val reflects the
|
|
635
|
+
/// number of top-level GEP fields, not individual array elements.
|
|
636
|
+
void AbsExtAPI::handleMemset(AbstractState& as, const SVF::SVFVar *dst,
|
|
637
|
+
IntervalValue elem, IntervalValue len)
|
|
674
638
|
{
|
|
639
|
+
if (!isValidLength(len)) return;
|
|
640
|
+
|
|
675
641
|
u32_t dstId = dst->getId();
|
|
676
|
-
u32_t size = std::min((u32_t)Options::MaxFieldLimit(), (u32_t) len.lb().getIntNumeral());
|
|
677
642
|
u32_t elemSize = 1;
|
|
678
643
|
if (dst->getType()->isArrayTy())
|
|
679
644
|
{
|
|
680
|
-
elemSize = SVFUtil::dyn_cast<SVFArrayType>(dst->getType())
|
|
645
|
+
elemSize = SVFUtil::dyn_cast<SVFArrayType>(dst->getType())
|
|
646
|
+
->getTypeOfElement()->getByteSize();
|
|
681
647
|
}
|
|
682
648
|
else if (dst->getType()->isPointerTy())
|
|
683
649
|
{
|
|
684
650
|
if (const SVFType* elemType = as.getPointeeElement(dstId))
|
|
685
|
-
{
|
|
686
651
|
elemSize = elemType->getByteSize();
|
|
687
|
-
}
|
|
688
652
|
else
|
|
689
|
-
{
|
|
690
653
|
elemSize = 1;
|
|
691
|
-
}
|
|
692
654
|
}
|
|
693
655
|
else
|
|
694
656
|
{
|
|
695
|
-
assert(false && "
|
|
657
|
+
assert(false && "unsupported type for element size");
|
|
696
658
|
}
|
|
697
|
-
|
|
659
|
+
u32_t size = std::min((u32_t)Options::MaxFieldLimit(),
|
|
660
|
+
(u32_t)len.lb().getIntNumeral());
|
|
698
661
|
u32_t range_val = size / elemSize;
|
|
662
|
+
|
|
699
663
|
for (u32_t index = 0; index < range_val; index++)
|
|
700
664
|
{
|
|
701
|
-
|
|
702
|
-
|
|
665
|
+
if (!as.inVarToAddrsTable(dstId))
|
|
666
|
+
break;
|
|
667
|
+
AbstractValue lhs_gep = as.getGepObjAddrs(dstId, IntervalValue(index));
|
|
668
|
+
for (const auto &addr: lhs_gep.getAddrs())
|
|
703
669
|
{
|
|
704
|
-
|
|
705
|
-
|
|
670
|
+
u32_t objId = as.getIDFromAddr(addr);
|
|
671
|
+
if (as.inAddrToValTable(objId))
|
|
706
672
|
{
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
else
|
|
715
|
-
{
|
|
716
|
-
as.store(addr, elem);
|
|
717
|
-
}
|
|
673
|
+
AbstractValue tmp = as.load(addr);
|
|
674
|
+
tmp.join_with(elem);
|
|
675
|
+
as.store(addr, tmp);
|
|
676
|
+
}
|
|
677
|
+
else
|
|
678
|
+
{
|
|
679
|
+
as.store(addr, elem);
|
|
718
680
|
}
|
|
719
681
|
}
|
|
720
|
-
else
|
|
721
|
-
break;
|
|
722
682
|
}
|
|
723
683
|
}
|
|
724
684
|
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
#include "Graphs/CallGraph.h"
|
|
35
35
|
#include "WPA/Andersen.h"
|
|
36
36
|
#include <cmath>
|
|
37
|
+
#include <deque>
|
|
37
38
|
|
|
38
39
|
using namespace SVF;
|
|
39
40
|
using namespace SVFUtil;
|
|
@@ -62,6 +63,8 @@ void AbstractInterpretation::runOnModule(ICFG *_icfg)
|
|
|
62
63
|
|
|
63
64
|
AbstractInterpretation::AbstractInterpretation()
|
|
64
65
|
{
|
|
66
|
+
AndersenWaveDiff* ander = AndersenWaveDiff::createAndersenWaveDiff(svfir);
|
|
67
|
+
callGraph = ander->getCallGraph();
|
|
65
68
|
stat = new AEStat(this);
|
|
66
69
|
}
|
|
67
70
|
/// Destructor
|
|
@@ -95,11 +98,8 @@ void AbstractInterpretation::collectCycleHeads(const std::list<const ICFGWTOComp
|
|
|
95
98
|
void AbstractInterpretation::initWTO()
|
|
96
99
|
{
|
|
97
100
|
AndersenWaveDiff* ander = AndersenWaveDiff::createAndersenWaveDiff(svfir);
|
|
98
|
-
|
|
99
|
-
Andersen::CallGraphSCC* callGraphScc = ander->getCallGraphSCC();
|
|
101
|
+
CallGraphSCC* callGraphScc = ander->getCallGraphSCC();
|
|
100
102
|
callGraphScc->find();
|
|
101
|
-
CallGraph* callGraph = ander->getCallGraph();
|
|
102
|
-
|
|
103
103
|
// Iterate through the call graph
|
|
104
104
|
for (auto it = callGraph->begin(); it != callGraph->end(); it++)
|
|
105
105
|
{
|
|
@@ -162,33 +162,103 @@ void AbstractInterpretation::initWTO()
|
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
///
|
|
165
|
+
/// Collect entry point functions for analysis.
|
|
166
|
+
/// Entry points are functions without callers (no incoming edges in CallGraph).
|
|
167
|
+
/// Uses a deque to allow efficient insertion at front for prioritizing main()
|
|
168
|
+
std::deque<const FunObjVar*> AbstractInterpretation::collectProgEntryFuns()
|
|
169
|
+
{
|
|
170
|
+
std::deque<const FunObjVar*> entryFunctions;
|
|
171
|
+
|
|
172
|
+
for (auto it = callGraph->begin(); it != callGraph->end(); ++it)
|
|
173
|
+
{
|
|
174
|
+
const CallGraphNode* cgNode = it->second;
|
|
175
|
+
const FunObjVar* fun = cgNode->getFunction();
|
|
176
|
+
|
|
177
|
+
// Skip declarations
|
|
178
|
+
if (fun->isDeclaration())
|
|
179
|
+
continue;
|
|
180
|
+
|
|
181
|
+
// Entry points are functions without callers (no incoming edges)
|
|
182
|
+
if (cgNode->getInEdges().empty())
|
|
183
|
+
{
|
|
184
|
+
// If main exists, put it first for priority using deque's push_front
|
|
185
|
+
if (fun->getName() == "main")
|
|
186
|
+
{
|
|
187
|
+
entryFunctions.push_front(fun);
|
|
188
|
+
}
|
|
189
|
+
else
|
|
190
|
+
{
|
|
191
|
+
entryFunctions.push_back(fun);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return entryFunctions;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
/// Program entry - analyze from all entry points (multi-entry analysis is the default)
|
|
166
201
|
void AbstractInterpretation::analyse()
|
|
167
202
|
{
|
|
168
203
|
initWTO();
|
|
204
|
+
|
|
205
|
+
// Always use multi-entry analysis from all entry points
|
|
206
|
+
analyzeFromAllProgEntries();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/// Analyze all entry points (functions without callers) - for whole-program analysis.
|
|
210
|
+
/// Abstract state is shared across entry points so that functions analyzed from
|
|
211
|
+
/// earlier entries are not re-analyzed from scratch.
|
|
212
|
+
void AbstractInterpretation::analyzeFromAllProgEntries()
|
|
213
|
+
{
|
|
214
|
+
// Collect all entry point functions
|
|
215
|
+
std::deque<const FunObjVar*> entryFunctions = collectProgEntryFuns();
|
|
216
|
+
|
|
217
|
+
if (entryFunctions.empty())
|
|
218
|
+
{
|
|
219
|
+
assert(false && "No entry functions found for analysis");
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
169
222
|
// handle Global ICFGNode of SVFModule
|
|
170
223
|
handleGlobalNode();
|
|
171
|
-
|
|
172
|
-
icfg->getGlobalICFGNode())[PAG::getPAG()->getBlkPtr()] = IntervalValue::top();
|
|
173
|
-
if (const CallGraphNode* cgn = svfir->getCallGraph()->getCallGraphNode("main"))
|
|
224
|
+
for (const FunObjVar* entryFun : entryFunctions)
|
|
174
225
|
{
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
handleFunction(mainEntry);
|
|
226
|
+
const ICFGNode* funEntry = icfg->getFunEntryICFGNode(entryFun);
|
|
227
|
+
handleFunction(funEntry);
|
|
178
228
|
}
|
|
179
229
|
}
|
|
180
230
|
|
|
181
231
|
/// handle global node
|
|
232
|
+
/// Initializes the abstract state for the global ICFG node and processes all global statements.
|
|
233
|
+
/// This includes setting up the null pointer and black hole pointer (blkPtr).
|
|
234
|
+
/// BlkPtr is initialized to point to the BlackHole object, representing
|
|
235
|
+
/// an unknown memory location that cannot be statically resolved.
|
|
182
236
|
void AbstractInterpretation::handleGlobalNode()
|
|
183
237
|
{
|
|
184
238
|
const ICFGNode* node = icfg->getGlobalICFGNode();
|
|
185
239
|
abstractTrace[node] = AbstractState();
|
|
186
240
|
abstractTrace[node][IRGraph::NullPtr] = AddressValue();
|
|
241
|
+
|
|
187
242
|
// Global Node, we just need to handle addr, load, store, copy and gep
|
|
188
243
|
for (const SVFStmt *stmt: node->getSVFStmts())
|
|
189
244
|
{
|
|
190
245
|
handleSVFStatement(stmt);
|
|
191
246
|
}
|
|
247
|
+
|
|
248
|
+
// BlkPtr represents a pointer whose target is statically unknown (e.g., from
|
|
249
|
+
// int2ptr casts, external function returns, or unmodeled instructions like
|
|
250
|
+
// AtomicCmpXchg). It should be an address pointing to the BlackHole object
|
|
251
|
+
// (ID=2), NOT an interval top.
|
|
252
|
+
//
|
|
253
|
+
// History: this was originally set to IntervalValue::top() as a quick fix when
|
|
254
|
+
// the analysis crashed on programs containing uninitialized BlkPtr. However,
|
|
255
|
+
// BlkPtr is semantically a *pointer* (address domain), not a numeric value
|
|
256
|
+
// (interval domain). Setting it to interval top broke cross-domain consistency:
|
|
257
|
+
// the interval domain and address domain gave contradictory information for the
|
|
258
|
+
// same variable. The correct representation is an AddressValue containing the
|
|
259
|
+
// BlackHole virtual address, which means "points to unknown memory".
|
|
260
|
+
abstractTrace[node][PAG::getPAG()->getBlkPtr()] =
|
|
261
|
+
AddressValue(BlackHoleObjAddr);
|
|
192
262
|
}
|
|
193
263
|
|
|
194
264
|
/// get execution state by merging states of predecessor blocks
|
|
@@ -598,39 +668,22 @@ bool AbstractInterpretation::handleICFGNode(const ICFGNode* node)
|
|
|
598
668
|
if (hadPrevState)
|
|
599
669
|
prevState = abstractTrace[node];
|
|
600
670
|
|
|
601
|
-
// For function entry nodes, initialize state from
|
|
671
|
+
// For function entry nodes, initialize state from predecessors or global
|
|
602
672
|
bool isFunEntry = SVFUtil::isa<FunEntryICFGNode>(node);
|
|
603
673
|
if (isFunEntry)
|
|
604
674
|
{
|
|
605
675
|
// Try to merge from predecessors first (handles call edges)
|
|
606
676
|
if (!mergeStatesFromPredecessors(node))
|
|
607
677
|
{
|
|
608
|
-
// No predecessors with state -
|
|
609
|
-
|
|
678
|
+
// No predecessors with state - inherit from global node
|
|
679
|
+
const ICFGNode* globalNode = icfg->getGlobalICFGNode();
|
|
680
|
+
if (hasAbsStateFromTrace(globalNode))
|
|
610
681
|
{
|
|
611
|
-
|
|
612
|
-
const CallICFGNode* caller = callSiteStack.back();
|
|
613
|
-
if (hasAbsStateFromTrace(caller))
|
|
614
|
-
{
|
|
615
|
-
abstractTrace[node] = abstractTrace[caller];
|
|
616
|
-
}
|
|
617
|
-
else
|
|
618
|
-
{
|
|
619
|
-
abstractTrace[node] = AbstractState();
|
|
620
|
-
}
|
|
682
|
+
abstractTrace[node] = abstractTrace[globalNode];
|
|
621
683
|
}
|
|
622
684
|
else
|
|
623
685
|
{
|
|
624
|
-
|
|
625
|
-
const ICFGNode* globalNode = icfg->getGlobalICFGNode();
|
|
626
|
-
if (hasAbsStateFromTrace(globalNode))
|
|
627
|
-
{
|
|
628
|
-
abstractTrace[node] = abstractTrace[globalNode];
|
|
629
|
-
}
|
|
630
|
-
else
|
|
631
|
-
{
|
|
632
|
-
abstractTrace[node] = AbstractState();
|
|
633
|
-
}
|
|
686
|
+
abstractTrace[node] = AbstractState();
|
|
634
687
|
}
|
|
635
688
|
}
|
|
636
689
|
}
|
|
@@ -661,6 +714,9 @@ bool AbstractInterpretation::handleICFGNode(const ICFGNode* node)
|
|
|
661
714
|
detector->detect(getAbsStateFromTrace(node), node);
|
|
662
715
|
stat->countStateSize();
|
|
663
716
|
|
|
717
|
+
// Track this node as analyzed (for coverage statistics across all entry points)
|
|
718
|
+
allAnalyzedNodes.insert(node);
|
|
719
|
+
|
|
664
720
|
// Check if state changed (for fixpoint detection)
|
|
665
721
|
// For entry nodes on first visit, always return true to process successors
|
|
666
722
|
if (isFunEntry && !hadPrevState)
|
|
@@ -754,7 +810,7 @@ std::vector<const ICFGNode*> AbstractInterpretation::getNextNodesOfCycle(const I
|
|
|
754
810
|
* Handle a function using worklist algorithm
|
|
755
811
|
* This replaces the recursive WTO component handling with explicit worklist iteration
|
|
756
812
|
*/
|
|
757
|
-
void AbstractInterpretation::handleFunction(const ICFGNode* funEntry)
|
|
813
|
+
void AbstractInterpretation::handleFunction(const ICFGNode* funEntry, const CallICFGNode* caller)
|
|
758
814
|
{
|
|
759
815
|
FIFOWorkList<const ICFGNode*> worklist;
|
|
760
816
|
worklist.push(funEntry);
|
|
@@ -767,7 +823,7 @@ void AbstractInterpretation::handleFunction(const ICFGNode* funEntry)
|
|
|
767
823
|
if (cycleHeadToCycle.find(node) != cycleHeadToCycle.end())
|
|
768
824
|
{
|
|
769
825
|
const ICFGCycleWTO* cycle = cycleHeadToCycle[node];
|
|
770
|
-
handleLoopOrRecursion(cycle);
|
|
826
|
+
handleLoopOrRecursion(cycle, caller);
|
|
771
827
|
|
|
772
828
|
// Push nodes outside the cycle to the worklist
|
|
773
829
|
std::vector<const ICFGNode*> cycleNextNodes = getNextNodesOfCycle(cycle);
|
|
@@ -821,13 +877,11 @@ bool AbstractInterpretation::isExtCall(const CallICFGNode *callNode)
|
|
|
821
877
|
|
|
822
878
|
void AbstractInterpretation::handleExtCall(const CallICFGNode *callNode)
|
|
823
879
|
{
|
|
824
|
-
callSiteStack.push_back(callNode);
|
|
825
880
|
utils->handleExtAPI(callNode);
|
|
826
881
|
for (auto& detector : detectors)
|
|
827
882
|
{
|
|
828
883
|
detector->handleStubFunctions(callNode);
|
|
829
884
|
}
|
|
830
|
-
callSiteStack.pop_back();
|
|
831
885
|
}
|
|
832
886
|
|
|
833
887
|
/// Check if a function is recursive (part of a call graph SCC)
|
|
@@ -945,7 +999,15 @@ bool AbstractInterpretation::shouldApplyNarrowing(const FunObjVar* fun)
|
|
|
945
999
|
return false;
|
|
946
1000
|
}
|
|
947
1001
|
}
|
|
948
|
-
/// Handle direct or indirect call: get callee, process function body, set return state
|
|
1002
|
+
/// Handle direct or indirect call: get callee(s), process function body, set return state.
|
|
1003
|
+
///
|
|
1004
|
+
/// For direct calls, the callee is known statically.
|
|
1005
|
+
/// For indirect calls, the previous implementation resolved callees from the abstract
|
|
1006
|
+
/// state's address domain, which only picked the first address and missed other targets.
|
|
1007
|
+
/// Since the abstract state's address domain is not an over-approximation for function
|
|
1008
|
+
/// pointers (it may be uninitialized or incomplete), we now use Andersen's pointer
|
|
1009
|
+
/// analysis results from the pre-computed call graph, which soundly resolves all
|
|
1010
|
+
/// possible indirect call targets.
|
|
949
1011
|
void AbstractInterpretation::handleFunCall(const CallICFGNode *callNode)
|
|
950
1012
|
{
|
|
951
1013
|
AbstractState& as = getAbsStateFromTrace(callNode);
|
|
@@ -955,16 +1017,30 @@ void AbstractInterpretation::handleFunCall(const CallICFGNode *callNode)
|
|
|
955
1017
|
if (skipRecursiveCall(callNode))
|
|
956
1018
|
return;
|
|
957
1019
|
|
|
958
|
-
|
|
959
|
-
if (
|
|
1020
|
+
// Direct call: callee is known
|
|
1021
|
+
if (const FunObjVar* callee = callNode->getCalledFunction())
|
|
1022
|
+
{
|
|
1023
|
+
const ICFGNode* calleeEntry = icfg->getFunEntryICFGNode(callee);
|
|
1024
|
+
handleFunction(calleeEntry, callNode);
|
|
1025
|
+
const RetICFGNode* retNode = callNode->getRetICFGNode();
|
|
1026
|
+
abstractTrace[retNode] = abstractTrace[callNode];
|
|
960
1027
|
return;
|
|
1028
|
+
}
|
|
961
1029
|
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1030
|
+
// Indirect call: use Andersen's call graph to get all resolved callees.
|
|
1031
|
+
// The call graph was built during initWTO() by running Andersen's pointer analysis,
|
|
1032
|
+
// which over-approximates the set of possible targets for each indirect callsite.
|
|
1033
|
+
if (callGraph->hasIndCSCallees(callNode))
|
|
1034
|
+
{
|
|
1035
|
+
const auto& callees = callGraph->getIndCSCallees(callNode);
|
|
1036
|
+
for (const FunObjVar* callee : callees)
|
|
1037
|
+
{
|
|
1038
|
+
if (callee->isDeclaration())
|
|
1039
|
+
continue;
|
|
1040
|
+
const ICFGNode* calleeEntry = icfg->getFunEntryICFGNode(callee);
|
|
1041
|
+
handleFunction(calleeEntry, callNode);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
968
1044
|
const RetICFGNode* retNode = callNode->getRetICFGNode();
|
|
969
1045
|
abstractTrace[retNode] = abstractTrace[callNode];
|
|
970
1046
|
}
|
|
@@ -1009,7 +1085,7 @@ void AbstractInterpretation::handleFunCall(const CallICFGNode *callNode)
|
|
|
1009
1085
|
/// Example:
|
|
1010
1086
|
/// int factorial(int n) { return n <= 1 ? 1 : n * factorial(n-1); }
|
|
1011
1087
|
/// factorial(5) -> returns [10000, 10000] (precise after narrowing)
|
|
1012
|
-
void AbstractInterpretation::handleLoopOrRecursion(const ICFGCycleWTO* cycle)
|
|
1088
|
+
void AbstractInterpretation::handleLoopOrRecursion(const ICFGCycleWTO* cycle, const CallICFGNode* caller)
|
|
1013
1089
|
{
|
|
1014
1090
|
const ICFGNode* cycle_head = cycle->head()->getICFGNode();
|
|
1015
1091
|
|
|
@@ -1017,11 +1093,9 @@ void AbstractInterpretation::handleLoopOrRecursion(const ICFGCycleWTO* cycle)
|
|
|
1017
1093
|
// all stores and return value to TOP, maintaining original semantics
|
|
1018
1094
|
if (Options::HandleRecur() == TOP && isRecursiveFun(cycle_head->getFun()))
|
|
1019
1095
|
{
|
|
1020
|
-
|
|
1021
|
-
if (!callSiteStack.empty())
|
|
1096
|
+
if (caller)
|
|
1022
1097
|
{
|
|
1023
|
-
|
|
1024
|
-
recursiveCallPass(callNode);
|
|
1098
|
+
recursiveCallPass(caller);
|
|
1025
1099
|
}
|
|
1026
1100
|
return;
|
|
1027
1101
|
}
|
|
@@ -1084,7 +1158,7 @@ void AbstractInterpretation::handleLoopOrRecursion(const ICFGCycleWTO* cycle)
|
|
|
1084
1158
|
else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
|
|
1085
1159
|
{
|
|
1086
1160
|
// Handle nested cycle recursively
|
|
1087
|
-
handleLoopOrRecursion(subCycle);
|
|
1161
|
+
handleLoopOrRecursion(subCycle, caller);
|
|
1088
1162
|
}
|
|
1089
1163
|
}
|
|
1090
1164
|
}
|
|
@@ -1229,15 +1303,39 @@ void AEStat::finializeStat()
|
|
|
1229
1303
|
generalNumMap["ES_Loc_Addr_AVG_Num"] /= count;
|
|
1230
1304
|
}
|
|
1231
1305
|
generalNumMap["SVF_STMT_NUM"] = count;
|
|
1232
|
-
|
|
1306
|
+
|
|
1307
|
+
u32_t totalICFGNodes = _ae->svfir->getICFG()->nodeNum;
|
|
1308
|
+
generalNumMap["ICFG_Node_Num"] = totalICFGNodes;
|
|
1309
|
+
|
|
1310
|
+
// Calculate coverage: use allAnalyzedNodes which tracks all nodes across all entry points
|
|
1311
|
+
u32_t analyzedNodes = _ae->allAnalyzedNodes.size();
|
|
1312
|
+
generalNumMap["Analyzed_ICFG_Node_Num"] = analyzedNodes;
|
|
1313
|
+
|
|
1314
|
+
// Coverage percentage (stored as integer percentage * 100 for precision)
|
|
1315
|
+
if (totalICFGNodes > 0)
|
|
1316
|
+
{
|
|
1317
|
+
double coveragePercent = (double)analyzedNodes / (double)totalICFGNodes * 100.0;
|
|
1318
|
+
generalNumMap["ICFG_Coverage_Percent"] = (u32_t)(coveragePercent * 100); // Store as percentage * 100
|
|
1319
|
+
}
|
|
1320
|
+
else
|
|
1321
|
+
{
|
|
1322
|
+
generalNumMap["ICFG_Coverage_Percent"] = 0;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1233
1325
|
u32_t callSiteNum = 0;
|
|
1234
1326
|
u32_t extCallSiteNum = 0;
|
|
1235
1327
|
Set<const FunObjVar *> funs;
|
|
1328
|
+
Set<const FunObjVar *> analyzedFuns;
|
|
1236
1329
|
for (const auto &it: *_ae->svfir->getICFG())
|
|
1237
1330
|
{
|
|
1238
1331
|
if (it.second->getFun())
|
|
1239
1332
|
{
|
|
1240
1333
|
funs.insert(it.second->getFun());
|
|
1334
|
+
// Check if this node was analyzed (across all entry points)
|
|
1335
|
+
if (_ae->allAnalyzedNodes.find(it.second) != _ae->allAnalyzedNodes.end())
|
|
1336
|
+
{
|
|
1337
|
+
analyzedFuns.insert(it.second->getFun());
|
|
1338
|
+
}
|
|
1241
1339
|
}
|
|
1242
1340
|
if (const CallICFGNode *callNode = dyn_cast<CallICFGNode>(it.second))
|
|
1243
1341
|
{
|
|
@@ -1252,6 +1350,19 @@ void AEStat::finializeStat()
|
|
|
1252
1350
|
}
|
|
1253
1351
|
}
|
|
1254
1352
|
generalNumMap["Func_Num"] = funs.size();
|
|
1353
|
+
generalNumMap["Analyzed_Func_Num"] = analyzedFuns.size();
|
|
1354
|
+
|
|
1355
|
+
// Function coverage percentage
|
|
1356
|
+
if (funs.size() > 0)
|
|
1357
|
+
{
|
|
1358
|
+
double funcCoveragePercent = (double)analyzedFuns.size() / (double)funs.size() * 100.0;
|
|
1359
|
+
generalNumMap["Func_Coverage_Percent"] = (u32_t)(funcCoveragePercent * 100); // Store as percentage * 100
|
|
1360
|
+
}
|
|
1361
|
+
else
|
|
1362
|
+
{
|
|
1363
|
+
generalNumMap["Func_Coverage_Percent"] = 0;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1255
1366
|
generalNumMap["EXT_CallSite_Num"] = extCallSiteNum;
|
|
1256
1367
|
generalNumMap["NonEXT_CallSite_Num"] = callSiteNum;
|
|
1257
1368
|
timeStatMap["Total_Time(sec)"] = (double)(endTime - startTime) / TIMEINTERVAL;
|
|
@@ -1280,8 +1391,16 @@ void AEStat::performStat()
|
|
|
1280
1391
|
unsigned field_width = 30;
|
|
1281
1392
|
for (NUMStatMap::iterator it = generalNumMap.begin(), eit = generalNumMap.end(); it != eit; ++it)
|
|
1282
1393
|
{
|
|
1283
|
-
//
|
|
1284
|
-
|
|
1394
|
+
// Special handling for percentage fields (stored as percentage * 100)
|
|
1395
|
+
if (it->first == "ICFG_Coverage_Percent" || it->first == "Func_Coverage_Percent")
|
|
1396
|
+
{
|
|
1397
|
+
double percent = (double)it->second / 100.0;
|
|
1398
|
+
std::cout << std::setw(field_width) << it->first << std::fixed << std::setprecision(2) << percent << "%\n";
|
|
1399
|
+
}
|
|
1400
|
+
else
|
|
1401
|
+
{
|
|
1402
|
+
std::cout << std::setw(field_width) << it->first << it->second << "\n";
|
|
1403
|
+
}
|
|
1285
1404
|
}
|
|
1286
1405
|
SVFUtil::outs() << "-------------------------------------------------------\n";
|
|
1287
1406
|
for (TIMEStatMap::iterator it = timeStatMap.begin(), eit = timeStatMap.end(); it != eit; ++it)
|
|
@@ -1605,6 +1724,13 @@ void AbstractInterpretation::updateStateOnCmp(const CmpStmt *cmp)
|
|
|
1605
1724
|
case CmpStmt::FCMP_TRUE:
|
|
1606
1725
|
resVal = IntervalValue(1, 1);
|
|
1607
1726
|
break;
|
|
1727
|
+
case CmpStmt::FCMP_ORD:
|
|
1728
|
+
case CmpStmt::FCMP_UNO:
|
|
1729
|
+
// FCMP_ORD: true if both operands are not NaN
|
|
1730
|
+
// FCMP_UNO: true if either operand is NaN
|
|
1731
|
+
// Conservatively return [0, 1] since we don't track NaN
|
|
1732
|
+
resVal = IntervalValue(0, 1);
|
|
1733
|
+
break;
|
|
1608
1734
|
default:
|
|
1609
1735
|
assert(false && "undefined compare: ");
|
|
1610
1736
|
}
|
|
@@ -1719,6 +1845,13 @@ void AbstractInterpretation::updateStateOnCmp(const CmpStmt *cmp)
|
|
|
1719
1845
|
case CmpStmt::FCMP_TRUE:
|
|
1720
1846
|
resVal = IntervalValue(1, 1);
|
|
1721
1847
|
break;
|
|
1848
|
+
case CmpStmt::FCMP_ORD:
|
|
1849
|
+
case CmpStmt::FCMP_UNO:
|
|
1850
|
+
// FCMP_ORD: true if both operands are not NaN
|
|
1851
|
+
// FCMP_UNO: true if either operand is NaN
|
|
1852
|
+
// Conservatively return [0, 1] since we don't track NaN
|
|
1853
|
+
resVal = IntervalValue(0, 1);
|
|
1854
|
+
break;
|
|
1722
1855
|
default:
|
|
1723
1856
|
assert(false && "undefined compare: ");
|
|
1724
1857
|
}
|
|
@@ -173,7 +173,8 @@ void CHGBuilder::connectInheritEdgeViaCall(const Function* caller, const CallBas
|
|
|
173
173
|
{
|
|
174
174
|
if (cs->arg_size() < 1 || (cs->arg_size() < 2 && cs->paramHasAttr(0, llvm::Attribute::StructRet)))
|
|
175
175
|
return;
|
|
176
|
-
if(caller->arg_size() == 0)
|
|
176
|
+
if(caller->arg_size() == 0)
|
|
177
|
+
{
|
|
177
178
|
return;
|
|
178
179
|
}
|
|
179
180
|
const Value* csThisPtr = cppUtil::getVCallThisPtr(cs);
|