xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //=== StackAddrEscapeChecker.cpp ----------------------------------*- C++ -*--//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // This file defines stack address leak checker, which checks if an invalid
107330f729Sjoerg // stack address is stored into a global or heap location. See CERT DCL30-C.
117330f729Sjoerg //
127330f729Sjoerg //===----------------------------------------------------------------------===//
137330f729Sjoerg 
147330f729Sjoerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
157330f729Sjoerg #include "clang/AST/ExprCXX.h"
167330f729Sjoerg #include "clang/Basic/SourceManager.h"
177330f729Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
187330f729Sjoerg #include "clang/StaticAnalyzer/Core/Checker.h"
197330f729Sjoerg #include "clang/StaticAnalyzer/Core/CheckerManager.h"
207330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
217330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
227330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
237330f729Sjoerg #include "llvm/ADT/SmallString.h"
247330f729Sjoerg #include "llvm/Support/raw_ostream.h"
257330f729Sjoerg using namespace clang;
267330f729Sjoerg using namespace ento;
277330f729Sjoerg 
287330f729Sjoerg namespace {
297330f729Sjoerg class StackAddrEscapeChecker
307330f729Sjoerg     : public Checker<check::PreCall, check::PreStmt<ReturnStmt>,
317330f729Sjoerg                      check::EndFunction> {
327330f729Sjoerg   mutable IdentifierInfo *dispatch_semaphore_tII;
337330f729Sjoerg   mutable std::unique_ptr<BuiltinBug> BT_stackleak;
347330f729Sjoerg   mutable std::unique_ptr<BuiltinBug> BT_returnstack;
357330f729Sjoerg   mutable std::unique_ptr<BuiltinBug> BT_capturedstackasync;
367330f729Sjoerg   mutable std::unique_ptr<BuiltinBug> BT_capturedstackret;
377330f729Sjoerg 
387330f729Sjoerg public:
397330f729Sjoerg   enum CheckKind {
407330f729Sjoerg     CK_StackAddrEscapeChecker,
417330f729Sjoerg     CK_StackAddrAsyncEscapeChecker,
427330f729Sjoerg     CK_NumCheckKinds
437330f729Sjoerg   };
447330f729Sjoerg 
457330f729Sjoerg   DefaultBool ChecksEnabled[CK_NumCheckKinds];
46*e038c9c4Sjoerg   CheckerNameRef CheckNames[CK_NumCheckKinds];
477330f729Sjoerg 
487330f729Sjoerg   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
497330f729Sjoerg   void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
507330f729Sjoerg   void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const;
517330f729Sjoerg 
527330f729Sjoerg private:
537330f729Sjoerg   void checkReturnedBlockCaptures(const BlockDataRegion &B,
547330f729Sjoerg                                   CheckerContext &C) const;
557330f729Sjoerg   void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B,
567330f729Sjoerg                                        CheckerContext &C) const;
577330f729Sjoerg   void EmitStackError(CheckerContext &C, const MemRegion *R,
587330f729Sjoerg                       const Expr *RetE) const;
597330f729Sjoerg   bool isSemaphoreCaptured(const BlockDecl &B) const;
607330f729Sjoerg   static SourceRange genName(raw_ostream &os, const MemRegion *R,
617330f729Sjoerg                              ASTContext &Ctx);
627330f729Sjoerg   static SmallVector<const MemRegion *, 4>
637330f729Sjoerg   getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C);
647330f729Sjoerg   static bool isArcManagedBlock(const MemRegion *R, CheckerContext &C);
657330f729Sjoerg   static bool isNotInCurrentFrame(const MemRegion *R, CheckerContext &C);
667330f729Sjoerg };
677330f729Sjoerg } // namespace
687330f729Sjoerg 
genName(raw_ostream & os,const MemRegion * R,ASTContext & Ctx)697330f729Sjoerg SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R,
707330f729Sjoerg                                             ASTContext &Ctx) {
717330f729Sjoerg   // Get the base region, stripping away fields and elements.
727330f729Sjoerg   R = R->getBaseRegion();
737330f729Sjoerg   SourceManager &SM = Ctx.getSourceManager();
747330f729Sjoerg   SourceRange range;
757330f729Sjoerg   os << "Address of ";
767330f729Sjoerg 
777330f729Sjoerg   // Check if the region is a compound literal.
787330f729Sjoerg   if (const auto *CR = dyn_cast<CompoundLiteralRegion>(R)) {
797330f729Sjoerg     const CompoundLiteralExpr *CL = CR->getLiteralExpr();
807330f729Sjoerg     os << "stack memory associated with a compound literal "
817330f729Sjoerg           "declared on line "
827330f729Sjoerg        << SM.getExpansionLineNumber(CL->getBeginLoc()) << " returned to caller";
837330f729Sjoerg     range = CL->getSourceRange();
847330f729Sjoerg   } else if (const auto *AR = dyn_cast<AllocaRegion>(R)) {
857330f729Sjoerg     const Expr *ARE = AR->getExpr();
867330f729Sjoerg     SourceLocation L = ARE->getBeginLoc();
877330f729Sjoerg     range = ARE->getSourceRange();
887330f729Sjoerg     os << "stack memory allocated by call to alloca() on line "
897330f729Sjoerg        << SM.getExpansionLineNumber(L);
907330f729Sjoerg   } else if (const auto *BR = dyn_cast<BlockDataRegion>(R)) {
917330f729Sjoerg     const BlockDecl *BD = BR->getCodeRegion()->getDecl();
927330f729Sjoerg     SourceLocation L = BD->getBeginLoc();
937330f729Sjoerg     range = BD->getSourceRange();
947330f729Sjoerg     os << "stack-allocated block declared on line "
957330f729Sjoerg        << SM.getExpansionLineNumber(L);
967330f729Sjoerg   } else if (const auto *VR = dyn_cast<VarRegion>(R)) {
977330f729Sjoerg     os << "stack memory associated with local variable '" << VR->getString()
987330f729Sjoerg        << '\'';
997330f729Sjoerg     range = VR->getDecl()->getSourceRange();
1007330f729Sjoerg   } else if (const auto *TOR = dyn_cast<CXXTempObjectRegion>(R)) {
1017330f729Sjoerg     QualType Ty = TOR->getValueType().getLocalUnqualifiedType();
1027330f729Sjoerg     os << "stack memory associated with temporary object of type '";
1037330f729Sjoerg     Ty.print(os, Ctx.getPrintingPolicy());
1047330f729Sjoerg     os << "'";
1057330f729Sjoerg     range = TOR->getExpr()->getSourceRange();
1067330f729Sjoerg   } else {
1077330f729Sjoerg     llvm_unreachable("Invalid region in ReturnStackAddressChecker.");
1087330f729Sjoerg   }
1097330f729Sjoerg 
1107330f729Sjoerg   return range;
1117330f729Sjoerg }
1127330f729Sjoerg 
isArcManagedBlock(const MemRegion * R,CheckerContext & C)1137330f729Sjoerg bool StackAddrEscapeChecker::isArcManagedBlock(const MemRegion *R,
1147330f729Sjoerg                                                CheckerContext &C) {
1157330f729Sjoerg   assert(R && "MemRegion should not be null");
1167330f729Sjoerg   return C.getASTContext().getLangOpts().ObjCAutoRefCount &&
1177330f729Sjoerg          isa<BlockDataRegion>(R);
1187330f729Sjoerg }
1197330f729Sjoerg 
isNotInCurrentFrame(const MemRegion * R,CheckerContext & C)1207330f729Sjoerg bool StackAddrEscapeChecker::isNotInCurrentFrame(const MemRegion *R,
1217330f729Sjoerg                                                  CheckerContext &C) {
1227330f729Sjoerg   const StackSpaceRegion *S = cast<StackSpaceRegion>(R->getMemorySpace());
1237330f729Sjoerg   return S->getStackFrame() != C.getStackFrame();
1247330f729Sjoerg }
1257330f729Sjoerg 
isSemaphoreCaptured(const BlockDecl & B) const1267330f729Sjoerg bool StackAddrEscapeChecker::isSemaphoreCaptured(const BlockDecl &B) const {
1277330f729Sjoerg   if (!dispatch_semaphore_tII)
1287330f729Sjoerg     dispatch_semaphore_tII = &B.getASTContext().Idents.get("dispatch_semaphore_t");
1297330f729Sjoerg   for (const auto &C : B.captures()) {
1307330f729Sjoerg     const auto *T = C.getVariable()->getType()->getAs<TypedefType>();
1317330f729Sjoerg     if (T && T->getDecl()->getIdentifier() == dispatch_semaphore_tII)
1327330f729Sjoerg       return true;
1337330f729Sjoerg   }
1347330f729Sjoerg   return false;
1357330f729Sjoerg }
1367330f729Sjoerg 
1377330f729Sjoerg SmallVector<const MemRegion *, 4>
getCapturedStackRegions(const BlockDataRegion & B,CheckerContext & C)1387330f729Sjoerg StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B,
1397330f729Sjoerg                                                 CheckerContext &C) {
1407330f729Sjoerg   SmallVector<const MemRegion *, 4> Regions;
1417330f729Sjoerg   BlockDataRegion::referenced_vars_iterator I = B.referenced_vars_begin();
1427330f729Sjoerg   BlockDataRegion::referenced_vars_iterator E = B.referenced_vars_end();
1437330f729Sjoerg   for (; I != E; ++I) {
1447330f729Sjoerg     SVal Val = C.getState()->getSVal(I.getCapturedRegion());
1457330f729Sjoerg     const MemRegion *Region = Val.getAsRegion();
1467330f729Sjoerg     if (Region && isa<StackSpaceRegion>(Region->getMemorySpace()))
1477330f729Sjoerg       Regions.push_back(Region);
1487330f729Sjoerg   }
1497330f729Sjoerg   return Regions;
1507330f729Sjoerg }
1517330f729Sjoerg 
EmitStackError(CheckerContext & C,const MemRegion * R,const Expr * RetE) const1527330f729Sjoerg void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,
1537330f729Sjoerg                                             const MemRegion *R,
1547330f729Sjoerg                                             const Expr *RetE) const {
1557330f729Sjoerg   ExplodedNode *N = C.generateNonFatalErrorNode();
1567330f729Sjoerg   if (!N)
1577330f729Sjoerg     return;
1587330f729Sjoerg   if (!BT_returnstack)
1597330f729Sjoerg     BT_returnstack = std::make_unique<BuiltinBug>(
160*e038c9c4Sjoerg         CheckNames[CK_StackAddrEscapeChecker],
161*e038c9c4Sjoerg         "Return of address to stack-allocated memory");
1627330f729Sjoerg   // Generate a report for this bug.
1637330f729Sjoerg   SmallString<128> buf;
1647330f729Sjoerg   llvm::raw_svector_ostream os(buf);
1657330f729Sjoerg   SourceRange range = genName(os, R, C.getASTContext());
1667330f729Sjoerg   os << " returned to caller";
1677330f729Sjoerg   auto report =
1687330f729Sjoerg       std::make_unique<PathSensitiveBugReport>(*BT_returnstack, os.str(), N);
1697330f729Sjoerg   report->addRange(RetE->getSourceRange());
1707330f729Sjoerg   if (range.isValid())
1717330f729Sjoerg     report->addRange(range);
1727330f729Sjoerg   C.emitReport(std::move(report));
1737330f729Sjoerg }
1747330f729Sjoerg 
checkAsyncExecutedBlockCaptures(const BlockDataRegion & B,CheckerContext & C) const1757330f729Sjoerg void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
1767330f729Sjoerg     const BlockDataRegion &B, CheckerContext &C) const {
1777330f729Sjoerg   // There is a not-too-uncommon idiom
1787330f729Sjoerg   // where a block passed to dispatch_async captures a semaphore
1797330f729Sjoerg   // and then the thread (which called dispatch_async) is blocked on waiting
1807330f729Sjoerg   // for the completion of the execution of the block
1817330f729Sjoerg   // via dispatch_semaphore_wait. To avoid false-positives (for now)
1827330f729Sjoerg   // we ignore all the blocks which have captured
1837330f729Sjoerg   // a variable of the type "dispatch_semaphore_t".
1847330f729Sjoerg   if (isSemaphoreCaptured(*B.getDecl()))
1857330f729Sjoerg     return;
1867330f729Sjoerg   for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
1877330f729Sjoerg     // The block passed to dispatch_async may capture another block
1887330f729Sjoerg     // created on the stack. However, there is no leak in this situaton,
1897330f729Sjoerg     // no matter if ARC or no ARC is enabled:
1907330f729Sjoerg     // dispatch_async copies the passed "outer" block (via Block_copy)
1917330f729Sjoerg     // and if the block has captured another "inner" block,
1927330f729Sjoerg     // the "inner" block will be copied as well.
1937330f729Sjoerg     if (isa<BlockDataRegion>(Region))
1947330f729Sjoerg       continue;
1957330f729Sjoerg     ExplodedNode *N = C.generateNonFatalErrorNode();
1967330f729Sjoerg     if (!N)
1977330f729Sjoerg       continue;
1987330f729Sjoerg     if (!BT_capturedstackasync)
1997330f729Sjoerg       BT_capturedstackasync = std::make_unique<BuiltinBug>(
200*e038c9c4Sjoerg           CheckNames[CK_StackAddrAsyncEscapeChecker],
201*e038c9c4Sjoerg           "Address of stack-allocated memory is captured");
2027330f729Sjoerg     SmallString<128> Buf;
2037330f729Sjoerg     llvm::raw_svector_ostream Out(Buf);
2047330f729Sjoerg     SourceRange Range = genName(Out, Region, C.getASTContext());
2057330f729Sjoerg     Out << " is captured by an asynchronously-executed block";
2067330f729Sjoerg     auto Report = std::make_unique<PathSensitiveBugReport>(
2077330f729Sjoerg         *BT_capturedstackasync, Out.str(), N);
2087330f729Sjoerg     if (Range.isValid())
2097330f729Sjoerg       Report->addRange(Range);
2107330f729Sjoerg     C.emitReport(std::move(Report));
2117330f729Sjoerg   }
2127330f729Sjoerg }
2137330f729Sjoerg 
checkReturnedBlockCaptures(const BlockDataRegion & B,CheckerContext & C) const2147330f729Sjoerg void StackAddrEscapeChecker::checkReturnedBlockCaptures(
2157330f729Sjoerg     const BlockDataRegion &B, CheckerContext &C) const {
2167330f729Sjoerg   for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
2177330f729Sjoerg     if (isArcManagedBlock(Region, C) || isNotInCurrentFrame(Region, C))
2187330f729Sjoerg       continue;
2197330f729Sjoerg     ExplodedNode *N = C.generateNonFatalErrorNode();
2207330f729Sjoerg     if (!N)
2217330f729Sjoerg       continue;
2227330f729Sjoerg     if (!BT_capturedstackret)
2237330f729Sjoerg       BT_capturedstackret = std::make_unique<BuiltinBug>(
224*e038c9c4Sjoerg           CheckNames[CK_StackAddrEscapeChecker],
225*e038c9c4Sjoerg           "Address of stack-allocated memory is captured");
2267330f729Sjoerg     SmallString<128> Buf;
2277330f729Sjoerg     llvm::raw_svector_ostream Out(Buf);
2287330f729Sjoerg     SourceRange Range = genName(Out, Region, C.getASTContext());
2297330f729Sjoerg     Out << " is captured by a returned block";
2307330f729Sjoerg     auto Report = std::make_unique<PathSensitiveBugReport>(*BT_capturedstackret,
2317330f729Sjoerg                                                            Out.str(), N);
2327330f729Sjoerg     if (Range.isValid())
2337330f729Sjoerg       Report->addRange(Range);
2347330f729Sjoerg     C.emitReport(std::move(Report));
2357330f729Sjoerg   }
2367330f729Sjoerg }
2377330f729Sjoerg 
checkPreCall(const CallEvent & Call,CheckerContext & C) const2387330f729Sjoerg void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
2397330f729Sjoerg                                           CheckerContext &C) const {
2407330f729Sjoerg   if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
2417330f729Sjoerg     return;
2427330f729Sjoerg   if (!Call.isGlobalCFunction("dispatch_after") &&
2437330f729Sjoerg       !Call.isGlobalCFunction("dispatch_async"))
2447330f729Sjoerg     return;
2457330f729Sjoerg   for (unsigned Idx = 0, NumArgs = Call.getNumArgs(); Idx < NumArgs; ++Idx) {
2467330f729Sjoerg     if (const BlockDataRegion *B = dyn_cast_or_null<BlockDataRegion>(
2477330f729Sjoerg             Call.getArgSVal(Idx).getAsRegion()))
2487330f729Sjoerg       checkAsyncExecutedBlockCaptures(*B, C);
2497330f729Sjoerg   }
2507330f729Sjoerg }
2517330f729Sjoerg 
checkPreStmt(const ReturnStmt * RS,CheckerContext & C) const2527330f729Sjoerg void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
2537330f729Sjoerg                                           CheckerContext &C) const {
2547330f729Sjoerg   if (!ChecksEnabled[CK_StackAddrEscapeChecker])
2557330f729Sjoerg     return;
2567330f729Sjoerg 
2577330f729Sjoerg   const Expr *RetE = RS->getRetValue();
2587330f729Sjoerg   if (!RetE)
2597330f729Sjoerg     return;
2607330f729Sjoerg   RetE = RetE->IgnoreParens();
2617330f729Sjoerg 
2627330f729Sjoerg   SVal V = C.getSVal(RetE);
2637330f729Sjoerg   const MemRegion *R = V.getAsRegion();
2647330f729Sjoerg   if (!R)
2657330f729Sjoerg     return;
2667330f729Sjoerg 
2677330f729Sjoerg   if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R))
2687330f729Sjoerg     checkReturnedBlockCaptures(*B, C);
2697330f729Sjoerg 
2707330f729Sjoerg   if (!isa<StackSpaceRegion>(R->getMemorySpace()) ||
2717330f729Sjoerg       isNotInCurrentFrame(R, C) || isArcManagedBlock(R, C))
2727330f729Sjoerg     return;
2737330f729Sjoerg 
2747330f729Sjoerg   // Returning a record by value is fine. (In this case, the returned
2757330f729Sjoerg   // expression will be a copy-constructor, possibly wrapped in an
2767330f729Sjoerg   // ExprWithCleanups node.)
2777330f729Sjoerg   if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE))
2787330f729Sjoerg     RetE = Cleanup->getSubExpr();
2797330f729Sjoerg   if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType())
2807330f729Sjoerg     return;
2817330f729Sjoerg 
2827330f729Sjoerg   // The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied
2837330f729Sjoerg   // so the stack address is not escaping here.
284*e038c9c4Sjoerg   if (const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) {
2857330f729Sjoerg     if (isa<BlockDataRegion>(R) &&
2867330f729Sjoerg         ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) {
2877330f729Sjoerg       return;
2887330f729Sjoerg     }
2897330f729Sjoerg   }
2907330f729Sjoerg 
2917330f729Sjoerg   EmitStackError(C, R, RetE);
2927330f729Sjoerg }
2937330f729Sjoerg 
checkEndFunction(const ReturnStmt * RS,CheckerContext & Ctx) const2947330f729Sjoerg void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
2957330f729Sjoerg                                               CheckerContext &Ctx) const {
2967330f729Sjoerg   if (!ChecksEnabled[CK_StackAddrEscapeChecker])
2977330f729Sjoerg     return;
2987330f729Sjoerg 
2997330f729Sjoerg   ProgramStateRef State = Ctx.getState();
3007330f729Sjoerg 
3017330f729Sjoerg   // Iterate over all bindings to global variables and see if it contains
3027330f729Sjoerg   // a memory region in the stack space.
3037330f729Sjoerg   class CallBack : public StoreManager::BindingsHandler {
3047330f729Sjoerg   private:
3057330f729Sjoerg     CheckerContext &Ctx;
3067330f729Sjoerg     const StackFrameContext *CurSFC;
3077330f729Sjoerg 
3087330f729Sjoerg   public:
3097330f729Sjoerg     SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10> V;
3107330f729Sjoerg 
3117330f729Sjoerg     CallBack(CheckerContext &CC) : Ctx(CC), CurSFC(CC.getStackFrame()) {}
3127330f729Sjoerg 
3137330f729Sjoerg     bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region,
3147330f729Sjoerg                        SVal Val) override {
3157330f729Sjoerg 
3167330f729Sjoerg       if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace()))
3177330f729Sjoerg         return true;
3187330f729Sjoerg       const MemRegion *VR = Val.getAsRegion();
3197330f729Sjoerg       if (VR && isa<StackSpaceRegion>(VR->getMemorySpace()) &&
3207330f729Sjoerg           !isArcManagedBlock(VR, Ctx) && !isNotInCurrentFrame(VR, Ctx))
3217330f729Sjoerg         V.emplace_back(Region, VR);
3227330f729Sjoerg       return true;
3237330f729Sjoerg     }
3247330f729Sjoerg   };
3257330f729Sjoerg 
3267330f729Sjoerg   CallBack Cb(Ctx);
3277330f729Sjoerg   State->getStateManager().getStoreManager().iterBindings(State->getStore(),
3287330f729Sjoerg                                                           Cb);
3297330f729Sjoerg 
3307330f729Sjoerg   if (Cb.V.empty())
3317330f729Sjoerg     return;
3327330f729Sjoerg 
3337330f729Sjoerg   // Generate an error node.
3347330f729Sjoerg   ExplodedNode *N = Ctx.generateNonFatalErrorNode(State);
3357330f729Sjoerg   if (!N)
3367330f729Sjoerg     return;
3377330f729Sjoerg 
3387330f729Sjoerg   if (!BT_stackleak)
3397330f729Sjoerg     BT_stackleak = std::make_unique<BuiltinBug>(
340*e038c9c4Sjoerg         CheckNames[CK_StackAddrEscapeChecker],
341*e038c9c4Sjoerg         "Stack address stored into global variable",
3427330f729Sjoerg         "Stack address was saved into a global variable. "
3437330f729Sjoerg         "This is dangerous because the address will become "
3447330f729Sjoerg         "invalid after returning from the function");
3457330f729Sjoerg 
3467330f729Sjoerg   for (const auto &P : Cb.V) {
3477330f729Sjoerg     // Generate a report for this bug.
3487330f729Sjoerg     SmallString<128> Buf;
3497330f729Sjoerg     llvm::raw_svector_ostream Out(Buf);
3507330f729Sjoerg     SourceRange Range = genName(Out, P.second, Ctx.getASTContext());
3517330f729Sjoerg     Out << " is still referred to by the ";
3527330f729Sjoerg     if (isa<StaticGlobalSpaceRegion>(P.first->getMemorySpace()))
3537330f729Sjoerg       Out << "static";
3547330f729Sjoerg     else
3557330f729Sjoerg       Out << "global";
3567330f729Sjoerg     Out << " variable '";
3577330f729Sjoerg     const VarRegion *VR = cast<VarRegion>(P.first->getBaseRegion());
3587330f729Sjoerg     Out << *VR->getDecl()
3597330f729Sjoerg         << "' upon returning to the caller.  This will be a dangling reference";
3607330f729Sjoerg     auto Report =
3617330f729Sjoerg         std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
3627330f729Sjoerg     if (Range.isValid())
3637330f729Sjoerg       Report->addRange(Range);
3647330f729Sjoerg 
3657330f729Sjoerg     Ctx.emitReport(std::move(Report));
3667330f729Sjoerg   }
3677330f729Sjoerg }
3687330f729Sjoerg 
registerStackAddrEscapeBase(CheckerManager & mgr)3697330f729Sjoerg void ento::registerStackAddrEscapeBase(CheckerManager &mgr) {
3707330f729Sjoerg   mgr.registerChecker<StackAddrEscapeChecker>();
3717330f729Sjoerg }
3727330f729Sjoerg 
shouldRegisterStackAddrEscapeBase(const CheckerManager & mgr)373*e038c9c4Sjoerg bool ento::shouldRegisterStackAddrEscapeBase(const CheckerManager &mgr) {
3747330f729Sjoerg   return true;
3757330f729Sjoerg }
3767330f729Sjoerg 
3777330f729Sjoerg #define REGISTER_CHECKER(name)                                                 \
3787330f729Sjoerg   void ento::register##name(CheckerManager &Mgr) {                             \
379*e038c9c4Sjoerg     StackAddrEscapeChecker *Chk = Mgr.getChecker<StackAddrEscapeChecker>();    \
3807330f729Sjoerg     Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true;              \
381*e038c9c4Sjoerg     Chk->CheckNames[StackAddrEscapeChecker::CK_##name] =                       \
382*e038c9c4Sjoerg         Mgr.getCurrentCheckerName();                                           \
3837330f729Sjoerg   }                                                                            \
3847330f729Sjoerg                                                                                \
385*e038c9c4Sjoerg   bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
3867330f729Sjoerg 
3877330f729Sjoerg REGISTER_CHECKER(StackAddrEscapeChecker)
3887330f729Sjoerg REGISTER_CHECKER(StackAddrAsyncEscapeChecker)
389