xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp (revision 770cf0a5f02dc8983a89c6568d741fbc25baa999)
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/AST/ExprCXX.h"
15 #include "clang/Basic/SourceManager.h"
16 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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/MemRegion.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
24 #include "llvm/ADT/STLExtras.h"
25 #include "llvm/Support/raw_ostream.h"
26 using namespace clang;
27 using namespace ento;
28 
29 namespace {
30 class StackAddrEscapeChecker
31     : public Checker<check::PreCall, check::PreStmt<ReturnStmt>,
32                      check::EndFunction> {
33   mutable IdentifierInfo *dispatch_semaphore_tII = nullptr;
34   mutable std::unique_ptr<BugType> BT_stackleak;
35   mutable std::unique_ptr<BugType> BT_returnstack;
36   mutable std::unique_ptr<BugType> BT_capturedstackasync;
37   mutable std::unique_ptr<BugType> BT_capturedstackret;
38 
39 public:
40   enum CheckKind {
41     CK_StackAddrEscapeChecker,
42     CK_StackAddrAsyncEscapeChecker,
43     CK_NumCheckKinds
44   };
45 
46   bool ChecksEnabled[CK_NumCheckKinds] = {false};
47   CheckerNameRef CheckNames[CK_NumCheckKinds];
48 
49   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
50   void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
51   void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const;
52 
53 private:
54   void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B,
55                                        CheckerContext &C) const;
56   void EmitReturnLeakError(CheckerContext &C, const MemRegion *LeakedRegion,
57                            const Expr *RetE) const;
58   bool isSemaphoreCaptured(const BlockDecl &B) const;
59   static SourceRange genName(raw_ostream &os, const MemRegion *R,
60                              ASTContext &Ctx);
61   static SmallVector<std::pair<const MemRegion *, const StackSpaceRegion *>, 4>
62   getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C);
63   static bool isNotInCurrentFrame(const StackSpaceRegion *MS,
64                                   CheckerContext &C);
65 };
66 } // namespace
67 
68 SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R,
69                                             ASTContext &Ctx) {
70   // Get the base region, stripping away fields and elements.
71   R = R->getBaseRegion();
72   SourceManager &SM = Ctx.getSourceManager();
73   SourceRange range;
74   os << "Address of ";
75 
76   // Check if the region is a compound literal.
77   if (const auto *CR = dyn_cast<CompoundLiteralRegion>(R)) {
78     const CompoundLiteralExpr *CL = CR->getLiteralExpr();
79     os << "stack memory associated with a compound literal "
80           "declared on line "
81        << SM.getExpansionLineNumber(CL->getBeginLoc());
82     range = CL->getSourceRange();
83   } else if (const auto *AR = dyn_cast<AllocaRegion>(R)) {
84     const Expr *ARE = AR->getExpr();
85     SourceLocation L = ARE->getBeginLoc();
86     range = ARE->getSourceRange();
87     os << "stack memory allocated by call to alloca() on line "
88        << SM.getExpansionLineNumber(L);
89   } else if (const auto *BR = dyn_cast<BlockDataRegion>(R)) {
90     const BlockDecl *BD = BR->getCodeRegion()->getDecl();
91     SourceLocation L = BD->getBeginLoc();
92     range = BD->getSourceRange();
93     os << "stack-allocated block declared on line "
94        << SM.getExpansionLineNumber(L);
95   } else if (const auto *VR = dyn_cast<VarRegion>(R)) {
96     os << "stack memory associated with local variable '" << VR->getString()
97        << '\'';
98     range = VR->getDecl()->getSourceRange();
99   } else if (const auto *LER = dyn_cast<CXXLifetimeExtendedObjectRegion>(R)) {
100     QualType Ty = LER->getValueType().getLocalUnqualifiedType();
101     os << "stack memory associated with temporary object of type '";
102     Ty.print(os, Ctx.getPrintingPolicy());
103     os << "' lifetime extended by local variable";
104     if (const IdentifierInfo *ID = LER->getExtendingDecl()->getIdentifier())
105       os << " '" << ID->getName() << '\'';
106     range = LER->getExpr()->getSourceRange();
107   } else if (const auto *TOR = dyn_cast<CXXTempObjectRegion>(R)) {
108     QualType Ty = TOR->getValueType().getLocalUnqualifiedType();
109     os << "stack memory associated with temporary object of type '";
110     Ty.print(os, Ctx.getPrintingPolicy());
111     os << "'";
112     range = TOR->getExpr()->getSourceRange();
113   } else {
114     llvm_unreachable("Invalid region in ReturnStackAddressChecker.");
115   }
116 
117   return range;
118 }
119 
120 bool StackAddrEscapeChecker::isNotInCurrentFrame(const StackSpaceRegion *MS,
121                                                  CheckerContext &C) {
122   return MS->getStackFrame() != C.getStackFrame();
123 }
124 
125 bool StackAddrEscapeChecker::isSemaphoreCaptured(const BlockDecl &B) const {
126   if (!dispatch_semaphore_tII)
127     dispatch_semaphore_tII = &B.getASTContext().Idents.get("dispatch_semaphore_t");
128   for (const auto &C : B.captures()) {
129     const auto *T = C.getVariable()->getType()->getAs<TypedefType>();
130     if (T && T->getDecl()->getIdentifier() == dispatch_semaphore_tII)
131       return true;
132   }
133   return false;
134 }
135 
136 SmallVector<std::pair<const MemRegion *, const StackSpaceRegion *>, 4>
137 StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B,
138                                                 CheckerContext &C) {
139   SmallVector<std::pair<const MemRegion *, const StackSpaceRegion *>, 4>
140       Regions;
141   ProgramStateRef State = C.getState();
142   for (auto Var : B.referenced_vars()) {
143     SVal Val = State->getSVal(Var.getCapturedRegion());
144     if (const MemRegion *Region = Val.getAsRegion()) {
145       if (const auto *Space =
146               Region->getMemorySpaceAs<StackSpaceRegion>(State)) {
147         Regions.emplace_back(Region, Space);
148       }
149     }
150   }
151   return Regions;
152 }
153 
154 static void EmitReturnedAsPartOfError(llvm::raw_ostream &OS, SVal ReturnedVal,
155                                       const MemRegion *LeakedRegion) {
156   if (const MemRegion *ReturnedRegion = ReturnedVal.getAsRegion()) {
157     if (isa<BlockDataRegion>(ReturnedRegion)) {
158       OS << " is captured by a returned block";
159       return;
160     }
161   }
162 
163   // Generic message
164   OS << " returned to caller";
165 }
166 
167 void StackAddrEscapeChecker::EmitReturnLeakError(CheckerContext &C,
168                                                  const MemRegion *R,
169                                                  const Expr *RetE) const {
170   ExplodedNode *N = C.generateNonFatalErrorNode();
171   if (!N)
172     return;
173   if (!BT_returnstack)
174     BT_returnstack = std::make_unique<BugType>(
175         CheckNames[CK_StackAddrEscapeChecker],
176         "Return of address to stack-allocated memory");
177 
178   // Generate a report for this bug.
179   SmallString<128> buf;
180   llvm::raw_svector_ostream os(buf);
181 
182   // Error message formatting
183   SourceRange range = genName(os, R, C.getASTContext());
184   EmitReturnedAsPartOfError(os, C.getSVal(RetE), R);
185 
186   auto report =
187       std::make_unique<PathSensitiveBugReport>(*BT_returnstack, os.str(), N);
188   report->addRange(RetE->getSourceRange());
189   if (range.isValid())
190     report->addRange(range);
191   C.emitReport(std::move(report));
192 }
193 
194 void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
195     const BlockDataRegion &B, CheckerContext &C) const {
196   // There is a not-too-uncommon idiom
197   // where a block passed to dispatch_async captures a semaphore
198   // and then the thread (which called dispatch_async) is blocked on waiting
199   // for the completion of the execution of the block
200   // via dispatch_semaphore_wait. To avoid false-positives (for now)
201   // we ignore all the blocks which have captured
202   // a variable of the type "dispatch_semaphore_t".
203   if (isSemaphoreCaptured(*B.getDecl()))
204     return;
205   auto Regions = getCapturedStackRegions(B, C);
206   for (const MemRegion *Region : llvm::make_first_range(Regions)) {
207     // The block passed to dispatch_async may capture another block
208     // created on the stack. However, there is no leak in this situaton,
209     // no matter if ARC or no ARC is enabled:
210     // dispatch_async copies the passed "outer" block (via Block_copy)
211     // and if the block has captured another "inner" block,
212     // the "inner" block will be copied as well.
213     if (isa<BlockDataRegion>(Region))
214       continue;
215     ExplodedNode *N = C.generateNonFatalErrorNode();
216     if (!N)
217       continue;
218     if (!BT_capturedstackasync)
219       BT_capturedstackasync = std::make_unique<BugType>(
220           CheckNames[CK_StackAddrAsyncEscapeChecker],
221           "Address of stack-allocated memory is captured");
222     SmallString<128> Buf;
223     llvm::raw_svector_ostream Out(Buf);
224     SourceRange Range = genName(Out, Region, C.getASTContext());
225     Out << " is captured by an asynchronously-executed block";
226     auto Report = std::make_unique<PathSensitiveBugReport>(
227         *BT_capturedstackasync, Out.str(), N);
228     if (Range.isValid())
229       Report->addRange(Range);
230     C.emitReport(std::move(Report));
231   }
232 }
233 
234 void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
235                                           CheckerContext &C) const {
236   if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
237     return;
238   if (!Call.isGlobalCFunction("dispatch_after") &&
239       !Call.isGlobalCFunction("dispatch_async"))
240     return;
241   for (unsigned Idx = 0, NumArgs = Call.getNumArgs(); Idx < NumArgs; ++Idx) {
242     if (const BlockDataRegion *B = dyn_cast_or_null<BlockDataRegion>(
243             Call.getArgSVal(Idx).getAsRegion()))
244       checkAsyncExecutedBlockCaptures(*B, C);
245   }
246 }
247 
248 /// A visitor made for use with a ScanReachableSymbols scanner, used
249 /// for finding stack regions within an SVal that live on the current
250 /// stack frame of the given checker context. This visitor excludes
251 /// NonParamVarRegion that data is bound to in a BlockDataRegion's
252 /// bindings, since these are likely uninteresting, e.g., in case a
253 /// temporary is constructed on the stack, but it captures values
254 /// that would leak.
255 class FindStackRegionsSymbolVisitor final : public SymbolVisitor {
256   CheckerContext &Ctxt;
257   const StackFrameContext *PoppedStackFrame;
258   SmallVectorImpl<const MemRegion *> &EscapingStackRegions;
259 
260 public:
261   explicit FindStackRegionsSymbolVisitor(
262       CheckerContext &Ctxt,
263       SmallVectorImpl<const MemRegion *> &StorageForStackRegions)
264       : Ctxt(Ctxt), PoppedStackFrame(Ctxt.getStackFrame()),
265         EscapingStackRegions(StorageForStackRegions) {}
266 
267   bool VisitSymbol(SymbolRef sym) override { return true; }
268 
269   bool VisitMemRegion(const MemRegion *MR) override {
270     SaveIfEscapes(MR);
271 
272     if (const BlockDataRegion *BDR = MR->getAs<BlockDataRegion>())
273       return VisitBlockDataRegionCaptures(BDR);
274 
275     return true;
276   }
277 
278 private:
279   void SaveIfEscapes(const MemRegion *MR) {
280     const auto *SSR = MR->getMemorySpaceAs<StackSpaceRegion>(Ctxt.getState());
281 
282     if (!SSR)
283       return;
284 
285     const StackFrameContext *CapturedSFC = SSR->getStackFrame();
286     if (CapturedSFC == PoppedStackFrame ||
287         PoppedStackFrame->isParentOf(CapturedSFC))
288       EscapingStackRegions.push_back(MR);
289   }
290 
291   bool VisitBlockDataRegionCaptures(const BlockDataRegion *BDR) {
292     for (auto Var : BDR->referenced_vars()) {
293       SVal Val = Ctxt.getState()->getSVal(Var.getCapturedRegion());
294       const MemRegion *Region = Val.getAsRegion();
295       if (Region) {
296         SaveIfEscapes(Region);
297         VisitMemRegion(Region);
298       }
299     }
300 
301     return false;
302   }
303 };
304 
305 /// Given some memory regions that are flagged by FindStackRegionsSymbolVisitor,
306 /// this function filters out memory regions that are being returned that are
307 /// likely not true leaks:
308 /// 1. If returning a block data region that has stack memory space
309 /// 2. If returning a constructed object that has stack memory space
310 static SmallVector<const MemRegion *> FilterReturnExpressionLeaks(
311     const SmallVectorImpl<const MemRegion *> &MaybeEscaped, CheckerContext &C,
312     const Expr *RetE, SVal &RetVal) {
313 
314   SmallVector<const MemRegion *> WillEscape;
315 
316   const MemRegion *RetRegion = RetVal.getAsRegion();
317 
318   // Returning a record by value is fine. (In this case, the returned
319   // expression will be a copy-constructor, possibly wrapped in an
320   // ExprWithCleanups node.)
321   if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE))
322     RetE = Cleanup->getSubExpr();
323   bool IsConstructExpr =
324       isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType();
325 
326   // The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied
327   // so the stack address is not escaping here.
328   bool IsCopyAndAutoreleaseBlockObj = false;
329   if (const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) {
330     IsCopyAndAutoreleaseBlockObj =
331         isa_and_nonnull<BlockDataRegion>(RetRegion) &&
332         ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject;
333   }
334 
335   for (const MemRegion *MR : MaybeEscaped) {
336     if (RetRegion == MR && (IsCopyAndAutoreleaseBlockObj || IsConstructExpr))
337       continue;
338 
339     WillEscape.push_back(MR);
340   }
341 
342   return WillEscape;
343 }
344 
345 /// For use in finding regions that live on the checker context's current
346 /// stack frame, deep in the SVal representing the return value.
347 static SmallVector<const MemRegion *>
348 FindEscapingStackRegions(CheckerContext &C, const Expr *RetE, SVal RetVal) {
349   SmallVector<const MemRegion *> FoundStackRegions;
350 
351   FindStackRegionsSymbolVisitor Finder(C, FoundStackRegions);
352   ScanReachableSymbols Scanner(C.getState(), Finder);
353   Scanner.scan(RetVal);
354 
355   return FilterReturnExpressionLeaks(FoundStackRegions, C, RetE, RetVal);
356 }
357 
358 void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
359                                           CheckerContext &C) const {
360   if (!ChecksEnabled[CK_StackAddrEscapeChecker])
361     return;
362 
363   const Expr *RetE = RS->getRetValue();
364   if (!RetE)
365     return;
366   RetE = RetE->IgnoreParens();
367 
368   SVal V = C.getSVal(RetE);
369 
370   SmallVector<const MemRegion *> EscapedStackRegions =
371       FindEscapingStackRegions(C, RetE, V);
372 
373   for (const MemRegion *ER : EscapedStackRegions)
374     EmitReturnLeakError(C, ER, RetE);
375 }
376 
377 static const MemSpaceRegion *getStackOrGlobalSpaceRegion(ProgramStateRef State,
378                                                          const MemRegion *R) {
379   assert(R);
380   if (const auto *MemSpace = R->getMemorySpace(State);
381       isa<StackSpaceRegion, GlobalsSpaceRegion>(MemSpace))
382     return MemSpace;
383 
384   // If R describes a lambda capture, it will be a symbolic region
385   // referring to a field region of another symbolic region.
386   if (const auto *SymReg = R->getBaseRegion()->getAs<SymbolicRegion>()) {
387     if (const auto *OriginReg = SymReg->getSymbol()->getOriginRegion())
388       return getStackOrGlobalSpaceRegion(State, OriginReg);
389   }
390   return nullptr;
391 }
392 
393 static const MemRegion *getOriginBaseRegion(const MemRegion *Reg) {
394   Reg = Reg->getBaseRegion();
395   while (const auto *SymReg = dyn_cast<SymbolicRegion>(Reg)) {
396     const auto *OriginReg = SymReg->getSymbol()->getOriginRegion();
397     if (!OriginReg)
398       break;
399     Reg = OriginReg->getBaseRegion();
400   }
401   return Reg;
402 }
403 
404 static std::optional<std::string> printReferrer(ProgramStateRef State,
405                                                 const MemRegion *Referrer) {
406   assert(Referrer);
407   const StringRef ReferrerMemorySpace = [](const MemSpaceRegion *Space) {
408     if (isa<StaticGlobalSpaceRegion>(Space))
409       return "static";
410     if (isa<GlobalsSpaceRegion>(Space))
411       return "global";
412     assert(isa<StackSpaceRegion>(Space));
413     // This case covers top-level and inlined analyses.
414     return "caller";
415   }(getStackOrGlobalSpaceRegion(State, Referrer));
416 
417   while (!Referrer->canPrintPretty()) {
418     if (const auto *SymReg = dyn_cast<SymbolicRegion>(Referrer);
419         SymReg && SymReg->getSymbol()->getOriginRegion()) {
420       Referrer = SymReg->getSymbol()->getOriginRegion()->getBaseRegion();
421     } else if (isa<CXXThisRegion>(Referrer)) {
422       // Skip members of a class, it is handled in CheckExprLifetime.cpp as
423       // warn_bind_ref_member_to_parameter or
424       // warn_init_ptr_member_to_parameter_addr
425       return std::nullopt;
426     } else if (isa<AllocaRegion>(Referrer)) {
427       // Skip alloca() regions, they indicate advanced memory management
428       // and higher likelihood of CSA false positives.
429       return std::nullopt;
430     } else {
431       assert(false && "Unexpected referrer region type.");
432       return std::nullopt;
433     }
434   }
435   assert(Referrer);
436   assert(Referrer->canPrintPretty());
437 
438   std::string buf;
439   llvm::raw_string_ostream os(buf);
440   os << ReferrerMemorySpace << " variable ";
441   Referrer->printPretty(os);
442   return buf;
443 }
444 
445 /// Check whether \p Region refers to a freshly minted symbol after an opaque
446 /// function call.
447 static bool isInvalidatedSymbolRegion(const MemRegion *Region) {
448   const auto *SymReg = Region->getAs<SymbolicRegion>();
449   if (!SymReg)
450     return false;
451   SymbolRef Symbol = SymReg->getSymbol();
452 
453   const auto *DerS = dyn_cast<SymbolDerived>(Symbol);
454   return DerS && isa_and_nonnull<SymbolConjured>(DerS->getParentSymbol());
455 }
456 
457 void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
458                                               CheckerContext &Ctx) const {
459   if (!ChecksEnabled[CK_StackAddrEscapeChecker])
460     return;
461 
462   ExplodedNode *Node = Ctx.getPredecessor();
463 
464   bool ExitingTopFrame =
465       Ctx.getPredecessor()->getLocationContext()->inTopFrame();
466 
467   if (ExitingTopFrame &&
468       Node->getLocation().getTag() == ExprEngine::cleanupNodeTag() &&
469       Node->getFirstPred()) {
470     // When finishing analysis of a top-level function, engine proactively
471     // removes dead symbols thus preventing this checker from looking through
472     // the output parameters. Take 1 step back, to the node where these symbols
473     // and their bindings are still present
474     Node = Node->getFirstPred();
475   }
476 
477   // Iterate over all bindings to global variables and see if it contains
478   // a memory region in the stack space.
479   class CallBack : public StoreManager::BindingsHandler {
480   private:
481     CheckerContext &Ctx;
482     ProgramStateRef State;
483     const StackFrameContext *PoppedFrame;
484     const bool TopFrame;
485 
486     /// Look for stack variables referring to popped stack variables.
487     /// Returns true only if it found some dangling stack variables
488     /// referred by an other stack variable from different stack frame.
489     bool checkForDanglingStackVariable(const MemRegion *Referrer,
490                                        const MemRegion *Referred) {
491       const auto *ReferrerMemSpace =
492           getStackOrGlobalSpaceRegion(State, Referrer);
493       const auto *ReferredMemSpace =
494           Referred->getMemorySpaceAs<StackSpaceRegion>(State);
495 
496       if (!ReferrerMemSpace || !ReferredMemSpace)
497         return false;
498 
499       const auto *ReferrerStackSpace =
500           ReferrerMemSpace->getAs<StackSpaceRegion>();
501 
502       if (!ReferrerStackSpace)
503         return false;
504 
505       if (const auto *ReferredFrame = ReferredMemSpace->getStackFrame();
506           ReferredFrame != PoppedFrame) {
507         return false;
508       }
509 
510       if (ReferrerStackSpace->getStackFrame()->isParentOf(PoppedFrame)) {
511         V.emplace_back(Referrer, Referred);
512         return true;
513       }
514       if (isa<StackArgumentsSpaceRegion>(ReferrerMemSpace) &&
515           // Not a simple ptr (int*) but something deeper, e.g. int**
516           isa<SymbolicRegion>(Referrer->getBaseRegion()) &&
517           ReferrerStackSpace->getStackFrame() == PoppedFrame && TopFrame) {
518         // Output parameter of a top-level function
519         V.emplace_back(Referrer, Referred);
520         return true;
521       }
522       return false;
523     }
524 
525     // Keep track of the variables that were invalidated through an opaque
526     // function call. Even if the initial values of such variables were bound to
527     // an address of a local variable, we cannot claim anything now, at the
528     // function exit, so skip them to avoid false positives.
529     void recordInInvalidatedRegions(const MemRegion *Region) {
530       if (isInvalidatedSymbolRegion(Region))
531         ExcludedRegions.insert(getOriginBaseRegion(Region));
532     }
533 
534   public:
535     SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10> V;
536     // ExcludedRegions are skipped from reporting.
537     // I.e., if a referrer in this set, skip the related bug report.
538     // It is useful to avoid false positive for the variables that were
539     // reset to a conjured value after an opaque function call.
540     llvm::SmallPtrSet<const MemRegion *, 4> ExcludedRegions;
541 
542     CallBack(CheckerContext &CC, bool TopFrame)
543         : Ctx(CC), State(CC.getState()), PoppedFrame(CC.getStackFrame()),
544           TopFrame(TopFrame) {}
545 
546     bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region,
547                        SVal Val) override {
548       recordInInvalidatedRegions(Region);
549       const MemRegion *VR = Val.getAsRegion();
550       if (!VR)
551         return true;
552 
553       if (checkForDanglingStackVariable(Region, VR))
554         return true;
555 
556       // Check the globals for the same.
557       if (!isa_and_nonnull<GlobalsSpaceRegion>(
558               getStackOrGlobalSpaceRegion(State, Region)))
559         return true;
560 
561       if (VR) {
562         if (const auto *S = VR->getMemorySpaceAs<StackSpaceRegion>(State);
563             S && !isNotInCurrentFrame(S, Ctx)) {
564           V.emplace_back(Region, VR);
565         }
566       }
567       return true;
568     }
569   };
570 
571   CallBack Cb(Ctx, ExitingTopFrame);
572   ProgramStateRef State = Node->getState();
573   State->getStateManager().getStoreManager().iterBindings(State->getStore(),
574                                                           Cb);
575 
576   if (Cb.V.empty())
577     return;
578 
579   // Generate an error node.
580   ExplodedNode *N = Ctx.generateNonFatalErrorNode(State, Node);
581   if (!N)
582     return;
583 
584   if (!BT_stackleak)
585     BT_stackleak =
586         std::make_unique<BugType>(CheckNames[CK_StackAddrEscapeChecker],
587                                   "Stack address leaks outside of stack frame");
588 
589   for (const auto &P : Cb.V) {
590     const MemRegion *Referrer = P.first->getBaseRegion();
591     const MemRegion *Referred = P.second;
592     if (Cb.ExcludedRegions.contains(getOriginBaseRegion(Referrer))) {
593       continue;
594     }
595 
596     // Generate a report for this bug.
597     const StringRef CommonSuffix =
598         " upon returning to the caller.  This will be a dangling reference";
599     SmallString<128> Buf;
600     llvm::raw_svector_ostream Out(Buf);
601     const SourceRange Range = genName(Out, Referred, Ctx.getASTContext());
602 
603     if (isa<CXXTempObjectRegion, CXXLifetimeExtendedObjectRegion>(Referrer)) {
604       Out << " is still referred to by a temporary object on the stack"
605           << CommonSuffix;
606       auto Report =
607           std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
608       if (Range.isValid())
609         Report->addRange(Range);
610       Ctx.emitReport(std::move(Report));
611       return;
612     }
613 
614     auto ReferrerVariable = printReferrer(State, Referrer);
615     if (!ReferrerVariable) {
616       continue;
617     }
618 
619     Out << " is still referred to by the " << *ReferrerVariable << CommonSuffix;
620     auto Report =
621         std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
622     if (Range.isValid())
623       Report->addRange(Range);
624 
625     Ctx.emitReport(std::move(Report));
626   }
627 }
628 
629 void ento::registerStackAddrEscapeBase(CheckerManager &mgr) {
630   mgr.registerChecker<StackAddrEscapeChecker>();
631 }
632 
633 bool ento::shouldRegisterStackAddrEscapeBase(const CheckerManager &mgr) {
634   return true;
635 }
636 
637 #define REGISTER_CHECKER(name)                                                 \
638   void ento::register##name(CheckerManager &Mgr) {                             \
639     StackAddrEscapeChecker *Chk = Mgr.getChecker<StackAddrEscapeChecker>();    \
640     Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true;              \
641     Chk->CheckNames[StackAddrEscapeChecker::CK_##name] =                       \
642         Mgr.getCurrentCheckerName();                                           \
643   }                                                                            \
644                                                                                \
645   bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
646 
647 REGISTER_CHECKER(StackAddrEscapeChecker)
648 REGISTER_CHECKER(StackAddrAsyncEscapeChecker)
649