clang API Documentation

StackAddrEscapeChecker.cpp
Go to the documentation of this file.
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 }