StackAddrEscapeChecker.cpp revision ec8605f1d7ec846dbf51047bfd5c56d32d1ff91c
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/GRState.h" 21#include "clang/Basic/SourceManager.h" 22#include "llvm/ADT/SmallString.h" 23using namespace clang; 24using namespace ento; 25 26namespace { 27class StackAddrEscapeChecker : public Checker< check::PreStmt<ReturnStmt>, 28 check::EndPath > { 29 mutable llvm::OwningPtr<BuiltinBug> BT_stackleak; 30 mutable llvm::OwningPtr<BuiltinBug> BT_returnstack; 31 32public: 33 void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; 34 void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const; 35private: 36 void EmitStackError(CheckerContext &C, const MemRegion *R, 37 const Expr *RetE) const; 38 static SourceRange GenName(llvm::raw_ostream &os, const MemRegion *R, 39 SourceManager &SM); 40}; 41} 42 43SourceRange StackAddrEscapeChecker::GenName(llvm::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.getInstantiationLineNumber(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.getInstantiationLineNumber(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.getInstantiationLineNumber(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 { 80 assert(false && "Invalid region in ReturnStackAddressChecker."); 81 } 82 83 return range; 84} 85 86void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R, 87 const Expr *RetE) const { 88 ExplodedNode *N = C.generateSink(); 89 90 if (!N) 91 return; 92 93 if (!BT_returnstack) 94 BT_returnstack.reset( 95 new BuiltinBug("Return of address to stack-allocated memory")); 96 97 // Generate a report for this bug. 98 llvm::SmallString<512> buf; 99 llvm::raw_svector_ostream os(buf); 100 SourceRange range = GenName(os, R, C.getSourceManager()); 101 os << " returned to caller"; 102 RangedBugReport *report = new RangedBugReport(*BT_returnstack, os.str(), N); 103 report->addRange(RetE->getSourceRange()); 104 if (range.isValid()) 105 report->addRange(range); 106 107 C.EmitReport(report); 108} 109 110void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, 111 CheckerContext &C) const { 112 113 const Expr *RetE = RS->getRetValue(); 114 if (!RetE) 115 return; 116 117 SVal V = C.getState()->getSVal(RetE); 118 const MemRegion *R = V.getAsRegion(); 119 120 if (!R || !R->hasStackStorage()) 121 return; 122 123 if (R->hasStackStorage()) { 124 EmitStackError(C, R, RetE); 125 return; 126 } 127} 128 129void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B, 130 ExprEngine &Eng) const { 131 132 const GRState *state = B.getState(); 133 134 // Iterate over all bindings to global variables and see if it contains 135 // a memory region in the stack space. 136 class CallBack : public StoreManager::BindingsHandler { 137 private: 138 const StackFrameContext *CurSFC; 139 public: 140 llvm::SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V; 141 142 CallBack(const LocationContext *LCtx) 143 : CurSFC(LCtx->getCurrentStackFrame()) {} 144 145 bool HandleBinding(StoreManager &SMgr, Store store, 146 const MemRegion *region, SVal val) { 147 148 if (!isa<GlobalsSpaceRegion>(region->getMemorySpace())) 149 return true; 150 151 const MemRegion *vR = val.getAsRegion(); 152 if (!vR) 153 return true; 154 155 if (const StackSpaceRegion *SSR = 156 dyn_cast<StackSpaceRegion>(vR->getMemorySpace())) { 157 // If the global variable holds a location in the current stack frame, 158 // record the binding to emit a warning. 159 if (SSR->getStackFrame() == CurSFC) 160 V.push_back(std::make_pair(region, vR)); 161 } 162 163 return true; 164 } 165 }; 166 167 CallBack cb(B.getPredecessor()->getLocationContext()); 168 state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb); 169 170 if (cb.V.empty()) 171 return; 172 173 // Generate an error node. 174 ExplodedNode *N = B.generateNode(state); 175 if (!N) 176 return; 177 178 if (!BT_stackleak) 179 BT_stackleak.reset( 180 new BuiltinBug("Stack address stored into global variable", 181 "Stack address was saved into a global variable. " 182 "This is dangerous because the address will become " 183 "invalid after returning from the function")); 184 185 for (unsigned i = 0, e = cb.V.size(); i != e; ++i) { 186 // Generate a report for this bug. 187 llvm::SmallString<512> buf; 188 llvm::raw_svector_ostream os(buf); 189 SourceRange range = GenName(os, cb.V[i].second, 190 Eng.getContext().getSourceManager()); 191 os << " is still referred to by the global variable '"; 192 const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion()); 193 os << VR->getDecl()->getNameAsString() 194 << "' upon returning to the caller. This will be a dangling reference"; 195 RangedBugReport *report = new RangedBugReport(*BT_stackleak, os.str(), N); 196 if (range.isValid()) 197 report->addRange(range); 198 199 Eng.getBugReporter().EmitReport(report); 200 } 201} 202 203void ento::registerStackAddrEscapeChecker(CheckerManager &mgr) { 204 mgr.registerChecker<StackAddrEscapeChecker>(); 205} 206