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