svf-tools 1.0.978 → 1.0.980
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 +10 -8
- package/svf/include/AE/Svfexe/AEDetector.h +322 -0
- package/svf/include/AE/Svfexe/AbstractInterpretation.h +30 -66
- package/svf/include/Graphs/IRGraph.h +1 -1
- package/svf/include/Graphs/ThreadCallGraph.h +0 -2
- package/svf/include/MTA/MHP.h +0 -2
- package/svf/include/MTA/MTA.h +0 -3
- package/svf/include/SABER/SaberCheckerAPI.h +4 -20
- package/svf/include/Util/SVFUtil.h +1 -40
- package/svf/lib/AE/Core/AbstractState.cpp +56 -0
- package/svf/lib/AE/Svfexe/AEDetector.cpp +435 -0
- package/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +410 -398
- package/svf/lib/SABER/SaberCondAllocator.cpp +1 -1
- package/svf/lib/Util/SVFUtil.cpp +4 -4
- package/svf-llvm/tools/AE/ae.cpp +4 -17
- package/svf/include/AE/Svfexe/BufOverflowChecker.h +0 -216
- package/svf/include/AE/Svfexe/ICFGSimplification.h +0 -44
- package/svf/lib/AE/Svfexe/BufOverflowChecker.cpp +0 -829
- package/svf/lib/AE/Svfexe/ICFGSimplification.cpp +0 -173
|
@@ -203,7 +203,7 @@ inline CallSite getSVFCallSite(const SVFInstruction* inst)
|
|
|
203
203
|
/// Match arguments for callsite at caller and callee
|
|
204
204
|
/// if the arg size does not match then we do not need to connect this parameter
|
|
205
205
|
/// unless the callee is a variadic function (the first parameter of variadic function is its parameter number)
|
|
206
|
-
bool matchArgs(const
|
|
206
|
+
bool matchArgs(const CallSite cs, const SVFFunction* callee);
|
|
207
207
|
|
|
208
208
|
/// Return LLVM callsite given a value
|
|
209
209
|
inline CallSite getSVFCallSite(const SVFValue* value)
|
|
@@ -466,11 +466,6 @@ inline int getHeapAllocHoldingArgPosition(const CallSite cs)
|
|
|
466
466
|
{
|
|
467
467
|
return getHeapAllocHoldingArgPosition(getCallee(cs));
|
|
468
468
|
}
|
|
469
|
-
|
|
470
|
-
inline int getHeapAllocHoldingArgPosition(const SVFInstruction *inst)
|
|
471
|
-
{
|
|
472
|
-
return getHeapAllocHoldingArgPosition(getCallee(inst));
|
|
473
|
-
}
|
|
474
469
|
//@}
|
|
475
470
|
|
|
476
471
|
inline bool isReallocExtCall(const CallSite cs)
|
|
@@ -478,12 +473,6 @@ inline bool isReallocExtCall(const CallSite cs)
|
|
|
478
473
|
bool isPtrTy = cs.getInstruction()->getType()->isPointerTy();
|
|
479
474
|
return isPtrTy && isReallocExtFun(getCallee(cs));
|
|
480
475
|
}
|
|
481
|
-
|
|
482
|
-
inline bool isReallocExtCall(const SVFInstruction *inst)
|
|
483
|
-
{
|
|
484
|
-
bool isPtrTy = inst->getType()->isPointerTy();
|
|
485
|
-
return isPtrTy && isReallocExtFun(getCallee(inst));
|
|
486
|
-
}
|
|
487
476
|
//@}
|
|
488
477
|
|
|
489
478
|
/// Return true if this is a thread creation call
|
|
@@ -504,10 +493,6 @@ inline bool isThreadJoinCall(const CallSite cs)
|
|
|
504
493
|
{
|
|
505
494
|
return ThreadAPI::getThreadAPI()->isTDJoin(cs.getInstruction());
|
|
506
495
|
}
|
|
507
|
-
inline bool isThreadJoinCall(const SVFInstruction *inst)
|
|
508
|
-
{
|
|
509
|
-
return ThreadAPI::getThreadAPI()->isTDJoin(inst);
|
|
510
|
-
}
|
|
511
496
|
//@}
|
|
512
497
|
|
|
513
498
|
/// Return true if this is a thread exit call
|
|
@@ -516,10 +501,6 @@ inline bool isThreadExitCall(const CallSite cs)
|
|
|
516
501
|
{
|
|
517
502
|
return ThreadAPI::getThreadAPI()->isTDExit(cs.getInstruction());
|
|
518
503
|
}
|
|
519
|
-
inline bool isThreadExitCall(const SVFInstruction *inst)
|
|
520
|
-
{
|
|
521
|
-
return ThreadAPI::getThreadAPI()->isTDExit(inst);
|
|
522
|
-
}
|
|
523
504
|
//@}
|
|
524
505
|
|
|
525
506
|
/// Return true if this is a lock acquire call
|
|
@@ -528,10 +509,6 @@ inline bool isLockAquireCall(const CallSite cs)
|
|
|
528
509
|
{
|
|
529
510
|
return ThreadAPI::getThreadAPI()->isTDAcquire(cs.getInstruction());
|
|
530
511
|
}
|
|
531
|
-
inline bool isLockAquireCall(const SVFInstruction *inst)
|
|
532
|
-
{
|
|
533
|
-
return ThreadAPI::getThreadAPI()->isTDAcquire(inst);
|
|
534
|
-
}
|
|
535
512
|
//@}
|
|
536
513
|
|
|
537
514
|
/// Return true if this is a lock acquire call
|
|
@@ -540,10 +517,6 @@ inline bool isLockReleaseCall(const CallSite cs)
|
|
|
540
517
|
{
|
|
541
518
|
return ThreadAPI::getThreadAPI()->isTDRelease(cs.getInstruction());
|
|
542
519
|
}
|
|
543
|
-
inline bool isLockReleaseCall(const SVFInstruction *inst)
|
|
544
|
-
{
|
|
545
|
-
return ThreadAPI::getThreadAPI()->isTDRelease(inst);
|
|
546
|
-
}
|
|
547
520
|
//@}
|
|
548
521
|
|
|
549
522
|
/// Return true if this is a barrier wait call
|
|
@@ -552,10 +525,6 @@ inline bool isBarrierWaitCall(const CallSite cs)
|
|
|
552
525
|
{
|
|
553
526
|
return ThreadAPI::getThreadAPI()->isTDBarWait(cs.getInstruction());
|
|
554
527
|
}
|
|
555
|
-
inline bool isBarrierWaitCall(const SVFInstruction *inst)
|
|
556
|
-
{
|
|
557
|
-
return ThreadAPI::getThreadAPI()->isTDBarWait(inst);
|
|
558
|
-
}
|
|
559
528
|
//@}
|
|
560
529
|
|
|
561
530
|
/// Return sole argument of the thread routine
|
|
@@ -564,10 +533,6 @@ inline const SVFValue* getActualParmAtForkSite(const CallSite cs)
|
|
|
564
533
|
{
|
|
565
534
|
return ThreadAPI::getThreadAPI()->getActualParmAtForkSite(cs.getInstruction());
|
|
566
535
|
}
|
|
567
|
-
inline const SVFValue* getActualParmAtForkSite(const SVFInstruction *inst)
|
|
568
|
-
{
|
|
569
|
-
return ThreadAPI::getThreadAPI()->getActualParmAtForkSite(inst);
|
|
570
|
-
}
|
|
571
536
|
//@}
|
|
572
537
|
|
|
573
538
|
|
|
@@ -576,10 +541,6 @@ inline bool isProgExitCall(const CallSite cs)
|
|
|
576
541
|
return isProgExitFunction(getCallee(cs));
|
|
577
542
|
}
|
|
578
543
|
|
|
579
|
-
inline bool isProgExitCall(const SVFInstruction *inst)
|
|
580
|
-
{
|
|
581
|
-
return isProgExitFunction(getCallee(inst));
|
|
582
|
-
}
|
|
583
544
|
|
|
584
545
|
template<typename T>
|
|
585
546
|
constexpr typename std::remove_reference<T>::type &&
|
|
@@ -468,4 +468,60 @@ void AbstractState::printAbstractState() const
|
|
|
468
468
|
}
|
|
469
469
|
}
|
|
470
470
|
SVFUtil::outs() << "-----------------------------------------\n";
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const SVFType* AbstractState::getPointeeElement(NodeID id)
|
|
474
|
+
{
|
|
475
|
+
SVFIR* svfir = PAG::getPAG();
|
|
476
|
+
if (inVarToAddrsTable(id))
|
|
477
|
+
{
|
|
478
|
+
const AbstractValue& addrs = (*this)[id];
|
|
479
|
+
for (auto addr: addrs.getAddrs())
|
|
480
|
+
{
|
|
481
|
+
NodeID addr_id = AbstractState::getInternalID(addr);
|
|
482
|
+
if (addr_id == 0) // nullptr has no memobj, skip
|
|
483
|
+
continue;
|
|
484
|
+
return SVFUtil::dyn_cast<ObjVar>(svfir->getGNode(addr_id))->getMemObj()->getType();
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
else
|
|
488
|
+
{
|
|
489
|
+
// do nothing if no record in addrs table.
|
|
490
|
+
}
|
|
491
|
+
return nullptr;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
u32_t AbstractState::getAllocaInstByteSize(const AddrStmt *addr)
|
|
495
|
+
{
|
|
496
|
+
SVFIR* svfir = PAG::getPAG();
|
|
497
|
+
if (const ObjVar* objvar = SVFUtil::dyn_cast<ObjVar>(addr->getRHSVar()))
|
|
498
|
+
{
|
|
499
|
+
objvar->getType();
|
|
500
|
+
if (objvar->getMemObj()->isConstantByteSize())
|
|
501
|
+
{
|
|
502
|
+
u32_t sz = objvar->getMemObj()->getByteSizeOfObj();
|
|
503
|
+
return sz;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
else
|
|
507
|
+
{
|
|
508
|
+
const std::vector<SVFValue*>& sizes = addr->getArrSize();
|
|
509
|
+
// Default element size is set to 1.
|
|
510
|
+
u32_t elementSize = 1;
|
|
511
|
+
u64_t res = elementSize;
|
|
512
|
+
for (const SVFValue* value: sizes)
|
|
513
|
+
{
|
|
514
|
+
if (!inVarToValTable(svfir->getValueNode(value)))
|
|
515
|
+
{
|
|
516
|
+
(*this)[svfir->getValueNode(value)] = IntervalValue(Options::MaxFieldLimit());
|
|
517
|
+
}
|
|
518
|
+
IntervalValue itv =
|
|
519
|
+
(*this)[svfir->getValueNode(value)].getInterval();
|
|
520
|
+
res = res * itv.ub().getIntNumeral() > Options::MaxFieldLimit()? Options::MaxFieldLimit(): res * itv.ub().getIntNumeral();
|
|
521
|
+
}
|
|
522
|
+
return (u32_t)res;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
assert (false && "Addr rhs value is not ObjVar");
|
|
526
|
+
abort();
|
|
471
527
|
}
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
//===- AEDetector.cpp -- Vulnerability Detectors---------------------------------//
|
|
2
|
+
//
|
|
3
|
+
// SVF: Static Value-Flow Analysis
|
|
4
|
+
//
|
|
5
|
+
// Copyright (C) <2013-> <Yulei Sui>
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
// This program is free software: you can redistribute it and/or modify
|
|
9
|
+
// it under the terms of the GNU Affero General Public License as published by
|
|
10
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
11
|
+
// (at your option) any later version.
|
|
12
|
+
|
|
13
|
+
// This program is distributed in the hope that it will be useful,
|
|
14
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16
|
+
// GNU Affero General Public License for more details.
|
|
17
|
+
|
|
18
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
19
|
+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
20
|
+
//
|
|
21
|
+
//===----------------------------------------------------------------------===//
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
//
|
|
25
|
+
// Created by Jiawei Wang on 2024/8/20.
|
|
26
|
+
//
|
|
27
|
+
|
|
28
|
+
#include <AE/Svfexe/AEDetector.h>
|
|
29
|
+
#include <AE/Svfexe/AbstractInterpretation.h>
|
|
30
|
+
|
|
31
|
+
using namespace SVF;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @brief Detects buffer overflow issues within a given ICFG node.
|
|
35
|
+
*
|
|
36
|
+
* This function handles both non-call nodes, where it analyzes GEP (GetElementPtr)
|
|
37
|
+
* instructions for potential buffer overflows, and call nodes, where it checks
|
|
38
|
+
* for external API calls that may cause overflows.
|
|
39
|
+
*
|
|
40
|
+
* @param as Reference to the abstract state.
|
|
41
|
+
* @param node Pointer to the ICFG node.
|
|
42
|
+
*/
|
|
43
|
+
void BufOverflowDetector::detect(AbstractState& as, const ICFGNode* node)
|
|
44
|
+
{
|
|
45
|
+
if (!SVFUtil::isa<CallICFGNode>(node))
|
|
46
|
+
{
|
|
47
|
+
// Handle non-call nodes by analyzing GEP instructions
|
|
48
|
+
for (const SVFStmt* stmt : node->getSVFStmts())
|
|
49
|
+
{
|
|
50
|
+
if (const GepStmt* gep = SVFUtil::dyn_cast<GepStmt>(stmt))
|
|
51
|
+
{
|
|
52
|
+
SVFIR* svfir = PAG::getPAG();
|
|
53
|
+
NodeID lhs = gep->getLHSVarID();
|
|
54
|
+
NodeID rhs = gep->getRHSVarID();
|
|
55
|
+
|
|
56
|
+
// Update the GEP object offset from its base
|
|
57
|
+
updateGepObjOffsetFromBase(as[lhs].getAddrs(), as[rhs].getAddrs(), as.getByteOffset(gep));
|
|
58
|
+
|
|
59
|
+
IntervalValue baseObjSize = IntervalValue::bottom();
|
|
60
|
+
AddressValue objAddrs = as[gep->getRHSVarID()].getAddrs();
|
|
61
|
+
for (const auto& addr : objAddrs)
|
|
62
|
+
{
|
|
63
|
+
NodeID objId = AbstractState::getInternalID(addr);
|
|
64
|
+
u32_t size = 0;
|
|
65
|
+
|
|
66
|
+
if (svfir->getBaseObj(objId)->isConstantByteSize())
|
|
67
|
+
{
|
|
68
|
+
size = svfir->getBaseObj(objId)->getByteSizeOfObj();
|
|
69
|
+
}
|
|
70
|
+
else
|
|
71
|
+
{
|
|
72
|
+
const ICFGNode* addrNode = svfir->getICFG()->getICFGNode(SVFUtil::cast<SVFInstruction>(svfir->getBaseObj(objId)->getValue()));
|
|
73
|
+
for (const SVFStmt* stmt2 : addrNode->getSVFStmts())
|
|
74
|
+
{
|
|
75
|
+
if (const AddrStmt* addrStmt = SVFUtil::dyn_cast<AddrStmt>(stmt2))
|
|
76
|
+
{
|
|
77
|
+
size = as.getAllocaInstByteSize(addrStmt);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Calculate access offset and check for potential overflow
|
|
83
|
+
IntervalValue accessOffset = getAccessOffset(as, objId, gep);
|
|
84
|
+
if (accessOffset.ub().getIntNumeral() >= size)
|
|
85
|
+
{
|
|
86
|
+
AEException bug(stmt->toString());
|
|
87
|
+
addBugToReporter(bug, stmt->getICFGNode());
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else
|
|
94
|
+
{
|
|
95
|
+
// Handle call nodes by checking for external API calls
|
|
96
|
+
const CallICFGNode* callNode = SVFUtil::cast<CallICFGNode>(node);
|
|
97
|
+
const SVFFunction *callfun = SVFUtil::getCallee(callNode->getCallSite());
|
|
98
|
+
if (SVFUtil::isExtCall(callfun))
|
|
99
|
+
{
|
|
100
|
+
detectExtAPI(as, callNode);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @brief Initializes external API buffer overflow check rules.
|
|
107
|
+
*
|
|
108
|
+
* This function sets up rules for various memory-related functions like memcpy,
|
|
109
|
+
* memset, etc., defining which arguments should be checked for buffer overflows.
|
|
110
|
+
*/
|
|
111
|
+
void BufOverflowDetector::initExtAPIBufOverflowCheckRules()
|
|
112
|
+
{
|
|
113
|
+
extAPIBufOverflowCheckRules["llvm_memcpy_p0i8_p0i8_i64"] = {{0, 2}, {1, 2}};
|
|
114
|
+
extAPIBufOverflowCheckRules["llvm_memcpy_p0_p0_i64"] = {{0, 2}, {1, 2}};
|
|
115
|
+
extAPIBufOverflowCheckRules["llvm_memcpy_p0i8_p0i8_i32"] = {{0, 2}, {1, 2}};
|
|
116
|
+
extAPIBufOverflowCheckRules["llvm_memcpy"] = {{0, 2}, {1, 2}};
|
|
117
|
+
extAPIBufOverflowCheckRules["llvm_memmove"] = {{0, 2}, {1, 2}};
|
|
118
|
+
extAPIBufOverflowCheckRules["llvm_memmove_p0i8_p0i8_i64"] = {{0, 2}, {1, 2}};
|
|
119
|
+
extAPIBufOverflowCheckRules["llvm_memmove_p0_p0_i64"] = {{0, 2}, {1, 2}};
|
|
120
|
+
extAPIBufOverflowCheckRules["llvm_memmove_p0i8_p0i8_i32"] = {{0, 2}, {1, 2}};
|
|
121
|
+
extAPIBufOverflowCheckRules["__memcpy_chk"] = {{0, 2}, {1, 2}};
|
|
122
|
+
extAPIBufOverflowCheckRules["memmove"] = {{0, 2}, {1, 2}};
|
|
123
|
+
extAPIBufOverflowCheckRules["bcopy"] = {{0, 2}, {1, 2}};
|
|
124
|
+
extAPIBufOverflowCheckRules["memccpy"] = {{0, 3}, {1, 3}};
|
|
125
|
+
extAPIBufOverflowCheckRules["__memmove_chk"] = {{0, 2}, {1, 2}};
|
|
126
|
+
extAPIBufOverflowCheckRules["llvm_memset"] = {{0, 2}};
|
|
127
|
+
extAPIBufOverflowCheckRules["llvm_memset_p0i8_i32"] = {{0, 2}};
|
|
128
|
+
extAPIBufOverflowCheckRules["llvm_memset_p0i8_i64"] = {{0, 2}};
|
|
129
|
+
extAPIBufOverflowCheckRules["llvm_memset_p0_i64"] = {{0, 2}};
|
|
130
|
+
extAPIBufOverflowCheckRules["__memset_chk"] = {{0, 2}};
|
|
131
|
+
extAPIBufOverflowCheckRules["wmemset"] = {{0, 2}};
|
|
132
|
+
extAPIBufOverflowCheckRules["strncpy"] = {{0, 2}, {1, 2}};
|
|
133
|
+
extAPIBufOverflowCheckRules["iconv"] = {{1, 2}, {3, 4}};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @brief Handles external API calls related to buffer overflow detection.
|
|
138
|
+
*
|
|
139
|
+
* This function checks the type of external memory API (e.g., memcpy, memset, strcpy, strcat)
|
|
140
|
+
* and applies the corresponding buffer overflow checks based on predefined rules.
|
|
141
|
+
*
|
|
142
|
+
* @param as Reference to the abstract state.
|
|
143
|
+
* @param call Pointer to the call ICFG node.
|
|
144
|
+
*/
|
|
145
|
+
void BufOverflowDetector::detectExtAPI(AbstractState& as,
|
|
146
|
+
const CallICFGNode* call)
|
|
147
|
+
{
|
|
148
|
+
SVFIR* svfir = PAG::getPAG();
|
|
149
|
+
const SVFFunction *fun = SVFUtil::getCallee(call->getCallSite());
|
|
150
|
+
assert(fun && "SVFFunction* is nullptr");
|
|
151
|
+
CallSite cs = SVFUtil::getSVFCallSite(call->getCallSite());
|
|
152
|
+
|
|
153
|
+
AbstractInterpretation::ExtAPIType extType = AbstractInterpretation::UNCLASSIFIED;
|
|
154
|
+
|
|
155
|
+
// Determine the type of external memory API
|
|
156
|
+
for (const std::string &annotation : fun->getAnnotations())
|
|
157
|
+
{
|
|
158
|
+
if (annotation.find("MEMCPY") != std::string::npos)
|
|
159
|
+
extType = AbstractInterpretation::MEMCPY;
|
|
160
|
+
if (annotation.find("MEMSET") != std::string::npos)
|
|
161
|
+
extType = AbstractInterpretation::MEMSET;
|
|
162
|
+
if (annotation.find("STRCPY") != std::string::npos)
|
|
163
|
+
extType = AbstractInterpretation::STRCPY;
|
|
164
|
+
if (annotation.find("STRCAT") != std::string::npos)
|
|
165
|
+
extType = AbstractInterpretation::STRCAT;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Apply buffer overflow checks based on the determined API type
|
|
169
|
+
if (extType == AbstractInterpretation::MEMCPY)
|
|
170
|
+
{
|
|
171
|
+
if (extAPIBufOverflowCheckRules.count(fun->getName()) == 0)
|
|
172
|
+
{
|
|
173
|
+
SVFUtil::errs() << "Warning: " << fun->getName() << " is not in the rules, please implement it\n";
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
std::vector<std::pair<u32_t, u32_t>> args =
|
|
177
|
+
extAPIBufOverflowCheckRules.at(fun->getName());
|
|
178
|
+
for (auto arg : args)
|
|
179
|
+
{
|
|
180
|
+
IntervalValue offset = as[svfir->getValueNode(cs.getArgument(arg.second))].getInterval() - IntervalValue(1);
|
|
181
|
+
if (!canSafelyAccessMemory(as, cs.getArgument(arg.first), offset))
|
|
182
|
+
{
|
|
183
|
+
AEException bug(call->getCallSite()->toString());
|
|
184
|
+
addBugToReporter(bug, call);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
else if (extType == AbstractInterpretation::MEMSET)
|
|
189
|
+
{
|
|
190
|
+
if (extAPIBufOverflowCheckRules.count(fun->getName()) == 0)
|
|
191
|
+
{
|
|
192
|
+
SVFUtil::errs() << "Warning: " << fun->getName() << " is not in the rules, please implement it\n";
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
std::vector<std::pair<u32_t, u32_t>> args =
|
|
196
|
+
extAPIBufOverflowCheckRules.at(fun->getName());
|
|
197
|
+
for (auto arg : args)
|
|
198
|
+
{
|
|
199
|
+
IntervalValue offset = as[svfir->getValueNode(cs.getArgument(arg.second))].getInterval() - IntervalValue(1);
|
|
200
|
+
if (!canSafelyAccessMemory(as, cs.getArgument(arg.first), offset))
|
|
201
|
+
{
|
|
202
|
+
AEException bug(call->getCallSite()->toString());
|
|
203
|
+
addBugToReporter(bug, call);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
else if (extType == AbstractInterpretation::STRCPY)
|
|
208
|
+
{
|
|
209
|
+
if (!detectStrcpy(as, call))
|
|
210
|
+
{
|
|
211
|
+
AEException bug(call->getCallSite()->toString());
|
|
212
|
+
addBugToReporter(bug, call);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else if (extType == AbstractInterpretation::STRCAT)
|
|
216
|
+
{
|
|
217
|
+
if (!detectStrcat(as, call))
|
|
218
|
+
{
|
|
219
|
+
AEException bug(call->getCallSite()->toString());
|
|
220
|
+
addBugToReporter(bug, call);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
else
|
|
224
|
+
{
|
|
225
|
+
// Handle other cases
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* @brief Retrieves the access offset for a given object and GEP statement.
|
|
231
|
+
*
|
|
232
|
+
* This function calculates the access offset for a base object or a sub-object of an
|
|
233
|
+
* aggregate object (using GEP). If the object is a dummy object, it returns a top interval value.
|
|
234
|
+
*
|
|
235
|
+
* @param as Reference to the abstract state.
|
|
236
|
+
* @param objId The ID of the object.
|
|
237
|
+
* @param gep Pointer to the GEP statement.
|
|
238
|
+
* @return The interval value of the access offset.
|
|
239
|
+
*/
|
|
240
|
+
IntervalValue BufOverflowDetector::getAccessOffset(SVF::AbstractState& as, SVF::NodeID objId, const SVF::GepStmt* gep)
|
|
241
|
+
{
|
|
242
|
+
SVFIR* svfir = PAG::getPAG();
|
|
243
|
+
auto obj = svfir->getGNode(objId);
|
|
244
|
+
|
|
245
|
+
// if the object is a FIObjVar, return the byte offset directly
|
|
246
|
+
if (SVFUtil::isa<FIObjVar>(obj))
|
|
247
|
+
{
|
|
248
|
+
return as.getByteOffset(gep);
|
|
249
|
+
}
|
|
250
|
+
else if (SVFUtil::isa<GepObjVar>(obj))
|
|
251
|
+
{
|
|
252
|
+
// if the object is a GepObjVar, return the offset from the base object
|
|
253
|
+
return getGepObjOffsetFromBase(SVFUtil::cast<GepObjVar>(obj)) + as.getByteOffset(gep);
|
|
254
|
+
}
|
|
255
|
+
else
|
|
256
|
+
{
|
|
257
|
+
assert(SVFUtil::isa<DummyObjVar>(obj) && "Unknown object type");
|
|
258
|
+
return IntervalValue::top();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* @brief Updates the offset of a GEP object from its base.
|
|
264
|
+
*
|
|
265
|
+
* This function calculates and stores the offset of a GEP object from its base object
|
|
266
|
+
* using the addresses and offsets provided.
|
|
267
|
+
*
|
|
268
|
+
* @param gepAddrs The addresses of the GEP objects.
|
|
269
|
+
* @param objAddrs The addresses of the base objects.
|
|
270
|
+
* @param offset The interval value of the offset.
|
|
271
|
+
*/
|
|
272
|
+
void BufOverflowDetector::updateGepObjOffsetFromBase(SVF::AddressValue gepAddrs, SVF::AddressValue objAddrs, SVF::IntervalValue offset)
|
|
273
|
+
{
|
|
274
|
+
SVFIR* svfir = PAG::getPAG();
|
|
275
|
+
|
|
276
|
+
for (const auto& objAddr : objAddrs)
|
|
277
|
+
{
|
|
278
|
+
NodeID objId = AbstractState::getInternalID(objAddr);
|
|
279
|
+
auto obj = svfir->getGNode(objId);
|
|
280
|
+
// if the object is a FIObjVar, add the offset directly
|
|
281
|
+
if (SVFUtil::isa<FIObjVar>(obj))
|
|
282
|
+
{
|
|
283
|
+
for (const auto& gepAddr : gepAddrs)
|
|
284
|
+
{
|
|
285
|
+
NodeID gepObj = AbstractState::getInternalID(gepAddr);
|
|
286
|
+
const GepObjVar* gepObjVar = SVFUtil::cast<GepObjVar>(svfir->getGNode(gepObj));
|
|
287
|
+
addToGepObjOffsetFromBase(gepObjVar, offset);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
else if (SVFUtil::isa<GepObjVar>(obj))
|
|
291
|
+
{
|
|
292
|
+
// if the object is a GepObjVar, add the offset from the base object
|
|
293
|
+
const GepObjVar* objVar = SVFUtil::cast<GepObjVar>(obj);
|
|
294
|
+
for (const auto& gepAddr : gepAddrs)
|
|
295
|
+
{
|
|
296
|
+
NodeID gepObj = AbstractState::getInternalID(gepAddr);
|
|
297
|
+
const GepObjVar* gepObjVar = SVFUtil::cast<GepObjVar>(svfir->getGNode(gepObj));
|
|
298
|
+
if (hasGepObjOffsetFromBase(objVar))
|
|
299
|
+
{
|
|
300
|
+
IntervalValue objOffsetFromBase = getGepObjOffsetFromBase(objVar);
|
|
301
|
+
if (!hasGepObjOffsetFromBase(gepObjVar))
|
|
302
|
+
addToGepObjOffsetFromBase(gepObjVar, objOffsetFromBase + offset);
|
|
303
|
+
}
|
|
304
|
+
else
|
|
305
|
+
{
|
|
306
|
+
assert(false && "GEP RHS object has no offset from base");
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* @brief Detects buffer overflow in 'strcpy' function calls.
|
|
315
|
+
*
|
|
316
|
+
* This function checks if the destination buffer can safely accommodate the
|
|
317
|
+
* source string being copied, accounting for the null terminator.
|
|
318
|
+
*
|
|
319
|
+
* @param as Reference to the abstract state.
|
|
320
|
+
* @param call Pointer to the call ICFG node.
|
|
321
|
+
* @return True if the memory access is safe, false otherwise.
|
|
322
|
+
*/
|
|
323
|
+
bool BufOverflowDetector::detectStrcpy(AbstractState& as, const CallICFGNode *call)
|
|
324
|
+
{
|
|
325
|
+
CallSite cs = SVFUtil::getSVFCallSite(call->getCallSite());
|
|
326
|
+
const SVFValue* arg0Val = cs.getArgument(0);
|
|
327
|
+
const SVFValue* arg1Val = cs.getArgument(1);
|
|
328
|
+
IntervalValue strLen = AbstractInterpretation::getAEInstance().getStrlen(as, arg1Val);
|
|
329
|
+
return canSafelyAccessMemory(as, arg0Val, strLen);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* @brief Detects buffer overflow in 'strcat' function calls.
|
|
334
|
+
*
|
|
335
|
+
* This function checks if the destination buffer can safely accommodate both the
|
|
336
|
+
* existing string and the concatenated string from the source.
|
|
337
|
+
*
|
|
338
|
+
* @param as Reference to the abstract state.
|
|
339
|
+
* @param call Pointer to the call ICFG node.
|
|
340
|
+
* @return True if the memory access is safe, false otherwise.
|
|
341
|
+
*/
|
|
342
|
+
bool BufOverflowDetector::detectStrcat(AbstractState& as, const CallICFGNode *call)
|
|
343
|
+
{
|
|
344
|
+
SVFIR* svfir = PAG::getPAG();
|
|
345
|
+
const SVFFunction *fun = SVFUtil::getCallee(call->getCallSite());
|
|
346
|
+
|
|
347
|
+
const std::vector<std::string> strcatGroup = {"__strcat_chk", "strcat", "__wcscat_chk", "wcscat"};
|
|
348
|
+
const std::vector<std::string> strncatGroup = {"__strncat_chk", "strncat", "__wcsncat_chk", "wcsncat"};
|
|
349
|
+
|
|
350
|
+
if (std::find(strcatGroup.begin(), strcatGroup.end(), fun->getName()) != strcatGroup.end())
|
|
351
|
+
{
|
|
352
|
+
CallSite cs = SVFUtil::getSVFCallSite(call->getCallSite());
|
|
353
|
+
const SVFValue* arg0Val = cs.getArgument(0);
|
|
354
|
+
const SVFValue* arg1Val = cs.getArgument(1);
|
|
355
|
+
IntervalValue strLen0 = AbstractInterpretation::getAEInstance().getStrlen(as, arg0Val);
|
|
356
|
+
IntervalValue strLen1 = AbstractInterpretation::getAEInstance().getStrlen(as, arg1Val);
|
|
357
|
+
IntervalValue totalLen = strLen0 + strLen1;
|
|
358
|
+
return canSafelyAccessMemory(as, arg0Val, totalLen);
|
|
359
|
+
}
|
|
360
|
+
else if (std::find(strncatGroup.begin(), strncatGroup.end(), fun->getName()) != strncatGroup.end())
|
|
361
|
+
{
|
|
362
|
+
CallSite cs = SVFUtil::getSVFCallSite(call->getCallSite());
|
|
363
|
+
const SVFValue* arg0Val = cs.getArgument(0);
|
|
364
|
+
const SVFValue* arg2Val = cs.getArgument(2);
|
|
365
|
+
IntervalValue arg2Num = as[svfir->getValueNode(arg2Val)].getInterval();
|
|
366
|
+
IntervalValue strLen0 = AbstractInterpretation::getAEInstance().getStrlen(as, arg0Val);
|
|
367
|
+
IntervalValue totalLen = strLen0 + arg2Num;
|
|
368
|
+
return canSafelyAccessMemory(as, arg0Val, totalLen);
|
|
369
|
+
}
|
|
370
|
+
else
|
|
371
|
+
{
|
|
372
|
+
assert(false && "Unknown strcat function, please add it to strcatGroup or strncatGroup");
|
|
373
|
+
abort();
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* @brief Checks if a memory access is safe given a specific buffer length.
|
|
379
|
+
*
|
|
380
|
+
* This function ensures that a given memory access, starting at a specific value,
|
|
381
|
+
* does not exceed the allocated size of the buffer.
|
|
382
|
+
*
|
|
383
|
+
* @param as Reference to the abstract state.
|
|
384
|
+
* @param value Pointer to the SVF value.
|
|
385
|
+
* @param len The interval value representing the length of the memory access.
|
|
386
|
+
* @return True if the memory access is safe, false otherwise.
|
|
387
|
+
*/
|
|
388
|
+
bool BufOverflowDetector::canSafelyAccessMemory(AbstractState& as, const SVF::SVFValue* value, const SVF::IntervalValue& len)
|
|
389
|
+
{
|
|
390
|
+
SVFIR* svfir = PAG::getPAG();
|
|
391
|
+
NodeID value_id = svfir->getValueNode(value);
|
|
392
|
+
|
|
393
|
+
assert(as[value_id].isAddr());
|
|
394
|
+
for (const auto& addr : as[value_id].getAddrs())
|
|
395
|
+
{
|
|
396
|
+
NodeID objId = AbstractState::getInternalID(addr);
|
|
397
|
+
u32_t size = 0;
|
|
398
|
+
|
|
399
|
+
// if the object is a constant size object, get the size directly
|
|
400
|
+
if (svfir->getBaseObj(objId)->isConstantByteSize())
|
|
401
|
+
{
|
|
402
|
+
size = svfir->getBaseObj(objId)->getByteSizeOfObj();
|
|
403
|
+
}
|
|
404
|
+
else
|
|
405
|
+
{
|
|
406
|
+
// if the object is not a constant size object, get the size from the addrStmt
|
|
407
|
+
const ICFGNode* addrNode = svfir->getICFG()->getICFGNode(SVFUtil::cast<SVFInstruction>(svfir->getBaseObj(objId)->getValue()));
|
|
408
|
+
for (const SVFStmt* stmt2 : addrNode->getSVFStmts())
|
|
409
|
+
{
|
|
410
|
+
if (const AddrStmt* addrStmt = SVFUtil::dyn_cast<AddrStmt>(stmt2))
|
|
411
|
+
{
|
|
412
|
+
size = as.getAllocaInstByteSize(addrStmt);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
IntervalValue offset(0);
|
|
418
|
+
// if the object is a GepObjVar, get the offset from the base object
|
|
419
|
+
if (SVFUtil::isa<GepObjVar>(svfir->getGNode(objId)))
|
|
420
|
+
{
|
|
421
|
+
offset = getGepObjOffsetFromBase(SVFUtil::cast<GepObjVar>(svfir->getGNode(objId))) + len;
|
|
422
|
+
}
|
|
423
|
+
else
|
|
424
|
+
{
|
|
425
|
+
// if the object is a FIObjVar, get the offset directly
|
|
426
|
+
offset = len;
|
|
427
|
+
}
|
|
428
|
+
// if the offset is greater than the size, return false
|
|
429
|
+
if (offset.ub().getIntNumeral() >= size)
|
|
430
|
+
{
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return true;
|
|
435
|
+
}
|