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