Home | History | Annotate | Line # | Download | only in Checkers
      1 //=== StackAddrEscapeChecker.cpp ----------------------------------*- C++ -*--//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 //
      9 // This file defines stack address leak checker, which checks if an invalid
     10 // stack address is stored into a global or heap location. See CERT DCL30-C.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
     15 #include "clang/AST/ExprCXX.h"
     16 #include "clang/Basic/SourceManager.h"
     17 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
     18 #include "clang/StaticAnalyzer/Core/Checker.h"
     19 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
     20 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.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
     30     : public Checker<check::PreCall, check::PreStmt<ReturnStmt>,
     31                      check::EndFunction> {
     32   mutable IdentifierInfo *dispatch_semaphore_tII;
     33   mutable std::unique_ptr<BuiltinBug> BT_stackleak;
     34   mutable std::unique_ptr<BuiltinBug> BT_returnstack;
     35   mutable std::unique_ptr<BuiltinBug> BT_capturedstackasync;
     36   mutable std::unique_ptr<BuiltinBug> BT_capturedstackret;
     37 
     38 public:
     39   enum CheckKind {
     40     CK_StackAddrEscapeChecker,
     41     CK_StackAddrAsyncEscapeChecker,
     42     CK_NumCheckKinds
     43   };
     44 
     45   DefaultBool ChecksEnabled[CK_NumCheckKinds];
     46   CheckerNameRef CheckNames[CK_NumCheckKinds];
     47 
     48   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
     49   void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
     50   void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const;
     51 
     52 private:
     53   void checkReturnedBlockCaptures(const BlockDataRegion &B,
     54                                   CheckerContext &C) const;
     55   void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B,
     56                                        CheckerContext &C) const;
     57   void EmitStackError(CheckerContext &C, const MemRegion *R,
     58                       const Expr *RetE) const;
     59   bool isSemaphoreCaptured(const BlockDecl &B) const;
     60   static SourceRange genName(raw_ostream &os, const MemRegion *R,
     61                              ASTContext &Ctx);
     62   static SmallVector<const MemRegion *, 4>
     63   getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C);
     64   static bool isArcManagedBlock(const MemRegion *R, CheckerContext &C);
     65   static bool isNotInCurrentFrame(const MemRegion *R, CheckerContext &C);
     66 };
     67 } // namespace
     68 
     69 SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R,
     70                                             ASTContext &Ctx) {
     71   // Get the base region, stripping away fields and elements.
     72   R = R->getBaseRegion();
     73   SourceManager &SM = Ctx.getSourceManager();
     74   SourceRange range;
     75   os << "Address of ";
     76 
     77   // Check if the region is a compound literal.
     78   if (const auto *CR = dyn_cast<CompoundLiteralRegion>(R)) {
     79     const CompoundLiteralExpr *CL = CR->getLiteralExpr();
     80     os << "stack memory associated with a compound literal "
     81           "declared on line "
     82        << SM.getExpansionLineNumber(CL->getBeginLoc()) << " returned to caller";
     83     range = CL->getSourceRange();
     84   } else if (const auto *AR = dyn_cast<AllocaRegion>(R)) {
     85     const Expr *ARE = AR->getExpr();
     86     SourceLocation L = ARE->getBeginLoc();
     87     range = ARE->getSourceRange();
     88     os << "stack memory allocated by call to alloca() on line "
     89        << SM.getExpansionLineNumber(L);
     90   } else if (const auto *BR = dyn_cast<BlockDataRegion>(R)) {
     91     const BlockDecl *BD = BR->getCodeRegion()->getDecl();
     92     SourceLocation L = BD->getBeginLoc();
     93     range = BD->getSourceRange();
     94     os << "stack-allocated block declared on line "
     95        << SM.getExpansionLineNumber(L);
     96   } else if (const auto *VR = dyn_cast<VarRegion>(R)) {
     97     os << "stack memory associated with local variable '" << VR->getString()
     98        << '\'';
     99     range = VR->getDecl()->getSourceRange();
    100   } else if (const auto *TOR = dyn_cast<CXXTempObjectRegion>(R)) {
    101     QualType Ty = TOR->getValueType().getLocalUnqualifiedType();
    102     os << "stack memory associated with temporary object of type '";
    103     Ty.print(os, Ctx.getPrintingPolicy());
    104     os << "'";
    105     range = TOR->getExpr()->getSourceRange();
    106   } else {
    107     llvm_unreachable("Invalid region in ReturnStackAddressChecker.");
    108   }
    109 
    110   return range;
    111 }
    112 
    113 bool StackAddrEscapeChecker::isArcManagedBlock(const MemRegion *R,
    114                                                CheckerContext &C) {
    115   assert(R && "MemRegion should not be null");
    116   return C.getASTContext().getLangOpts().ObjCAutoRefCount &&
    117          isa<BlockDataRegion>(R);
    118 }
    119 
    120 bool StackAddrEscapeChecker::isNotInCurrentFrame(const MemRegion *R,
    121                                                  CheckerContext &C) {
    122   const StackSpaceRegion *S = cast<StackSpaceRegion>(R->getMemorySpace());
    123   return S->getStackFrame() != C.getStackFrame();
    124 }
    125 
    126 bool StackAddrEscapeChecker::isSemaphoreCaptured(const BlockDecl &B) const {
    127   if (!dispatch_semaphore_tII)
    128     dispatch_semaphore_tII = &B.getASTContext().Idents.get("dispatch_semaphore_t");
    129   for (const auto &C : B.captures()) {
    130     const auto *T = C.getVariable()->getType()->getAs<TypedefType>();
    131     if (T && T->getDecl()->getIdentifier() == dispatch_semaphore_tII)
    132       return true;
    133   }
    134   return false;
    135 }
    136 
    137 SmallVector<const MemRegion *, 4>
    138 StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B,
    139                                                 CheckerContext &C) {
    140   SmallVector<const MemRegion *, 4> Regions;
    141   BlockDataRegion::referenced_vars_iterator I = B.referenced_vars_begin();
    142   BlockDataRegion::referenced_vars_iterator E = B.referenced_vars_end();
    143   for (; I != E; ++I) {
    144     SVal Val = C.getState()->getSVal(I.getCapturedRegion());
    145     const MemRegion *Region = Val.getAsRegion();
    146     if (Region && isa<StackSpaceRegion>(Region->getMemorySpace()))
    147       Regions.push_back(Region);
    148   }
    149   return Regions;
    150 }
    151 
    152 void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,
    153                                             const MemRegion *R,
    154                                             const Expr *RetE) const {
    155   ExplodedNode *N = C.generateNonFatalErrorNode();
    156   if (!N)
    157     return;
    158   if (!BT_returnstack)
    159     BT_returnstack = std::make_unique<BuiltinBug>(
    160         CheckNames[CK_StackAddrEscapeChecker],
    161         "Return of address to stack-allocated memory");
    162   // Generate a report for this bug.
    163   SmallString<128> buf;
    164   llvm::raw_svector_ostream os(buf);
    165   SourceRange range = genName(os, R, C.getASTContext());
    166   os << " returned to caller";
    167   auto report =
    168       std::make_unique<PathSensitiveBugReport>(*BT_returnstack, os.str(), N);
    169   report->addRange(RetE->getSourceRange());
    170   if (range.isValid())
    171     report->addRange(range);
    172   C.emitReport(std::move(report));
    173 }
    174 
    175 void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
    176     const BlockDataRegion &B, CheckerContext &C) const {
    177   // There is a not-too-uncommon idiom
    178   // where a block passed to dispatch_async captures a semaphore
    179   // and then the thread (which called dispatch_async) is blocked on waiting
    180   // for the completion of the execution of the block
    181   // via dispatch_semaphore_wait. To avoid false-positives (for now)
    182   // we ignore all the blocks which have captured
    183   // a variable of the type "dispatch_semaphore_t".
    184   if (isSemaphoreCaptured(*B.getDecl()))
    185     return;
    186   for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
    187     // The block passed to dispatch_async may capture another block
    188     // created on the stack. However, there is no leak in this situaton,
    189     // no matter if ARC or no ARC is enabled:
    190     // dispatch_async copies the passed "outer" block (via Block_copy)
    191     // and if the block has captured another "inner" block,
    192     // the "inner" block will be copied as well.
    193     if (isa<BlockDataRegion>(Region))
    194       continue;
    195     ExplodedNode *N = C.generateNonFatalErrorNode();
    196     if (!N)
    197       continue;
    198     if (!BT_capturedstackasync)
    199       BT_capturedstackasync = std::make_unique<BuiltinBug>(
    200           CheckNames[CK_StackAddrAsyncEscapeChecker],
    201           "Address of stack-allocated memory is captured");
    202     SmallString<128> Buf;
    203     llvm::raw_svector_ostream Out(Buf);
    204     SourceRange Range = genName(Out, Region, C.getASTContext());
    205     Out << " is captured by an asynchronously-executed block";
    206     auto Report = std::make_unique<PathSensitiveBugReport>(
    207         *BT_capturedstackasync, Out.str(), N);
    208     if (Range.isValid())
    209       Report->addRange(Range);
    210     C.emitReport(std::move(Report));
    211   }
    212 }
    213 
    214 void StackAddrEscapeChecker::checkReturnedBlockCaptures(
    215     const BlockDataRegion &B, CheckerContext &C) const {
    216   for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
    217     if (isArcManagedBlock(Region, C) || isNotInCurrentFrame(Region, C))
    218       continue;
    219     ExplodedNode *N = C.generateNonFatalErrorNode();
    220     if (!N)
    221       continue;
    222     if (!BT_capturedstackret)
    223       BT_capturedstackret = std::make_unique<BuiltinBug>(
    224           CheckNames[CK_StackAddrEscapeChecker],
    225           "Address of stack-allocated memory is captured");
    226     SmallString<128> Buf;
    227     llvm::raw_svector_ostream Out(Buf);
    228     SourceRange Range = genName(Out, Region, C.getASTContext());
    229     Out << " is captured by a returned block";
    230     auto Report = std::make_unique<PathSensitiveBugReport>(*BT_capturedstackret,
    231                                                            Out.str(), N);
    232     if (Range.isValid())
    233       Report->addRange(Range);
    234     C.emitReport(std::move(Report));
    235   }
    236 }
    237 
    238 void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
    239                                           CheckerContext &C) const {
    240   if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
    241     return;
    242   if (!Call.isGlobalCFunction("dispatch_after") &&
    243       !Call.isGlobalCFunction("dispatch_async"))
    244     return;
    245   for (unsigned Idx = 0, NumArgs = Call.getNumArgs(); Idx < NumArgs; ++Idx) {
    246     if (const BlockDataRegion *B = dyn_cast_or_null<BlockDataRegion>(
    247             Call.getArgSVal(Idx).getAsRegion()))
    248       checkAsyncExecutedBlockCaptures(*B, C);
    249   }
    250 }
    251 
    252 void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
    253                                           CheckerContext &C) const {
    254   if (!ChecksEnabled[CK_StackAddrEscapeChecker])
    255     return;
    256 
    257   const Expr *RetE = RS->getRetValue();
    258   if (!RetE)
    259     return;
    260   RetE = RetE->IgnoreParens();
    261 
    262   SVal V = C.getSVal(RetE);
    263   const MemRegion *R = V.getAsRegion();
    264   if (!R)
    265     return;
    266 
    267   if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R))
    268     checkReturnedBlockCaptures(*B, C);
    269 
    270   if (!isa<StackSpaceRegion>(R->getMemorySpace()) ||
    271       isNotInCurrentFrame(R, C) || isArcManagedBlock(R, C))
    272     return;
    273 
    274   // Returning a record by value is fine. (In this case, the returned
    275   // expression will be a copy-constructor, possibly wrapped in an
    276   // ExprWithCleanups node.)
    277   if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE))
    278     RetE = Cleanup->getSubExpr();
    279   if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType())
    280     return;
    281 
    282   // The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied
    283   // so the stack address is not escaping here.
    284   if (const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) {
    285     if (isa<BlockDataRegion>(R) &&
    286         ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) {
    287       return;
    288     }
    289   }
    290 
    291   EmitStackError(C, R, RetE);
    292 }
    293 
    294 void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
    295                                               CheckerContext &Ctx) const {
    296   if (!ChecksEnabled[CK_StackAddrEscapeChecker])
    297     return;
    298 
    299   ProgramStateRef State = Ctx.getState();
    300 
    301   // Iterate over all bindings to global variables and see if it contains
    302   // a memory region in the stack space.
    303   class CallBack : public StoreManager::BindingsHandler {
    304   private:
    305     CheckerContext &Ctx;
    306     const StackFrameContext *CurSFC;
    307 
    308   public:
    309     SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10> V;
    310 
    311     CallBack(CheckerContext &CC) : Ctx(CC), CurSFC(CC.getStackFrame()) {}
    312 
    313     bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region,
    314                        SVal Val) override {
    315 
    316       if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace()))
    317         return true;
    318       const MemRegion *VR = Val.getAsRegion();
    319       if (VR && isa<StackSpaceRegion>(VR->getMemorySpace()) &&
    320           !isArcManagedBlock(VR, Ctx) && !isNotInCurrentFrame(VR, Ctx))
    321         V.emplace_back(Region, VR);
    322       return true;
    323     }
    324   };
    325 
    326   CallBack Cb(Ctx);
    327   State->getStateManager().getStoreManager().iterBindings(State->getStore(),
    328                                                           Cb);
    329 
    330   if (Cb.V.empty())
    331     return;
    332 
    333   // Generate an error node.
    334   ExplodedNode *N = Ctx.generateNonFatalErrorNode(State);
    335   if (!N)
    336     return;
    337 
    338   if (!BT_stackleak)
    339     BT_stackleak = std::make_unique<BuiltinBug>(
    340         CheckNames[CK_StackAddrEscapeChecker],
    341         "Stack address stored into global variable",
    342         "Stack address was saved into a global variable. "
    343         "This is dangerous because the address will become "
    344         "invalid after returning from the function");
    345 
    346   for (const auto &P : Cb.V) {
    347     // Generate a report for this bug.
    348     SmallString<128> Buf;
    349     llvm::raw_svector_ostream Out(Buf);
    350     SourceRange Range = genName(Out, P.second, Ctx.getASTContext());
    351     Out << " is still referred to by the ";
    352     if (isa<StaticGlobalSpaceRegion>(P.first->getMemorySpace()))
    353       Out << "static";
    354     else
    355       Out << "global";
    356     Out << " variable '";
    357     const VarRegion *VR = cast<VarRegion>(P.first->getBaseRegion());
    358     Out << *VR->getDecl()
    359         << "' upon returning to the caller.  This will be a dangling reference";
    360     auto Report =
    361         std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
    362     if (Range.isValid())
    363       Report->addRange(Range);
    364 
    365     Ctx.emitReport(std::move(Report));
    366   }
    367 }
    368 
    369 void ento::registerStackAddrEscapeBase(CheckerManager &mgr) {
    370   mgr.registerChecker<StackAddrEscapeChecker>();
    371 }
    372 
    373 bool ento::shouldRegisterStackAddrEscapeBase(const CheckerManager &mgr) {
    374   return true;
    375 }
    376 
    377 #define REGISTER_CHECKER(name)                                                 \
    378   void ento::register##name(CheckerManager &Mgr) {                             \
    379     StackAddrEscapeChecker *Chk = Mgr.getChecker<StackAddrEscapeChecker>();    \
    380     Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true;              \
    381     Chk->CheckNames[StackAddrEscapeChecker::CK_##name] =                       \
    382         Mgr.getCurrentCheckerName();                                           \
    383   }                                                                            \
    384                                                                                \
    385   bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
    386 
    387 REGISTER_CHECKER(StackAddrEscapeChecker)
    388 REGISTER_CHECKER(StackAddrAsyncEscapeChecker)
    389