1219069Sdim//=== StackAddrEscapeChecker.cpp ----------------------------------*- C++ -*--// 2219069Sdim// 3219069Sdim// The LLVM Compiler Infrastructure 4219069Sdim// 5219069Sdim// This file is distributed under the University of Illinois Open Source 6219069Sdim// License. See LICENSE.TXT for details. 7219069Sdim// 8219069Sdim//===----------------------------------------------------------------------===// 9219069Sdim// 10219069Sdim// This file defines stack address leak checker, which checks if an invalid 11219069Sdim// stack address is stored into a global or heap location. See CERT DCL30-C. 12219069Sdim// 13219069Sdim//===----------------------------------------------------------------------===// 14219069Sdim 15219069Sdim#include "ClangSACheckers.h" 16252723Sdim#include "clang/AST/ExprCXX.h" 17252723Sdim#include "clang/Basic/SourceManager.h" 18252723Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 19221345Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 20219069Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h" 21219069Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 22226890Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 23219069Sdim#include "llvm/ADT/SmallString.h" 24252723Sdim#include "llvm/Support/raw_ostream.h" 25219069Sdimusing namespace clang; 26219069Sdimusing namespace ento; 27219069Sdim 28219069Sdimnamespace { 29221345Sdimclass StackAddrEscapeChecker : public Checker< check::PreStmt<ReturnStmt>, 30252723Sdim check::EndFunction > { 31235633Sdim mutable OwningPtr<BuiltinBug> BT_stackleak; 32235633Sdim mutable OwningPtr<BuiltinBug> BT_returnstack; 33219069Sdim 34219069Sdimpublic: 35219069Sdim void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; 36252723Sdim void checkEndFunction(CheckerContext &Ctx) const; 37219069Sdimprivate: 38219069Sdim void EmitStackError(CheckerContext &C, const MemRegion *R, 39219069Sdim const Expr *RetE) const; 40252723Sdim static SourceRange genName(raw_ostream &os, const MemRegion *R, 41252723Sdim ASTContext &Ctx); 42219069Sdim}; 43219069Sdim} 44219069Sdim 45252723SdimSourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R, 46252723Sdim ASTContext &Ctx) { 47219069Sdim // Get the base region, stripping away fields and elements. 48219069Sdim R = R->getBaseRegion(); 49252723Sdim SourceManager &SM = Ctx.getSourceManager(); 50219069Sdim SourceRange range; 51219069Sdim os << "Address of "; 52219069Sdim 53219069Sdim // Check if the region is a compound literal. 54219069Sdim if (const CompoundLiteralRegion* CR = dyn_cast<CompoundLiteralRegion>(R)) { 55226890Sdim const CompoundLiteralExpr *CL = CR->getLiteralExpr(); 56219069Sdim os << "stack memory associated with a compound literal " 57219069Sdim "declared on line " 58226890Sdim << SM.getExpansionLineNumber(CL->getLocStart()) 59219069Sdim << " returned to caller"; 60219069Sdim range = CL->getSourceRange(); 61219069Sdim } 62219069Sdim else if (const AllocaRegion* AR = dyn_cast<AllocaRegion>(R)) { 63226890Sdim const Expr *ARE = AR->getExpr(); 64219069Sdim SourceLocation L = ARE->getLocStart(); 65219069Sdim range = ARE->getSourceRange(); 66219069Sdim os << "stack memory allocated by call to alloca() on line " 67226890Sdim << SM.getExpansionLineNumber(L); 68219069Sdim } 69219069Sdim else if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) { 70219069Sdim const BlockDecl *BD = BR->getCodeRegion()->getDecl(); 71219069Sdim SourceLocation L = BD->getLocStart(); 72219069Sdim range = BD->getSourceRange(); 73219069Sdim os << "stack-allocated block declared on line " 74226890Sdim << SM.getExpansionLineNumber(L); 75219069Sdim } 76219069Sdim else if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { 77219069Sdim os << "stack memory associated with local variable '" 78219069Sdim << VR->getString() << '\''; 79219069Sdim range = VR->getDecl()->getSourceRange(); 80219069Sdim } 81226890Sdim else if (const CXXTempObjectRegion *TOR = dyn_cast<CXXTempObjectRegion>(R)) { 82252723Sdim QualType Ty = TOR->getValueType().getLocalUnqualifiedType(); 83252723Sdim os << "stack memory associated with temporary object of type '"; 84252723Sdim Ty.print(os, Ctx.getPrintingPolicy()); 85252723Sdim os << "'"; 86226890Sdim range = TOR->getExpr()->getSourceRange(); 87226890Sdim } 88219069Sdim else { 89226890Sdim llvm_unreachable("Invalid region in ReturnStackAddressChecker."); 90219069Sdim } 91219069Sdim 92219069Sdim return range; 93219069Sdim} 94219069Sdim 95219069Sdimvoid StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R, 96219069Sdim const Expr *RetE) const { 97219069Sdim ExplodedNode *N = C.generateSink(); 98219069Sdim 99219069Sdim if (!N) 100219069Sdim return; 101219069Sdim 102219069Sdim if (!BT_returnstack) 103219069Sdim BT_returnstack.reset( 104219069Sdim new BuiltinBug("Return of address to stack-allocated memory")); 105219069Sdim 106219069Sdim // Generate a report for this bug. 107235633Sdim SmallString<512> buf; 108219069Sdim llvm::raw_svector_ostream os(buf); 109252723Sdim SourceRange range = genName(os, R, C.getASTContext()); 110219069Sdim os << " returned to caller"; 111226890Sdim BugReport *report = new BugReport(*BT_returnstack, os.str(), N); 112219069Sdim report->addRange(RetE->getSourceRange()); 113219069Sdim if (range.isValid()) 114219069Sdim report->addRange(range); 115219069Sdim 116245431Sdim C.emitReport(report); 117219069Sdim} 118219069Sdim 119219069Sdimvoid StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, 120235633Sdim CheckerContext &C) const { 121219069Sdim 122219069Sdim const Expr *RetE = RS->getRetValue(); 123219069Sdim if (!RetE) 124219069Sdim return; 125245431Sdim RetE = RetE->IgnoreParens(); 126245431Sdim 127245431Sdim const LocationContext *LCtx = C.getLocationContext(); 128245431Sdim SVal V = C.getState()->getSVal(RetE, LCtx); 129219069Sdim const MemRegion *R = V.getAsRegion(); 130219069Sdim 131235633Sdim if (!R) 132235633Sdim return; 133219069Sdim 134235633Sdim const StackSpaceRegion *SS = 135235633Sdim dyn_cast_or_null<StackSpaceRegion>(R->getMemorySpace()); 136235633Sdim 137235633Sdim if (!SS) 138235633Sdim return; 139224145Sdim 140235633Sdim // Return stack memory in an ancestor stack frame is fine. 141245431Sdim const StackFrameContext *CurFrame = LCtx->getCurrentStackFrame(); 142245431Sdim const StackFrameContext *MemFrame = SS->getStackFrame(); 143245431Sdim if (MemFrame != CurFrame) 144219069Sdim return; 145235633Sdim 146235633Sdim // Automatic reference counting automatically copies blocks. 147235633Sdim if (C.getASTContext().getLangOpts().ObjCAutoRefCount && 148235633Sdim isa<BlockDataRegion>(R)) 149235633Sdim return; 150235633Sdim 151245431Sdim // Returning a record by value is fine. (In this case, the returned 152245431Sdim // expression will be a copy-constructor, possibly wrapped in an 153245431Sdim // ExprWithCleanups node.) 154245431Sdim if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE)) 155245431Sdim RetE = Cleanup->getSubExpr(); 156245431Sdim if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType()) 157245431Sdim return; 158245431Sdim 159235633Sdim EmitStackError(C, R, RetE); 160219069Sdim} 161219069Sdim 162252723Sdimvoid StackAddrEscapeChecker::checkEndFunction(CheckerContext &Ctx) const { 163235633Sdim ProgramStateRef state = Ctx.getState(); 164219069Sdim 165219069Sdim // Iterate over all bindings to global variables and see if it contains 166219069Sdim // a memory region in the stack space. 167219069Sdim class CallBack : public StoreManager::BindingsHandler { 168219069Sdim private: 169235633Sdim CheckerContext &Ctx; 170219069Sdim const StackFrameContext *CurSFC; 171219069Sdim public: 172226890Sdim SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V; 173219069Sdim 174235633Sdim CallBack(CheckerContext &CC) : 175235633Sdim Ctx(CC), 176235633Sdim CurSFC(CC.getLocationContext()->getCurrentStackFrame()) 177235633Sdim {} 178219069Sdim 179219069Sdim bool HandleBinding(StoreManager &SMgr, Store store, 180219069Sdim const MemRegion *region, SVal val) { 181219069Sdim 182219069Sdim if (!isa<GlobalsSpaceRegion>(region->getMemorySpace())) 183219069Sdim return true; 184219069Sdim 185219069Sdim const MemRegion *vR = val.getAsRegion(); 186219069Sdim if (!vR) 187219069Sdim return true; 188224145Sdim 189224145Sdim // Under automated retain release, it is okay to assign a block 190224145Sdim // directly to a global variable. 191235633Sdim if (Ctx.getASTContext().getLangOpts().ObjCAutoRefCount && 192224145Sdim isa<BlockDataRegion>(vR)) 193224145Sdim return true; 194224145Sdim 195219069Sdim if (const StackSpaceRegion *SSR = 196219069Sdim dyn_cast<StackSpaceRegion>(vR->getMemorySpace())) { 197219069Sdim // If the global variable holds a location in the current stack frame, 198219069Sdim // record the binding to emit a warning. 199219069Sdim if (SSR->getStackFrame() == CurSFC) 200219069Sdim V.push_back(std::make_pair(region, vR)); 201219069Sdim } 202219069Sdim 203219069Sdim return true; 204219069Sdim } 205219069Sdim }; 206219069Sdim 207235633Sdim CallBack cb(Ctx); 208219069Sdim state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb); 209219069Sdim 210219069Sdim if (cb.V.empty()) 211219069Sdim return; 212219069Sdim 213219069Sdim // Generate an error node. 214235633Sdim ExplodedNode *N = Ctx.addTransition(state); 215219069Sdim if (!N) 216219069Sdim return; 217219069Sdim 218219069Sdim if (!BT_stackleak) 219219069Sdim BT_stackleak.reset( 220219069Sdim new BuiltinBug("Stack address stored into global variable", 221219069Sdim "Stack address was saved into a global variable. " 222219069Sdim "This is dangerous because the address will become " 223219069Sdim "invalid after returning from the function")); 224219069Sdim 225219069Sdim for (unsigned i = 0, e = cb.V.size(); i != e; ++i) { 226219069Sdim // Generate a report for this bug. 227235633Sdim SmallString<512> buf; 228219069Sdim llvm::raw_svector_ostream os(buf); 229252723Sdim SourceRange range = genName(os, cb.V[i].second, Ctx.getASTContext()); 230219069Sdim os << " is still referred to by the global variable '"; 231219069Sdim const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion()); 232226890Sdim os << *VR->getDecl() 233219069Sdim << "' upon returning to the caller. This will be a dangling reference"; 234226890Sdim BugReport *report = new BugReport(*BT_stackleak, os.str(), N); 235219069Sdim if (range.isValid()) 236219069Sdim report->addRange(range); 237219069Sdim 238245431Sdim Ctx.emitReport(report); 239219069Sdim } 240219069Sdim} 241219069Sdim 242219069Sdimvoid ento::registerStackAddrEscapeChecker(CheckerManager &mgr) { 243219069Sdim mgr.registerChecker<StackAddrEscapeChecker>(); 244219069Sdim} 245