Static Value-Flow Analysis
Loading...
Searching...
No Matches
AEDetector.cpp
Go to the documentation of this file.
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
29#include <AE/Svfexe/AbsExtAPI.h>
31
32using namespace SVF;
33
45{
46 if (!SVFUtil::isa<CallICFGNode>(node))
47 {
48 // Handle non-call nodes by analyzing GEP instructions
49 for (const SVFStmt* stmt : node->getSVFStmts())
50 {
51 if (const GepStmt* gep = SVFUtil::dyn_cast<GepStmt>(stmt))
52 {
53 SVFIR* svfir = PAG::getPAG();
54 NodeID lhs = gep->getLHSVarID();
55 NodeID rhs = gep->getRHSVarID();
56
57 // Update the GEP object offset from its base
58 updateGepObjOffsetFromBase(as[lhs].getAddrs(), as[rhs].getAddrs(), as.getByteOffset(gep));
59
61 AddressValue objAddrs = as[gep->getRHSVarID()].getAddrs();
62 for (const auto& addr : objAddrs)
63 {
65 u32_t size = 0;
66
67 if (svfir->getBaseObj(objId)->isConstantByteSize())
68 {
69 size = svfir->getBaseObj(objId)->getByteSizeOfObj();
70 }
71 else
72 {
73 const ICFGNode* addrNode = SVFUtil::cast<ICFGNode>(
74 svfir->getBaseObj(objId)->getGNode());
75 for (const SVFStmt* stmt2 : addrNode->getSVFStmts())
76 {
77 if (const AddrStmt* addrStmt = SVFUtil::dyn_cast<AddrStmt>(stmt2))
78 {
79 size = as.getAllocaInstByteSize(addrStmt);
80 }
81 }
82 }
83
84 // Calculate access offset and check for potential overflow
86 if (accessOffset.ub().getIntNumeral() >= size)
87 {
88 AEException bug(stmt->toString());
89 addBugToReporter(bug, stmt->getICFGNode());
90 }
91 }
92 }
93 }
94 }
95 else
96 {
97 // Handle call nodes by checking for external API calls
98 const CallICFGNode* callNode = SVFUtil::cast<CallICFGNode>(node);
99 if (SVFUtil::isExtCall(callNode->getCalledFunction()))
100 {
102 }
103 }
104}
105
106
115{
116 // get function name
117 std::string funcName = callNode->getCalledFunction()->getName();
118 if (funcName == "SAFE_BUFACCESS")
119 {
120 // void SAFE_BUFACCESS(void* data, int size);
122 if (callNode->arg_size() < 2)
123 return;
126 callNode);
127 u32_t size_id = callNode->getArgument(1)->getId();
128 IntervalValue val = as[size_id].getInterval();
129 if (val.isBottom())
130 {
131 val = IntervalValue(0);
132 assert(false && "SAFE_BUFACCESS size is bottom");
133 }
134 const SVFVar* arg0Val = callNode->getArgument(0);
136 if (isSafe)
137 {
138 std::cout << "safe buffer access success: " << callNode->toString()
139 << std::endl;
140 return;
141 }
142 else
143 {
144 std::string err_msg = "this SAFE_BUFACCESS should be a safe access but detected buffer overflow. Pos: ";
145 err_msg += callNode->getSourceLoc();
146 std::cerr << err_msg << std::endl;
147 assert(false);
148 }
149 }
150 else if (funcName == "UNSAFE_BUFACCESS")
151 {
152 // handle other stub functions
153 //void UNSAFE_BUFACCESS(void* data, int size);
155 if (callNode->arg_size() < 2) return;
157 u32_t size_id = callNode->getArgument(1)->getId();
158 IntervalValue val = as[size_id].getInterval();
159 if (val.isBottom())
160 {
161 assert(false && "UNSAFE_BUFACCESS size is bottom");
162 }
163 const SVFVar* arg0Val = callNode->getArgument(0);
165 if (!isSafe)
166 {
167 std::cout << "detect buffer overflow success: " << callNode->toString() << std::endl;
168 return;
169 }
170 else
171 {
172 std::string err_msg = "this UNSAFE_BUFACCESS should be a buffer overflow but not detected. Pos: ";
173 err_msg += callNode->getSourceLoc();
174 std::cerr << err_msg << std::endl;
175 assert(false);
176 }
177 }
178}
179
187{
188 extAPIBufOverflowCheckRules["llvm_memcpy_p0i8_p0i8_i64"] = {{0, 2}, {1, 2}};
189 extAPIBufOverflowCheckRules["llvm_memcpy_p0_p0_i64"] = {{0, 2}, {1, 2}};
190 extAPIBufOverflowCheckRules["llvm_memcpy_p0i8_p0i8_i32"] = {{0, 2}, {1, 2}};
191 extAPIBufOverflowCheckRules["llvm_memcpy"] = {{0, 2}, {1, 2}};
192 extAPIBufOverflowCheckRules["llvm_memmove"] = {{0, 2}, {1, 2}};
193 extAPIBufOverflowCheckRules["llvm_memmove_p0i8_p0i8_i64"] = {{0, 2}, {1, 2}};
194 extAPIBufOverflowCheckRules["llvm_memmove_p0_p0_i64"] = {{0, 2}, {1, 2}};
195 extAPIBufOverflowCheckRules["llvm_memmove_p0i8_p0i8_i32"] = {{0, 2}, {1, 2}};
196 extAPIBufOverflowCheckRules["__memcpy_chk"] = {{0, 2}, {1, 2}};
197 extAPIBufOverflowCheckRules["memmove"] = {{0, 2}, {1, 2}};
198 extAPIBufOverflowCheckRules["bcopy"] = {{0, 2}, {1, 2}};
199 extAPIBufOverflowCheckRules["memccpy"] = {{0, 3}, {1, 3}};
200 extAPIBufOverflowCheckRules["__memmove_chk"] = {{0, 2}, {1, 2}};
201 extAPIBufOverflowCheckRules["llvm_memset"] = {{0, 2}};
202 extAPIBufOverflowCheckRules["llvm_memset_p0i8_i32"] = {{0, 2}};
203 extAPIBufOverflowCheckRules["llvm_memset_p0i8_i64"] = {{0, 2}};
204 extAPIBufOverflowCheckRules["llvm_memset_p0_i64"] = {{0, 2}};
205 extAPIBufOverflowCheckRules["__memset_chk"] = {{0, 2}};
206 extAPIBufOverflowCheckRules["wmemset"] = {{0, 2}};
207 extAPIBufOverflowCheckRules["strncpy"] = {{0, 2}, {1, 2}};
208 extAPIBufOverflowCheckRules["iconv"] = {{1, 2}, {3, 4}};
209}
210
221 const CallICFGNode* call)
222{
223 assert(call->getCalledFunction() && "SVFFunction* is nullptr");
224
226
227 // Determine the type of external memory API
228 for (const std::string &annotation : ExtAPI::getExtAPI()->getExtFuncAnnotations(call->getCalledFunction()))
229 {
230 if (annotation.find("MEMCPY") != std::string::npos)
232 if (annotation.find("MEMSET") != std::string::npos)
234 if (annotation.find("STRCPY") != std::string::npos)
236 if (annotation.find("STRCAT") != std::string::npos)
238 }
239
240 // Apply buffer overflow checks based on the determined API type
242 {
243 if (extAPIBufOverflowCheckRules.count(call->getCalledFunction()->getName()) == 0)
244 {
245 SVFUtil::errs() << "Warning: " << call->getCalledFunction()->getName() << " is not in the rules, please implement it\n";
246 return;
247 }
248 std::vector<std::pair<u32_t, u32_t>> args =
250 for (auto arg : args)
251 {
252 IntervalValue offset = as[call->getArgument(arg.second)->getId()].getInterval() - IntervalValue(1);
253 const SVFVar* argVar = call->getArgument(arg.first);
255 {
256 AEException bug(call->toString());
257 addBugToReporter(bug, call);
258 }
259 }
260 }
261 else if (extType == AbsExtAPI::MEMSET)
262 {
263 if (extAPIBufOverflowCheckRules.count(call->getCalledFunction()->getName()) == 0)
264 {
265 SVFUtil::errs() << "Warning: " << call->getCalledFunction()->getName() << " is not in the rules, please implement it\n";
266 return;
267 }
268 std::vector<std::pair<u32_t, u32_t>> args =
270 for (auto arg : args)
271 {
272 IntervalValue offset = as[call->getArgument(arg.second)->getId()].getInterval() - IntervalValue(1);
273 const SVFVar* argVar = call->getArgument(arg.first);
275 {
276 AEException bug(call->toString());
277 addBugToReporter(bug, call);
278 }
279 }
280 }
281 else if (extType == AbsExtAPI::STRCPY)
282 {
283 if (!detectStrcpy(as, call))
284 {
285 AEException bug(call->toString());
286 addBugToReporter(bug, call);
287 }
288 }
289 else if (extType == AbsExtAPI::STRCAT)
290 {
291 if (!detectStrcat(as, call))
292 {
293 AEException bug(call->toString());
294 addBugToReporter(bug, call);
295 }
296 }
297 else
298 {
299 // Handle other cases
300 }
301}
302
315{
316 SVFIR* svfir = PAG::getPAG();
317 auto obj = svfir->getGNode(objId);
318
319 // if the object is a BaseObjVar, return the byte offset directly
320 if (SVFUtil::isa<BaseObjVar>(obj))
321 {
322 return as.getByteOffset(gep);
323 }
324 else if (SVFUtil::isa<GepObjVar>(obj))
325 {
326 // if the object is a GepObjVar, return the offset from the base object
327 return getGepObjOffsetFromBase(SVFUtil::cast<GepObjVar>(obj)) + as.getByteOffset(gep);
328 }
329 else
330 {
331 assert(SVFUtil::isa<DummyObjVar>(obj) && "Unknown object type");
332 return IntervalValue::top();
333 }
334}
335
347{
348 SVFIR* svfir = PAG::getPAG();
349
350 for (const auto& objAddr : objAddrs)
351 {
353 auto obj = svfir->getGNode(objId);
354 // if the object is a BaseObjVar, add the offset directly
355 if (SVFUtil::isa<BaseObjVar>(obj))
356 {
357 for (const auto& gepAddr : gepAddrs)
358 {
360 const GepObjVar* gepObjVar = SVFUtil::cast<GepObjVar>(svfir->getGNode(gepObj));
362 }
363 }
364 else if (SVFUtil::isa<GepObjVar>(obj))
365 {
366 // if the object is a GepObjVar, add the offset from the base object
367 const GepObjVar* objVar = SVFUtil::cast<GepObjVar>(obj);
368 for (const auto& gepAddr : gepAddrs)
369 {
371 const GepObjVar* gepObjVar = SVFUtil::cast<GepObjVar>(svfir->getGNode(gepObj));
373 {
377 }
378 else
379 {
380 assert(false && "GEP RHS object has no offset from base");
381 }
382 }
383 }
384 }
385}
386
404
416{
417 const std::vector<std::string> strcatGroup = {"__strcat_chk", "strcat", "__wcscat_chk", "wcscat"};
418 const std::vector<std::string> strncatGroup = {"__strncat_chk", "strncat", "__wcsncat_chk", "wcsncat"};
419
420 if (std::find(strcatGroup.begin(), strcatGroup.end(), call->getCalledFunction()->getName()) != strcatGroup.end())
421 {
422 const SVFVar* arg0Val = call->getArgument(0);
423 const SVFVar* arg1Val = call->getArgument(1);
428 }
429 else if (std::find(strncatGroup.begin(), strncatGroup.end(), call->getCalledFunction()->getName()) != strncatGroup.end())
430 {
431 const SVFVar* arg0Val = call->getArgument(0);
432 const SVFVar* arg2Val = call->getArgument(2);
433 IntervalValue arg2Num = as[arg2Val->getId()].getInterval();
437 }
438 else
439 {
440 assert(false && "Unknown strcat function, please add it to strcatGroup or strncatGroup");
441 abort();
442 }
443}
444
457{
458 SVFIR* svfir = PAG::getPAG();
459 NodeID value_id = value->getId();
460
461 assert(as[value_id].isAddr());
462 for (const auto& addr : as[value_id].getAddrs())
463 {
465 u32_t size = 0;
466
467 // if the object is a constant size object, get the size directly
468 if (svfir->getBaseObj(objId)->isConstantByteSize())
469 {
470 size = svfir->getBaseObj(objId)->getByteSizeOfObj();
471 }
472 else
473 {
474 // if the object is not a constant size object, get the size from the addrStmt
475 const ICFGNode* addrNode = SVFUtil::cast<ICFGNode>(
476 svfir->getBaseObj(objId)->getGNode());
477 for (const SVFStmt* stmt2 : addrNode->getSVFStmts())
478 {
479 if (const AddrStmt* addrStmt = SVFUtil::dyn_cast<AddrStmt>(stmt2))
480 {
481 size = as.getAllocaInstByteSize(addrStmt);
482 }
483 }
484 }
485
487 // if the object is a GepObjVar, get the offset from the base object
488 if (SVFUtil::isa<GepObjVar>(svfir->getGNode(objId)))
489 {
490 offset = getGepObjOffsetFromBase(SVFUtil::cast<GepObjVar>(svfir->getGNode(objId))) + len;
491 }
492 else
493 {
494 // if the object is a BaseObjVar, get the offset directly
495 offset = len;
496 }
497 // if the offset is greater than the size, return false
498 if (offset.ub().getIntNumeral() >= size)
499 {
500 return false;
501 }
502 }
503 return true;
504}
buffer offset
Definition cJSON.cpp:1113
Exception class for handling errors in Abstract Execution.
Definition AEDetector.h:107
IntervalValue getStrlen(AbstractState &as, const SVF::SVFVar *strValue)
Calculates the length of a string.
ExtAPIType
Enumeration of external API types.
Definition AbsExtAPI.h:52
static AbstractInterpretation & getAEInstance()
AbstractState & getAbsStateFromTrace(const ICFGNode *node)
Set< const CallICFGNode * > checkpoints
static u32_t getInternalID(u32_t idx)
Return the internal index if idx is an address otherwise return the value of idx.
IntervalValue getAccessOffset(AbstractState &as, NodeID objId, const GepStmt *gep)
Retrieves the access offset for a given object and GEP statement.
void addToGepObjOffsetFromBase(const GepObjVar *obj, const IntervalValue &offset)
Adds an offset to a GEP object.
Definition AEDetector.h:190
Map< std::string, std::vector< std::pair< u32_t, u32_t > > > extAPIBufOverflowCheckRules
Rules for checking buffer overflows in external APIs.
Definition AEDetector.h:318
void detect(AbstractState &as, const ICFGNode *)
Detect buffer overflow issues within a node.
bool detectStrcpy(AbstractState &as, const CallICFGNode *call)
Detects buffer overflow in 'strcpy' function calls.
bool detectStrcat(AbstractState &as, const CallICFGNode *call)
Detects buffer overflow in 'strcat' function calls.
IntervalValue getGepObjOffsetFromBase(const GepObjVar *obj) const
Retrieves the offset of a GEP object from its base.
Definition AEDetector.h:210
void handleStubFunctions(const CallICFGNode *)
Handles external API calls related to buffer overflow detection.
bool hasGepObjOffsetFromBase(const GepObjVar *obj) const
Checks if a GEP object has an associated offset.
Definition AEDetector.h:200
void updateGepObjOffsetFromBase(AddressValue gepAddrs, AddressValue objAddrs, IntervalValue offset)
Updates the offset of a GEP object from its base.
bool canSafelyAccessMemory(AbstractState &as, const SVFVar *value, const IntervalValue &len)
Checks if memory can be safely accessed.
void initExtAPIBufOverflowCheckRules()
Initializes external API buffer overflow check rules.
void detectExtAPI(AbstractState &as, const CallICFGNode *call)
Handles external API calls related to buffer overflow detection.
void addBugToReporter(const AEException &e, const ICFGNode *node)
Adds a bug to the reporter based on an exception.
Definition AEDetector.h:232
const std::string toString() const override
Definition ICFG.cpp:131
const ValVar * getArgument(u32_t ArgNo) const
Parameter operations.
Definition ICFGNode.h:500
const SVFFunction * getCalledFunction() const
Definition ICFGNode.h:518
const std::vector< std::string > & getExtFuncAnnotations(const SVFFunction *fun)
Definition ExtAPI.cpp:196
static ExtAPI * getExtAPI()
Definition ExtAPI.cpp:42
NodeType * getGNode(NodeID id) const
Get a node.
const SVFStmtList & getSVFStmts() const
Definition ICFGNode.h:117
static IntervalValue bottom()
Create the bottom IntervalValue [+inf, -inf].
static IntervalValue top()
Create the IntervalValue [-inf, +inf].
const SVFBaseNode * getGNode() const
Get the reference value to this object.
u32_t getByteSizeOfObj() const
Get the byte size of this object.
bool isConstantByteSize() const
Check if byte size is a const value.
NodeID getId() const
Get ID.
const MemObj * getBaseObj(NodeID id) const
Definition SVFIR.h:481
static SVFIR * getPAG(bool buildFromFile=false)
Singleton design here to make sure we only have one instance during any analysis.
Definition SVFIR.h:116
const std::string & getName() const
Definition SVFValue.h:243
bool isExtCall(const SVFFunction *fun)
Definition SVFUtil.h:278
std::ostream & errs()
Overwrite llvm::errs()
Definition SVFUtil.h:56
for isBitcode
Definition BasicTypes.h:68
u32_t NodeID
Definition GeneralType.h:55
llvm::IRBuilder IRBuilder
Definition BasicTypes.h:74
unsigned u32_t
Definition GeneralType.h:46