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" 25 using namespace clang; 26 using namespace ento; 27 28 namespace { 29 class StackAddrEscapeChecker : public Checker< check::PreStmt<ReturnStmt>, 30 check::EndFunction > { 31 mutable std::unique_ptr<BuiltinBug> BT_stackleak; 32 mutable std::unique_ptr<BuiltinBug> BT_returnstack; 33 34 public: 35 void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; 36 void checkEndFunction(CheckerContext &Ctx) const; 37 private: 38 void EmitStackError(CheckerContext &C, const MemRegion *R, 39 const Expr *RetE) const; 40 static SourceRange genName(raw_ostream &os, const MemRegion *R, 41 ASTContext &Ctx); 42 }; 43 } 44 45 SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R, 46 ASTContext &Ctx) { 47 // Get the base region, stripping away fields and elements. 48 R = R->getBaseRegion(); 49 SourceManager &SM = Ctx.getSourceManager(); 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 QualType Ty = TOR->getValueType().getLocalUnqualifiedType(); 83 os << "stack memory associated with temporary object of type '"; 84 Ty.print(os, Ctx.getPrintingPolicy()); 85 os << "'"; 86 range = TOR->getExpr()->getSourceRange(); 87 } 88 else { 89 llvm_unreachable("Invalid region in ReturnStackAddressChecker."); 90 } 91 92 return range; 93 } 94 95 void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R, 96 const Expr *RetE) const { 97 ExplodedNode *N = C.generateErrorNode(); 98 99 if (!N) 100 return; 101 102 if (!BT_returnstack) 103 BT_returnstack.reset( 104 new BuiltinBug(this, "Return of address to stack-allocated memory")); 105 106 // Generate a report for this bug. 107 SmallString<512> buf; 108 llvm::raw_svector_ostream os(buf); 109 SourceRange range = genName(os, R, C.getASTContext()); 110 os << " returned to caller"; 111 auto report = llvm::make_unique<BugReport>(*BT_returnstack, os.str(), N); 112 report->addRange(RetE->getSourceRange()); 113 if (range.isValid()) 114 report->addRange(range); 115 116 C.emitReport(std::move(report)); 117 } 118 119 void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, 120 CheckerContext &C) const { 121 122 const Expr *RetE = RS->getRetValue(); 123 if (!RetE) 124 return; 125 RetE = RetE->IgnoreParens(); 126 127 const LocationContext *LCtx = C.getLocationContext(); 128 SVal V = C.getState()->getSVal(RetE, LCtx); 129 const MemRegion *R = V.getAsRegion(); 130 131 if (!R) 132 return; 133 134 const StackSpaceRegion *SS = 135 dyn_cast_or_null<StackSpaceRegion>(R->getMemorySpace()); 136 137 if (!SS) 138 return; 139 140 // Return stack memory in an ancestor stack frame is fine. 141 const StackFrameContext *CurFrame = LCtx->getCurrentStackFrame(); 142 const StackFrameContext *MemFrame = SS->getStackFrame(); 143 if (MemFrame != CurFrame) 144 return; 145 146 // Automatic reference counting automatically copies blocks. 147 if (C.getASTContext().getLangOpts().ObjCAutoRefCount && 148 isa<BlockDataRegion>(R)) 149 return; 150 151 // Returning a record by value is fine. (In this case, the returned 152 // expression will be a copy-constructor, possibly wrapped in an 153 // ExprWithCleanups node.) 154 if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE)) 155 RetE = Cleanup->getSubExpr(); 156 if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType()) 157 return; 158 159 // The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied 160 // so the stack address is not escaping here. 161 if (auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) { 162 if (isa<BlockDataRegion>(R) && 163 ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) { 164 return; 165 } 166 } 167 168 EmitStackError(C, R, RetE); 169 } 170 171 void StackAddrEscapeChecker::checkEndFunction(CheckerContext &Ctx) const { 172 ProgramStateRef state = Ctx.getState(); 173 174 // Iterate over all bindings to global variables and see if it contains 175 // a memory region in the stack space. 176 class CallBack : public StoreManager::BindingsHandler { 177 private: 178 CheckerContext &Ctx; 179 const StackFrameContext *CurSFC; 180 public: 181 SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V; 182 183 CallBack(CheckerContext &CC) : 184 Ctx(CC), 185 CurSFC(CC.getLocationContext()->getCurrentStackFrame()) 186 {} 187 188 bool HandleBinding(StoreManager &SMgr, Store store, 189 const MemRegion *region, SVal val) override { 190 191 if (!isa<GlobalsSpaceRegion>(region->getMemorySpace())) 192 return true; 193 194 const MemRegion *vR = val.getAsRegion(); 195 if (!vR) 196 return true; 197 198 // Under automated retain release, it is okay to assign a block 199 // directly to a global variable. 200 if (Ctx.getASTContext().getLangOpts().ObjCAutoRefCount && 201 isa<BlockDataRegion>(vR)) 202 return true; 203 204 if (const StackSpaceRegion *SSR = 205 dyn_cast<StackSpaceRegion>(vR->getMemorySpace())) { 206 // If the global variable holds a location in the current stack frame, 207 // record the binding to emit a warning. 208 if (SSR->getStackFrame() == CurSFC) 209 V.push_back(std::make_pair(region, vR)); 210 } 211 212 return true; 213 } 214 }; 215 216 CallBack cb(Ctx); 217 state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb); 218 219 if (cb.V.empty()) 220 return; 221 222 // Generate an error node. 223 ExplodedNode *N = Ctx.generateNonFatalErrorNode(state); 224 if (!N) 225 return; 226 227 if (!BT_stackleak) 228 BT_stackleak.reset( 229 new BuiltinBug(this, "Stack address stored into global variable", 230 "Stack address was saved into a global variable. " 231 "This is dangerous because the address will become " 232 "invalid after returning from the function")); 233 234 for (unsigned i = 0, e = cb.V.size(); i != e; ++i) { 235 // Generate a report for this bug. 236 SmallString<512> buf; 237 llvm::raw_svector_ostream os(buf); 238 SourceRange range = genName(os, cb.V[i].second, Ctx.getASTContext()); 239 os << " is still referred to by the "; 240 if (isa<StaticGlobalSpaceRegion>(cb.V[i].first->getMemorySpace())) 241 os << "static"; 242 else 243 os << "global"; 244 os << " variable '"; 245 const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion()); 246 os << *VR->getDecl() 247 << "' upon returning to the caller. This will be a dangling reference"; 248 auto report = llvm::make_unique<BugReport>(*BT_stackleak, os.str(), N); 249 if (range.isValid()) 250 report->addRange(range); 251 252 Ctx.emitReport(std::move(report)); 253 } 254 } 255 256 void ento::registerStackAddrEscapeChecker(CheckerManager &mgr) { 257 mgr.registerChecker<StackAddrEscapeChecker>(); 258 } 259