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