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