clang API Documentation
00001 //=== StackAddrEscapeChecker.cpp ----------------------------------*- C++ -*--// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This file defines stack address leak checker, which checks if an invalid 00011 // stack address is stored into a global or heap location. See CERT DCL30-C. 00012 // 00013 //===----------------------------------------------------------------------===// 00014 00015 #include "ClangSACheckers.h" 00016 #include "clang/AST/ExprCXX.h" 00017 #include "clang/Basic/SourceManager.h" 00018 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 00019 #include "clang/StaticAnalyzer/Core/Checker.h" 00020 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 00021 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 00022 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 00023 #include "llvm/ADT/SmallString.h" 00024 #include "llvm/Support/raw_ostream.h" 00025 using namespace clang; 00026 using namespace ento; 00027 00028 namespace { 00029 class StackAddrEscapeChecker : public Checker< check::PreStmt<ReturnStmt>, 00030 check::EndFunction > { 00031 mutable std::unique_ptr<BuiltinBug> BT_stackleak; 00032 mutable std::unique_ptr<BuiltinBug> BT_returnstack; 00033 00034 public: 00035 void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; 00036 void checkEndFunction(CheckerContext &Ctx) const; 00037 private: 00038 void EmitStackError(CheckerContext &C, const MemRegion *R, 00039 const Expr *RetE) const; 00040 static SourceRange genName(raw_ostream &os, const MemRegion *R, 00041 ASTContext &Ctx); 00042 }; 00043 } 00044 00045 SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R, 00046 ASTContext &Ctx) { 00047 // Get the base region, stripping away fields and elements. 00048 R = R->getBaseRegion(); 00049 SourceManager &SM = Ctx.getSourceManager(); 00050 SourceRange range; 00051 os << "Address of "; 00052 00053 // Check if the region is a compound literal. 00054 if (const CompoundLiteralRegion* CR = dyn_cast<CompoundLiteralRegion>(R)) { 00055 const CompoundLiteralExpr *CL = CR->getLiteralExpr(); 00056 os << "stack memory associated with a compound literal " 00057 "declared on line " 00058 << SM.getExpansionLineNumber(CL->getLocStart()) 00059 << " returned to caller"; 00060 range = CL->getSourceRange(); 00061 } 00062 else if (const AllocaRegion* AR = dyn_cast<AllocaRegion>(R)) { 00063 const Expr *ARE = AR->getExpr(); 00064 SourceLocation L = ARE->getLocStart(); 00065 range = ARE->getSourceRange(); 00066 os << "stack memory allocated by call to alloca() on line " 00067 << SM.getExpansionLineNumber(L); 00068 } 00069 else if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) { 00070 const BlockDecl *BD = BR->getCodeRegion()->getDecl(); 00071 SourceLocation L = BD->getLocStart(); 00072 range = BD->getSourceRange(); 00073 os << "stack-allocated block declared on line " 00074 << SM.getExpansionLineNumber(L); 00075 } 00076 else if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { 00077 os << "stack memory associated with local variable '" 00078 << VR->getString() << '\''; 00079 range = VR->getDecl()->getSourceRange(); 00080 } 00081 else if (const CXXTempObjectRegion *TOR = dyn_cast<CXXTempObjectRegion>(R)) { 00082 QualType Ty = TOR->getValueType().getLocalUnqualifiedType(); 00083 os << "stack memory associated with temporary object of type '"; 00084 Ty.print(os, Ctx.getPrintingPolicy()); 00085 os << "'"; 00086 range = TOR->getExpr()->getSourceRange(); 00087 } 00088 else { 00089 llvm_unreachable("Invalid region in ReturnStackAddressChecker."); 00090 } 00091 00092 return range; 00093 } 00094 00095 void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R, 00096 const Expr *RetE) const { 00097 ExplodedNode *N = C.generateSink(); 00098 00099 if (!N) 00100 return; 00101 00102 if (!BT_returnstack) 00103 BT_returnstack.reset( 00104 new BuiltinBug(this, "Return of address to stack-allocated memory")); 00105 00106 // Generate a report for this bug. 00107 SmallString<512> buf; 00108 llvm::raw_svector_ostream os(buf); 00109 SourceRange range = genName(os, R, C.getASTContext()); 00110 os << " returned to caller"; 00111 BugReport *report = new BugReport(*BT_returnstack, os.str(), N); 00112 report->addRange(RetE->getSourceRange()); 00113 if (range.isValid()) 00114 report->addRange(range); 00115 00116 C.emitReport(report); 00117 } 00118 00119 void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, 00120 CheckerContext &C) const { 00121 00122 const Expr *RetE = RS->getRetValue(); 00123 if (!RetE) 00124 return; 00125 RetE = RetE->IgnoreParens(); 00126 00127 const LocationContext *LCtx = C.getLocationContext(); 00128 SVal V = C.getState()->getSVal(RetE, LCtx); 00129 const MemRegion *R = V.getAsRegion(); 00130 00131 if (!R) 00132 return; 00133 00134 const StackSpaceRegion *SS = 00135 dyn_cast_or_null<StackSpaceRegion>(R->getMemorySpace()); 00136 00137 if (!SS) 00138 return; 00139 00140 // Return stack memory in an ancestor stack frame is fine. 00141 const StackFrameContext *CurFrame = LCtx->getCurrentStackFrame(); 00142 const StackFrameContext *MemFrame = SS->getStackFrame(); 00143 if (MemFrame != CurFrame) 00144 return; 00145 00146 // Automatic reference counting automatically copies blocks. 00147 if (C.getASTContext().getLangOpts().ObjCAutoRefCount && 00148 isa<BlockDataRegion>(R)) 00149 return; 00150 00151 // Returning a record by value is fine. (In this case, the returned 00152 // expression will be a copy-constructor, possibly wrapped in an 00153 // ExprWithCleanups node.) 00154 if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE)) 00155 RetE = Cleanup->getSubExpr(); 00156 if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType()) 00157 return; 00158 00159 EmitStackError(C, R, RetE); 00160 } 00161 00162 void StackAddrEscapeChecker::checkEndFunction(CheckerContext &Ctx) const { 00163 ProgramStateRef state = Ctx.getState(); 00164 00165 // Iterate over all bindings to global variables and see if it contains 00166 // a memory region in the stack space. 00167 class CallBack : public StoreManager::BindingsHandler { 00168 private: 00169 CheckerContext &Ctx; 00170 const StackFrameContext *CurSFC; 00171 public: 00172 SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V; 00173 00174 CallBack(CheckerContext &CC) : 00175 Ctx(CC), 00176 CurSFC(CC.getLocationContext()->getCurrentStackFrame()) 00177 {} 00178 00179 bool HandleBinding(StoreManager &SMgr, Store store, 00180 const MemRegion *region, SVal val) override { 00181 00182 if (!isa<GlobalsSpaceRegion>(region->getMemorySpace())) 00183 return true; 00184 00185 const MemRegion *vR = val.getAsRegion(); 00186 if (!vR) 00187 return true; 00188 00189 // Under automated retain release, it is okay to assign a block 00190 // directly to a global variable. 00191 if (Ctx.getASTContext().getLangOpts().ObjCAutoRefCount && 00192 isa<BlockDataRegion>(vR)) 00193 return true; 00194 00195 if (const StackSpaceRegion *SSR = 00196 dyn_cast<StackSpaceRegion>(vR->getMemorySpace())) { 00197 // If the global variable holds a location in the current stack frame, 00198 // record the binding to emit a warning. 00199 if (SSR->getStackFrame() == CurSFC) 00200 V.push_back(std::make_pair(region, vR)); 00201 } 00202 00203 return true; 00204 } 00205 }; 00206 00207 CallBack cb(Ctx); 00208 state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb); 00209 00210 if (cb.V.empty()) 00211 return; 00212 00213 // Generate an error node. 00214 ExplodedNode *N = Ctx.addTransition(state); 00215 if (!N) 00216 return; 00217 00218 if (!BT_stackleak) 00219 BT_stackleak.reset( 00220 new BuiltinBug(this, "Stack address stored into global variable", 00221 "Stack address was saved into a global variable. " 00222 "This is dangerous because the address will become " 00223 "invalid after returning from the function")); 00224 00225 for (unsigned i = 0, e = cb.V.size(); i != e; ++i) { 00226 // Generate a report for this bug. 00227 SmallString<512> buf; 00228 llvm::raw_svector_ostream os(buf); 00229 SourceRange range = genName(os, cb.V[i].second, Ctx.getASTContext()); 00230 os << " is still referred to by the global variable '"; 00231 const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion()); 00232 os << *VR->getDecl() 00233 << "' upon returning to the caller. This will be a dangling reference"; 00234 BugReport *report = new BugReport(*BT_stackleak, os.str(), N); 00235 if (range.isValid()) 00236 report->addRange(range); 00237 00238 Ctx.emitReport(report); 00239 } 00240 } 00241 00242 void ento::registerStackAddrEscapeChecker(CheckerManager &mgr) { 00243 mgr.registerChecker<StackAddrEscapeChecker>(); 00244 }