StackAddrEscapeChecker.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. //=== StackAddrEscapeChecker.cpp ----------------------------------*- C++ -*--//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. //
  10. // This file defines stack address leak checker, which checks if an invalid
  11. // stack address is stored into a global or heap location. See CERT DCL30-C.
  12. //
  13. //===----------------------------------------------------------------------===//
  14. #include "ClangSACheckers.h"
  15. #include "clang/StaticAnalyzer/Core/Checker.h"
  16. #include "clang/StaticAnalyzer/Core/CheckerManager.h"
  17. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  18. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  19. #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
  20. #include "clang/Basic/SourceManager.h"
  21. #include "llvm/ADT/SmallString.h"
  22. using namespace clang;
  23. using namespace ento;
  24. namespace {
  25. class StackAddrEscapeChecker : public Checker< check::PreStmt<ReturnStmt>,
  26. check::EndPath > {
  27. mutable llvm::OwningPtr<BuiltinBug> BT_stackleak;
  28. mutable llvm::OwningPtr<BuiltinBug> BT_returnstack;
  29. public:
  30. void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
  31. void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const;
  32. private:
  33. void EmitStackError(CheckerContext &C, const MemRegion *R,
  34. const Expr *RetE) const;
  35. static SourceRange GenName(raw_ostream &os, const MemRegion *R,
  36. SourceManager &SM);
  37. };
  38. }
  39. SourceRange StackAddrEscapeChecker::GenName(raw_ostream &os,
  40. const MemRegion *R,
  41. SourceManager &SM) {
  42. // Get the base region, stripping away fields and elements.
  43. R = R->getBaseRegion();
  44. SourceRange range;
  45. os << "Address of ";
  46. // Check if the region is a compound literal.
  47. if (const CompoundLiteralRegion* CR = dyn_cast<CompoundLiteralRegion>(R)) {
  48. const CompoundLiteralExpr *CL = CR->getLiteralExpr();
  49. os << "stack memory associated with a compound literal "
  50. "declared on line "
  51. << SM.getExpansionLineNumber(CL->getLocStart())
  52. << " returned to caller";
  53. range = CL->getSourceRange();
  54. }
  55. else if (const AllocaRegion* AR = dyn_cast<AllocaRegion>(R)) {
  56. const Expr *ARE = AR->getExpr();
  57. SourceLocation L = ARE->getLocStart();
  58. range = ARE->getSourceRange();
  59. os << "stack memory allocated by call to alloca() on line "
  60. << SM.getExpansionLineNumber(L);
  61. }
  62. else if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) {
  63. const BlockDecl *BD = BR->getCodeRegion()->getDecl();
  64. SourceLocation L = BD->getLocStart();
  65. range = BD->getSourceRange();
  66. os << "stack-allocated block declared on line "
  67. << SM.getExpansionLineNumber(L);
  68. }
  69. else if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
  70. os << "stack memory associated with local variable '"
  71. << VR->getString() << '\'';
  72. range = VR->getDecl()->getSourceRange();
  73. }
  74. else {
  75. assert(false && "Invalid region in ReturnStackAddressChecker.");
  76. }
  77. return range;
  78. }
  79. void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R,
  80. const Expr *RetE) const {
  81. ExplodedNode *N = C.generateSink();
  82. if (!N)
  83. return;
  84. if (!BT_returnstack)
  85. BT_returnstack.reset(
  86. new BuiltinBug("Return of address to stack-allocated memory"));
  87. // Generate a report for this bug.
  88. llvm::SmallString<512> buf;
  89. llvm::raw_svector_ostream os(buf);
  90. SourceRange range = GenName(os, R, C.getSourceManager());
  91. os << " returned to caller";
  92. BugReport *report = new BugReport(*BT_returnstack, os.str(), N);
  93. report->addRange(RetE->getSourceRange());
  94. if (range.isValid())
  95. report->addRange(range);
  96. C.EmitReport(report);
  97. }
  98. void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
  99. CheckerContext &C) const {
  100. const Expr *RetE = RS->getRetValue();
  101. if (!RetE)
  102. return;
  103. SVal V = C.getState()->getSVal(RetE);
  104. const MemRegion *R = V.getAsRegion();
  105. if (!R || !R->hasStackStorage())
  106. return;
  107. if (R->hasStackStorage()) {
  108. // Automatic reference counting automatically copies blocks.
  109. if (C.getASTContext().getLangOptions().ObjCAutoRefCount &&
  110. isa<BlockDataRegion>(R))
  111. return;
  112. EmitStackError(C, R, RetE);
  113. return;
  114. }
  115. }
  116. void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
  117. ExprEngine &Eng) const {
  118. const ProgramState *state = B.getState();
  119. // Iterate over all bindings to global variables and see if it contains
  120. // a memory region in the stack space.
  121. class CallBack : public StoreManager::BindingsHandler {
  122. private:
  123. ExprEngine &Eng;
  124. const StackFrameContext *CurSFC;
  125. public:
  126. SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V;
  127. CallBack(ExprEngine &Eng, const LocationContext *LCtx)
  128. : Eng(Eng), CurSFC(LCtx->getCurrentStackFrame()) {}
  129. bool HandleBinding(StoreManager &SMgr, Store store,
  130. const MemRegion *region, SVal val) {
  131. if (!isa<GlobalsSpaceRegion>(region->getMemorySpace()))
  132. return true;
  133. const MemRegion *vR = val.getAsRegion();
  134. if (!vR)
  135. return true;
  136. // Under automated retain release, it is okay to assign a block
  137. // directly to a global variable.
  138. if (Eng.getContext().getLangOptions().ObjCAutoRefCount &&
  139. isa<BlockDataRegion>(vR))
  140. return true;
  141. if (const StackSpaceRegion *SSR =
  142. dyn_cast<StackSpaceRegion>(vR->getMemorySpace())) {
  143. // If the global variable holds a location in the current stack frame,
  144. // record the binding to emit a warning.
  145. if (SSR->getStackFrame() == CurSFC)
  146. V.push_back(std::make_pair(region, vR));
  147. }
  148. return true;
  149. }
  150. };
  151. CallBack cb(Eng, B.getPredecessor()->getLocationContext());
  152. state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb);
  153. if (cb.V.empty())
  154. return;
  155. // Generate an error node.
  156. ExplodedNode *N = B.generateNode(state);
  157. if (!N)
  158. return;
  159. if (!BT_stackleak)
  160. BT_stackleak.reset(
  161. new BuiltinBug("Stack address stored into global variable",
  162. "Stack address was saved into a global variable. "
  163. "This is dangerous because the address will become "
  164. "invalid after returning from the function"));
  165. for (unsigned i = 0, e = cb.V.size(); i != e; ++i) {
  166. // Generate a report for this bug.
  167. llvm::SmallString<512> buf;
  168. llvm::raw_svector_ostream os(buf);
  169. SourceRange range = GenName(os, cb.V[i].second,
  170. Eng.getContext().getSourceManager());
  171. os << " is still referred to by the global variable '";
  172. const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion());
  173. os << VR->getDecl()->getNameAsString()
  174. << "' upon returning to the caller. This will be a dangling reference";
  175. BugReport *report = new BugReport(*BT_stackleak, os.str(), N);
  176. if (range.isValid())
  177. report->addRange(range);
  178. Eng.getBugReporter().EmitReport(report);
  179. }
  180. }
  181. void ento::registerStackAddrEscapeChecker(CheckerManager &mgr) {
  182. mgr.registerChecker<StackAddrEscapeChecker>();
  183. }