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 15 #include "ClangSACheckers.h" 16 #include "clang/StaticAnalyzer/Core/Checker.h" 17 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 18 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 19 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 21 #include "clang/AST/ExprCXX.h" 22 #include "clang/Basic/SourceManager.h" 23 #include "llvm/ADT/SmallString.h" 24 using namespace clang; 25 using namespace ento; 26 27 namespace { 28 class StackAddrEscapeChecker : public Checker< check::PreStmt<ReturnStmt>, 29 check::EndPath > { 30 mutable OwningPtr<BuiltinBug> BT_stackleak; 31 mutable OwningPtr<BuiltinBug> BT_returnstack; 32 33 public: 34 void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; 35 void checkEndPath(CheckerContext &Ctx) const; 36 private: 37 void EmitStackError(CheckerContext &C, const MemRegion *R, 38 const Expr *RetE) const; 39 static SourceRange GenName(raw_ostream &os, const MemRegion *R, 40 SourceManager &SM); 41 }; 42 } 43 44 SourceRange StackAddrEscapeChecker::GenName(raw_ostream &os, 45 const MemRegion *R, 46 SourceManager &SM) { 47 // Get the base region, stripping away fields and elements. 48 R = R->getBaseRegion(); 49 SourceRange range; 50 os << "Address of "; 51 52 // Check if the region is a compound literal. 53 if (const CompoundLiteralRegion* CR = dyn_cast<CompoundLiteralRegion>(R)) { 54 const CompoundLiteralExpr *CL = CR->getLiteralExpr(); 55 os << "stack memory associated with a compound literal " 56 "declared on line " 57 << SM.getExpansionLineNumber(CL->getLocStart()) 58 << " returned to caller"; 59 range = CL->getSourceRange(); 60 } 61 else if (const AllocaRegion* AR = dyn_cast<AllocaRegion>(R)) { 62 const Expr *ARE = AR->getExpr(); 63 SourceLocation L = ARE->getLocStart(); 64 range = ARE->getSourceRange(); 65 os << "stack memory allocated by call to alloca() on line " 66 << SM.getExpansionLineNumber(L); 67 } 68 else if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) { 69 const BlockDecl *BD = BR->getCodeRegion()->getDecl(); 70 SourceLocation L = BD->getLocStart(); 71 range = BD->getSourceRange(); 72 os << "stack-allocated block declared on line " 73 << SM.getExpansionLineNumber(L); 74 } 75 else if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { 76 os << "stack memory associated with local variable '" 77 << VR->getString() << '\''; 78 range = VR->getDecl()->getSourceRange(); 79 } 80 else if (const CXXTempObjectRegion *TOR = dyn_cast<CXXTempObjectRegion>(R)) { 81 os << "stack memory associated with temporary object of type '" 82 << TOR->getValueType().getAsString() << '\''; 83 range = TOR->getExpr()->getSourceRange(); 84 } 85 else { 86 llvm_unreachable("Invalid region in ReturnStackAddressChecker."); 87 } 88 89 return range; 90 } 91 92 void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R, 93 const Expr *RetE) const { 94 ExplodedNode *N = C.generateSink(); 95 96 if (!N) 97 return; 98 99 if (!BT_returnstack) 100 BT_returnstack.reset( 101 new BuiltinBug("Return of address to stack-allocated memory")); 102 103 // Generate a report for this bug. 104 SmallString<512> buf; 105 llvm::raw_svector_ostream os(buf); 106 SourceRange range = GenName(os, R, C.getSourceManager()); 107 os << " returned to caller"; 108 BugReport *report = new BugReport(*BT_returnstack, os.str(), N); 109 report->addRange(RetE->getSourceRange()); 110 if (range.isValid()) 111 report->addRange(range); 112 113 C.emitReport(report); 114 } 115 116 void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, 117 CheckerContext &C) const { 118 119 const Expr *RetE = RS->getRetValue(); 120 if (!RetE) 121 return; 122 RetE = RetE->IgnoreParens(); 123 124 const LocationContext *LCtx = C.getLocationContext(); 125 SVal V = C.getState()->getSVal(RetE, LCtx); 126 const MemRegion *R = V.getAsRegion(); 127 128 if (!R) 129 return; 130 131 const StackSpaceRegion *SS = 132 dyn_cast_or_null<StackSpaceRegion>(R->getMemorySpace()); 133 134 if (!SS) 135 return; 136 137 // Return stack memory in an ancestor stack frame is fine. 138 const StackFrameContext *CurFrame = LCtx->getCurrentStackFrame(); 139 const StackFrameContext *MemFrame = SS->getStackFrame(); 140 if (MemFrame != CurFrame) 141 return; 142 143 // Automatic reference counting automatically copies blocks. 144 if (C.getASTContext().getLangOpts().ObjCAutoRefCount && 145 isa<BlockDataRegion>(R)) 146 return; 147 148 // Returning a record by value is fine. (In this case, the returned 149 // expression will be a copy-constructor, possibly wrapped in an 150 // ExprWithCleanups node.) 151 if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE)) 152 RetE = Cleanup->getSubExpr(); 153 if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType()) 154 return; 155 156 EmitStackError(C, R, RetE); 157 } 158 159 void StackAddrEscapeChecker::checkEndPath(CheckerContext &Ctx) const { 160 ProgramStateRef state = Ctx.getState(); 161 162 // Iterate over all bindings to global variables and see if it contains 163 // a memory region in the stack space. 164 class CallBack : public StoreManager::BindingsHandler { 165 private: 166 CheckerContext &Ctx; 167 const StackFrameContext *CurSFC; 168 public: 169 SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V; 170 171 CallBack(CheckerContext &CC) : 172 Ctx(CC), 173 CurSFC(CC.getLocationContext()->getCurrentStackFrame()) 174 {} 175 176 bool HandleBinding(StoreManager &SMgr, Store store, 177 const MemRegion *region, SVal val) { 178 179 if (!isa<GlobalsSpaceRegion>(region->getMemorySpace())) 180 return true; 181 182 const MemRegion *vR = val.getAsRegion(); 183 if (!vR) 184 return true; 185 186 // Under automated retain release, it is okay to assign a block 187 // directly to a global variable. 188 if (Ctx.getASTContext().getLangOpts().ObjCAutoRefCount && 189 isa<BlockDataRegion>(vR)) 190 return true; 191 192 if (const StackSpaceRegion *SSR = 193 dyn_cast<StackSpaceRegion>(vR->getMemorySpace())) { 194 // If the global variable holds a location in the current stack frame, 195 // record the binding to emit a warning. 196 if (SSR->getStackFrame() == CurSFC) 197 V.push_back(std::make_pair(region, vR)); 198 } 199 200 return true; 201 } 202 }; 203 204 CallBack cb(Ctx); 205 state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb); 206 207 if (cb.V.empty()) 208 return; 209 210 // Generate an error node. 211 ExplodedNode *N = Ctx.addTransition(state); 212 if (!N) 213 return; 214 215 if (!BT_stackleak) 216 BT_stackleak.reset( 217 new BuiltinBug("Stack address stored into global variable", 218 "Stack address was saved into a global variable. " 219 "This is dangerous because the address will become " 220 "invalid after returning from the function")); 221 222 for (unsigned i = 0, e = cb.V.size(); i != e; ++i) { 223 // Generate a report for this bug. 224 SmallString<512> buf; 225 llvm::raw_svector_ostream os(buf); 226 SourceRange range = GenName(os, cb.V[i].second, 227 Ctx.getSourceManager()); 228 os << " is still referred to by the global variable '"; 229 const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion()); 230 os << *VR->getDecl() 231 << "' upon returning to the caller. This will be a dangling reference"; 232 BugReport *report = new BugReport(*BT_stackleak, os.str(), N); 233 if (range.isValid()) 234 report->addRange(range); 235 236 Ctx.emitReport(report); 237 } 238 } 239 240 void ento::registerStackAddrEscapeChecker(CheckerManager &mgr) { 241 mgr.registerChecker<StackAddrEscapeChecker>(); 242 } 243