Static Value-Flow Analysis
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 
28 #include <AE/Svfexe/AEDetector.h>
29 #include <AE/Svfexe/AbsExtAPI.h>
31 
32 using 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 
60  IntervalValue baseObjSize = IntervalValue::bottom();
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
85  IntervalValue accessOffset = getAccessOffset(as, objId, gep);
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  {
101  detectExtAPI(as, callNode);
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;
124  AbstractState& as =
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);
135  bool isSafe = canSafelyAccessMemory(as, arg0Val, val);
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);
164  bool isSafe = canSafelyAccessMemory(as, arg0Val, val);
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)
231  extType = AbsExtAPI::MEMCPY;
232  if (annotation.find("MEMSET") != std::string::npos)
233  extType = AbsExtAPI::MEMSET;
234  if (annotation.find("STRCPY") != std::string::npos)
235  extType = AbsExtAPI::STRCPY;
236  if (annotation.find("STRCAT") != std::string::npos)
237  extType = AbsExtAPI::STRCAT;
238  }
239 
240  // Apply buffer overflow checks based on the determined API type
241  if (extType == AbsExtAPI::MEMCPY)
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);
254  if (!canSafelyAccessMemory(as, argVar, offset))
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);
274  if (!canSafelyAccessMemory(as, argVar, offset))
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 FIObjVar, return the byte offset directly
320  if (SVFUtil::isa<FIObjVar>(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  {
352  NodeID objId = AbstractState::getInternalID(objAddr);
353  auto obj = svfir->getGNode(objId);
354  // if the object is a FIObjVar, add the offset directly
355  if (SVFUtil::isa<FIObjVar>(obj))
356  {
357  for (const auto& gepAddr : gepAddrs)
358  {
359  NodeID gepObj = AbstractState::getInternalID(gepAddr);
360  const GepObjVar* gepObjVar = SVFUtil::cast<GepObjVar>(svfir->getGNode(gepObj));
361  addToGepObjOffsetFromBase(gepObjVar, offset);
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  {
370  NodeID gepObj = AbstractState::getInternalID(gepAddr);
371  const GepObjVar* gepObjVar = SVFUtil::cast<GepObjVar>(svfir->getGNode(gepObj));
372  if (hasGepObjOffsetFromBase(objVar))
373  {
374  IntervalValue objOffsetFromBase = getGepObjOffsetFromBase(objVar);
375  if (!hasGepObjOffsetFromBase(gepObjVar))
376  addToGepObjOffsetFromBase(gepObjVar, objOffsetFromBase + offset);
377  }
378  else
379  {
380  assert(false && "GEP RHS object has no offset from base");
381  }
382  }
383  }
384  }
385 }
386 
398 {
399  const SVFVar* arg0Val = call->getArgument(0);
400  const SVFVar* arg1Val = call->getArgument(1);
402  return canSafelyAccessMemory(as, arg0Val, strLen);
403 }
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);
426  IntervalValue totalLen = strLen0 + strLen1;
427  return canSafelyAccessMemory(as, arg0Val, totalLen);
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();
435  IntervalValue totalLen = strLen0 + arg2Num;
436  return canSafelyAccessMemory(as, arg0Val, totalLen);
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  {
464  NodeID objId = AbstractState::getInternalID(addr);
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 FIObjVar, 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
const char *const string
Definition: cJSON.h:172
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.
Definition: AbsExtAPI.cpp:469
ExtAPIType
Enumeration of external API types.
Definition: AbsExtAPI.h:52
Set< const CallICFGNode * > checkpoints
static AbstractInterpretation & getAEInstance()
AbstractState & getAbsStateFromTrace(const ICFGNode *node)
u32_t getAllocaInstByteSize(const AddrStmt *addr)
IntervalValue getByteOffset(const GepStmt *gep)
static u32_t getInternalID(u32_t idx)
Return the internal index if idx is an address otherwise return the value of idx.
s64_t getIntNumeral() const
Definition: NumericValue.h:703
IntervalValue getAccessOffset(AbstractState &as, NodeID objId, const GepStmt *gep)
Retrieves the access offset for a given object and GEP statement.
Definition: AEDetector.cpp:314
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.
Definition: AEDetector.cpp:44
bool detectStrcpy(AbstractState &as, const CallICFGNode *call)
Detects buffer overflow in 'strcpy' function calls.
Definition: AEDetector.cpp:397
bool detectStrcat(AbstractState &as, const CallICFGNode *call)
Detects buffer overflow in 'strcat' function calls.
Definition: AEDetector.cpp:415
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.
Definition: AEDetector.cpp:114
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.
Definition: AEDetector.cpp:346
bool canSafelyAccessMemory(AbstractState &as, const SVFVar *value, const IntervalValue &len)
Checks if memory can be safely accessed.
Definition: AEDetector.cpp:456
void initExtAPIBufOverflowCheckRules()
Initializes external API buffer overflow check rules.
Definition: AEDetector.cpp:186
void detectExtAPI(AbstractState &as, const CallICFGNode *call)
Handles external API calls related to buffer overflow detection.
Definition: AEDetector.cpp:220
void addBugToReporter(const AEException &e, const ICFGNode *node)
Adds a bug to the reporter based on an exception.
Definition: AEDetector.h:232
const SVFFunction * getCalledFunction() const
Definition: ICFGNode.h:518
const std::string toString() const override
Definition: ICFG.cpp:131
const std::string getSourceLoc() const override
Definition: ICFGNode.h:588
u32_t arg_size() const
Definition: ICFGNode.h:505
const SVFVar * getArgument(u32_t ArgNo) const
Parameter operations.
Definition: ICFGNode.h:500
static ExtAPI * getExtAPI()
Definition: ExtAPI.cpp:42
NodeType * getGNode(NodeID id) const
Get a node.
Definition: GenericGraph.h:653
const SVFStmtList & getSVFStmts() const
Definition: ICFGNode.h:117
bool isBottom() const
Definition: IntervalValue.h:71
const BoundedInt & ub() const
Return the upper bound.
static IntervalValue bottom()
Create the bottom IntervalValue [+inf, -inf].
static IntervalValue top()
Create the IntervalValue [-inf, +inf].
Definition: IntervalValue.h:94
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.
Definition: GenericGraph.h:260
static SVFIR * getPAG(bool buildFromFile=false)
Singleton design here to make sure we only have one instance during any analysis.
Definition: SVFIR.h:115
const MemObj * getBaseObj(NodeID id) const
Definition: SVFIR.h:459
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
unsigned u32_t
Definition: GeneralType.h:46