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