10b57cec5SDimitry Andric //=== StackAddrEscapeChecker.cpp ----------------------------------*- C++ -*--//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file defines stack address leak checker, which checks if an invalid
100b57cec5SDimitry Andric // stack address is stored into a global or heap location. See CERT DCL30-C.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "clang/AST/ExprCXX.h"
150b57cec5SDimitry Andric #include "clang/Basic/SourceManager.h"
16349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
230b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
240b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
250b57cec5SDimitry Andric using namespace clang;
260b57cec5SDimitry Andric using namespace ento;
270b57cec5SDimitry Andric 
280b57cec5SDimitry Andric namespace {
290b57cec5SDimitry Andric class StackAddrEscapeChecker
300b57cec5SDimitry Andric     : public Checker<check::PreCall, check::PreStmt<ReturnStmt>,
310b57cec5SDimitry Andric                      check::EndFunction> {
3206c3fb27SDimitry Andric   mutable IdentifierInfo *dispatch_semaphore_tII = nullptr;
335f757f3fSDimitry Andric   mutable std::unique_ptr<BugType> BT_stackleak;
345f757f3fSDimitry Andric   mutable std::unique_ptr<BugType> BT_returnstack;
355f757f3fSDimitry Andric   mutable std::unique_ptr<BugType> BT_capturedstackasync;
365f757f3fSDimitry Andric   mutable std::unique_ptr<BugType> BT_capturedstackret;
370b57cec5SDimitry Andric 
380b57cec5SDimitry Andric public:
390b57cec5SDimitry Andric   enum CheckKind {
400b57cec5SDimitry Andric     CK_StackAddrEscapeChecker,
410b57cec5SDimitry Andric     CK_StackAddrAsyncEscapeChecker,
420b57cec5SDimitry Andric     CK_NumCheckKinds
430b57cec5SDimitry Andric   };
440b57cec5SDimitry Andric 
4581ad6265SDimitry Andric   bool ChecksEnabled[CK_NumCheckKinds] = {false};
465ffd83dbSDimitry Andric   CheckerNameRef CheckNames[CK_NumCheckKinds];
470b57cec5SDimitry Andric 
480b57cec5SDimitry Andric   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
490b57cec5SDimitry Andric   void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
500b57cec5SDimitry Andric   void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const;
510b57cec5SDimitry Andric 
520b57cec5SDimitry Andric private:
530b57cec5SDimitry Andric   void checkReturnedBlockCaptures(const BlockDataRegion &B,
540b57cec5SDimitry Andric                                   CheckerContext &C) const;
550b57cec5SDimitry Andric   void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B,
560b57cec5SDimitry Andric                                        CheckerContext &C) const;
570b57cec5SDimitry Andric   void EmitStackError(CheckerContext &C, const MemRegion *R,
580b57cec5SDimitry Andric                       const Expr *RetE) const;
590b57cec5SDimitry Andric   bool isSemaphoreCaptured(const BlockDecl &B) const;
600b57cec5SDimitry Andric   static SourceRange genName(raw_ostream &os, const MemRegion *R,
610b57cec5SDimitry Andric                              ASTContext &Ctx);
620b57cec5SDimitry Andric   static SmallVector<const MemRegion *, 4>
630b57cec5SDimitry Andric   getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C);
640b57cec5SDimitry Andric   static bool isNotInCurrentFrame(const MemRegion *R, CheckerContext &C);
650b57cec5SDimitry Andric };
660b57cec5SDimitry Andric } // namespace
670b57cec5SDimitry Andric 
genName(raw_ostream & os,const MemRegion * R,ASTContext & Ctx)680b57cec5SDimitry Andric SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R,
690b57cec5SDimitry Andric                                             ASTContext &Ctx) {
700b57cec5SDimitry Andric   // Get the base region, stripping away fields and elements.
710b57cec5SDimitry Andric   R = R->getBaseRegion();
720b57cec5SDimitry Andric   SourceManager &SM = Ctx.getSourceManager();
730b57cec5SDimitry Andric   SourceRange range;
740b57cec5SDimitry Andric   os << "Address of ";
750b57cec5SDimitry Andric 
760b57cec5SDimitry Andric   // Check if the region is a compound literal.
770b57cec5SDimitry Andric   if (const auto *CR = dyn_cast<CompoundLiteralRegion>(R)) {
780b57cec5SDimitry Andric     const CompoundLiteralExpr *CL = CR->getLiteralExpr();
790b57cec5SDimitry Andric     os << "stack memory associated with a compound literal "
800b57cec5SDimitry Andric           "declared on line "
810b57cec5SDimitry Andric        << SM.getExpansionLineNumber(CL->getBeginLoc()) << " returned to caller";
820b57cec5SDimitry Andric     range = CL->getSourceRange();
830b57cec5SDimitry Andric   } else if (const auto *AR = dyn_cast<AllocaRegion>(R)) {
840b57cec5SDimitry Andric     const Expr *ARE = AR->getExpr();
850b57cec5SDimitry Andric     SourceLocation L = ARE->getBeginLoc();
860b57cec5SDimitry Andric     range = ARE->getSourceRange();
870b57cec5SDimitry Andric     os << "stack memory allocated by call to alloca() on line "
880b57cec5SDimitry Andric        << SM.getExpansionLineNumber(L);
890b57cec5SDimitry Andric   } else if (const auto *BR = dyn_cast<BlockDataRegion>(R)) {
900b57cec5SDimitry Andric     const BlockDecl *BD = BR->getCodeRegion()->getDecl();
910b57cec5SDimitry Andric     SourceLocation L = BD->getBeginLoc();
920b57cec5SDimitry Andric     range = BD->getSourceRange();
930b57cec5SDimitry Andric     os << "stack-allocated block declared on line "
940b57cec5SDimitry Andric        << SM.getExpansionLineNumber(L);
950b57cec5SDimitry Andric   } else if (const auto *VR = dyn_cast<VarRegion>(R)) {
960b57cec5SDimitry Andric     os << "stack memory associated with local variable '" << VR->getString()
970b57cec5SDimitry Andric        << '\'';
980b57cec5SDimitry Andric     range = VR->getDecl()->getSourceRange();
9906c3fb27SDimitry Andric   } else if (const auto *LER = dyn_cast<CXXLifetimeExtendedObjectRegion>(R)) {
10006c3fb27SDimitry Andric     QualType Ty = LER->getValueType().getLocalUnqualifiedType();
10106c3fb27SDimitry Andric     os << "stack memory associated with temporary object of type '";
10206c3fb27SDimitry Andric     Ty.print(os, Ctx.getPrintingPolicy());
10306c3fb27SDimitry Andric     os << "' lifetime extended by local variable";
10406c3fb27SDimitry Andric     if (const IdentifierInfo *ID = LER->getExtendingDecl()->getIdentifier())
10506c3fb27SDimitry Andric       os << " '" << ID->getName() << '\'';
10606c3fb27SDimitry Andric     range = LER->getExpr()->getSourceRange();
1070b57cec5SDimitry Andric   } else if (const auto *TOR = dyn_cast<CXXTempObjectRegion>(R)) {
1080b57cec5SDimitry Andric     QualType Ty = TOR->getValueType().getLocalUnqualifiedType();
1090b57cec5SDimitry Andric     os << "stack memory associated with temporary object of type '";
1100b57cec5SDimitry Andric     Ty.print(os, Ctx.getPrintingPolicy());
1110b57cec5SDimitry Andric     os << "'";
1120b57cec5SDimitry Andric     range = TOR->getExpr()->getSourceRange();
1130b57cec5SDimitry Andric   } else {
1140b57cec5SDimitry Andric     llvm_unreachable("Invalid region in ReturnStackAddressChecker.");
1150b57cec5SDimitry Andric   }
1160b57cec5SDimitry Andric 
1170b57cec5SDimitry Andric   return range;
1180b57cec5SDimitry Andric }
1190b57cec5SDimitry Andric 
isNotInCurrentFrame(const MemRegion * R,CheckerContext & C)1200b57cec5SDimitry Andric bool StackAddrEscapeChecker::isNotInCurrentFrame(const MemRegion *R,
1210b57cec5SDimitry Andric                                                  CheckerContext &C) {
1220b57cec5SDimitry Andric   const StackSpaceRegion *S = cast<StackSpaceRegion>(R->getMemorySpace());
1230b57cec5SDimitry Andric   return S->getStackFrame() != C.getStackFrame();
1240b57cec5SDimitry Andric }
1250b57cec5SDimitry Andric 
isSemaphoreCaptured(const BlockDecl & B) const1260b57cec5SDimitry Andric bool StackAddrEscapeChecker::isSemaphoreCaptured(const BlockDecl &B) const {
1270b57cec5SDimitry Andric   if (!dispatch_semaphore_tII)
1280b57cec5SDimitry Andric     dispatch_semaphore_tII = &B.getASTContext().Idents.get("dispatch_semaphore_t");
1290b57cec5SDimitry Andric   for (const auto &C : B.captures()) {
1300b57cec5SDimitry Andric     const auto *T = C.getVariable()->getType()->getAs<TypedefType>();
1310b57cec5SDimitry Andric     if (T && T->getDecl()->getIdentifier() == dispatch_semaphore_tII)
1320b57cec5SDimitry Andric       return true;
1330b57cec5SDimitry Andric   }
1340b57cec5SDimitry Andric   return false;
1350b57cec5SDimitry Andric }
1360b57cec5SDimitry Andric 
1370b57cec5SDimitry Andric SmallVector<const MemRegion *, 4>
getCapturedStackRegions(const BlockDataRegion & B,CheckerContext & C)1380b57cec5SDimitry Andric StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B,
1390b57cec5SDimitry Andric                                                 CheckerContext &C) {
1400b57cec5SDimitry Andric   SmallVector<const MemRegion *, 4> Regions;
14106c3fb27SDimitry Andric   for (auto Var : B.referenced_vars()) {
14206c3fb27SDimitry Andric     SVal Val = C.getState()->getSVal(Var.getCapturedRegion());
1430b57cec5SDimitry Andric     const MemRegion *Region = Val.getAsRegion();
1440b57cec5SDimitry Andric     if (Region && isa<StackSpaceRegion>(Region->getMemorySpace()))
1450b57cec5SDimitry Andric       Regions.push_back(Region);
1460b57cec5SDimitry Andric   }
1470b57cec5SDimitry Andric   return Regions;
1480b57cec5SDimitry Andric }
1490b57cec5SDimitry Andric 
EmitStackError(CheckerContext & C,const MemRegion * R,const Expr * RetE) const1500b57cec5SDimitry Andric void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,
1510b57cec5SDimitry Andric                                             const MemRegion *R,
1520b57cec5SDimitry Andric                                             const Expr *RetE) const {
1530b57cec5SDimitry Andric   ExplodedNode *N = C.generateNonFatalErrorNode();
1540b57cec5SDimitry Andric   if (!N)
1550b57cec5SDimitry Andric     return;
1560b57cec5SDimitry Andric   if (!BT_returnstack)
1575f757f3fSDimitry Andric     BT_returnstack = std::make_unique<BugType>(
1585ffd83dbSDimitry Andric         CheckNames[CK_StackAddrEscapeChecker],
1595ffd83dbSDimitry Andric         "Return of address to stack-allocated memory");
1600b57cec5SDimitry Andric   // Generate a report for this bug.
1610b57cec5SDimitry Andric   SmallString<128> buf;
1620b57cec5SDimitry Andric   llvm::raw_svector_ostream os(buf);
1630b57cec5SDimitry Andric   SourceRange range = genName(os, R, C.getASTContext());
1640b57cec5SDimitry Andric   os << " returned to caller";
165a7dea167SDimitry Andric   auto report =
166a7dea167SDimitry Andric       std::make_unique<PathSensitiveBugReport>(*BT_returnstack, os.str(), N);
1670b57cec5SDimitry Andric   report->addRange(RetE->getSourceRange());
1680b57cec5SDimitry Andric   if (range.isValid())
1690b57cec5SDimitry Andric     report->addRange(range);
1700b57cec5SDimitry Andric   C.emitReport(std::move(report));
1710b57cec5SDimitry Andric }
1720b57cec5SDimitry Andric 
checkAsyncExecutedBlockCaptures(const BlockDataRegion & B,CheckerContext & C) const1730b57cec5SDimitry Andric void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
1740b57cec5SDimitry Andric     const BlockDataRegion &B, CheckerContext &C) const {
1750b57cec5SDimitry Andric   // There is a not-too-uncommon idiom
1760b57cec5SDimitry Andric   // where a block passed to dispatch_async captures a semaphore
1770b57cec5SDimitry Andric   // and then the thread (which called dispatch_async) is blocked on waiting
1780b57cec5SDimitry Andric   // for the completion of the execution of the block
1790b57cec5SDimitry Andric   // via dispatch_semaphore_wait. To avoid false-positives (for now)
1800b57cec5SDimitry Andric   // we ignore all the blocks which have captured
1810b57cec5SDimitry Andric   // a variable of the type "dispatch_semaphore_t".
1820b57cec5SDimitry Andric   if (isSemaphoreCaptured(*B.getDecl()))
1830b57cec5SDimitry Andric     return;
1840b57cec5SDimitry Andric   for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
1850b57cec5SDimitry Andric     // The block passed to dispatch_async may capture another block
1860b57cec5SDimitry Andric     // created on the stack. However, there is no leak in this situaton,
1870b57cec5SDimitry Andric     // no matter if ARC or no ARC is enabled:
1880b57cec5SDimitry Andric     // dispatch_async copies the passed "outer" block (via Block_copy)
1890b57cec5SDimitry Andric     // and if the block has captured another "inner" block,
1900b57cec5SDimitry Andric     // the "inner" block will be copied as well.
1910b57cec5SDimitry Andric     if (isa<BlockDataRegion>(Region))
1920b57cec5SDimitry Andric       continue;
1930b57cec5SDimitry Andric     ExplodedNode *N = C.generateNonFatalErrorNode();
1940b57cec5SDimitry Andric     if (!N)
1950b57cec5SDimitry Andric       continue;
1960b57cec5SDimitry Andric     if (!BT_capturedstackasync)
1975f757f3fSDimitry Andric       BT_capturedstackasync = std::make_unique<BugType>(
1985ffd83dbSDimitry Andric           CheckNames[CK_StackAddrAsyncEscapeChecker],
1995ffd83dbSDimitry Andric           "Address of stack-allocated memory is captured");
2000b57cec5SDimitry Andric     SmallString<128> Buf;
2010b57cec5SDimitry Andric     llvm::raw_svector_ostream Out(Buf);
2020b57cec5SDimitry Andric     SourceRange Range = genName(Out, Region, C.getASTContext());
2030b57cec5SDimitry Andric     Out << " is captured by an asynchronously-executed block";
204a7dea167SDimitry Andric     auto Report = std::make_unique<PathSensitiveBugReport>(
205a7dea167SDimitry Andric         *BT_capturedstackasync, Out.str(), N);
2060b57cec5SDimitry Andric     if (Range.isValid())
2070b57cec5SDimitry Andric       Report->addRange(Range);
2080b57cec5SDimitry Andric     C.emitReport(std::move(Report));
2090b57cec5SDimitry Andric   }
2100b57cec5SDimitry Andric }
2110b57cec5SDimitry Andric 
checkReturnedBlockCaptures(const BlockDataRegion & B,CheckerContext & C) const2120b57cec5SDimitry Andric void StackAddrEscapeChecker::checkReturnedBlockCaptures(
2130b57cec5SDimitry Andric     const BlockDataRegion &B, CheckerContext &C) const {
2140b57cec5SDimitry Andric   for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
215bdd1243dSDimitry Andric     if (isNotInCurrentFrame(Region, C))
2160b57cec5SDimitry Andric       continue;
2170b57cec5SDimitry Andric     ExplodedNode *N = C.generateNonFatalErrorNode();
2180b57cec5SDimitry Andric     if (!N)
2190b57cec5SDimitry Andric       continue;
2200b57cec5SDimitry Andric     if (!BT_capturedstackret)
2215f757f3fSDimitry Andric       BT_capturedstackret = std::make_unique<BugType>(
2225ffd83dbSDimitry Andric           CheckNames[CK_StackAddrEscapeChecker],
2235ffd83dbSDimitry Andric           "Address of stack-allocated memory is captured");
2240b57cec5SDimitry Andric     SmallString<128> Buf;
2250b57cec5SDimitry Andric     llvm::raw_svector_ostream Out(Buf);
2260b57cec5SDimitry Andric     SourceRange Range = genName(Out, Region, C.getASTContext());
2270b57cec5SDimitry Andric     Out << " is captured by a returned block";
228a7dea167SDimitry Andric     auto Report = std::make_unique<PathSensitiveBugReport>(*BT_capturedstackret,
229a7dea167SDimitry Andric                                                            Out.str(), N);
2300b57cec5SDimitry Andric     if (Range.isValid())
2310b57cec5SDimitry Andric       Report->addRange(Range);
2320b57cec5SDimitry Andric     C.emitReport(std::move(Report));
2330b57cec5SDimitry Andric   }
2340b57cec5SDimitry Andric }
2350b57cec5SDimitry Andric 
checkPreCall(const CallEvent & Call,CheckerContext & C) const2360b57cec5SDimitry Andric void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
2370b57cec5SDimitry Andric                                           CheckerContext &C) const {
2380b57cec5SDimitry Andric   if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
2390b57cec5SDimitry Andric     return;
2400b57cec5SDimitry Andric   if (!Call.isGlobalCFunction("dispatch_after") &&
2410b57cec5SDimitry Andric       !Call.isGlobalCFunction("dispatch_async"))
2420b57cec5SDimitry Andric     return;
2430b57cec5SDimitry Andric   for (unsigned Idx = 0, NumArgs = Call.getNumArgs(); Idx < NumArgs; ++Idx) {
2440b57cec5SDimitry Andric     if (const BlockDataRegion *B = dyn_cast_or_null<BlockDataRegion>(
2450b57cec5SDimitry Andric             Call.getArgSVal(Idx).getAsRegion()))
2460b57cec5SDimitry Andric       checkAsyncExecutedBlockCaptures(*B, C);
2470b57cec5SDimitry Andric   }
2480b57cec5SDimitry Andric }
2490b57cec5SDimitry Andric 
checkPreStmt(const ReturnStmt * RS,CheckerContext & C) const2500b57cec5SDimitry Andric void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
2510b57cec5SDimitry Andric                                           CheckerContext &C) const {
2520b57cec5SDimitry Andric   if (!ChecksEnabled[CK_StackAddrEscapeChecker])
2530b57cec5SDimitry Andric     return;
2540b57cec5SDimitry Andric 
2550b57cec5SDimitry Andric   const Expr *RetE = RS->getRetValue();
2560b57cec5SDimitry Andric   if (!RetE)
2570b57cec5SDimitry Andric     return;
2580b57cec5SDimitry Andric   RetE = RetE->IgnoreParens();
2590b57cec5SDimitry Andric 
2600b57cec5SDimitry Andric   SVal V = C.getSVal(RetE);
2610b57cec5SDimitry Andric   const MemRegion *R = V.getAsRegion();
2620b57cec5SDimitry Andric   if (!R)
2630b57cec5SDimitry Andric     return;
2640b57cec5SDimitry Andric 
2650b57cec5SDimitry Andric   if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R))
2660b57cec5SDimitry Andric     checkReturnedBlockCaptures(*B, C);
2670b57cec5SDimitry Andric 
268bdd1243dSDimitry Andric   if (!isa<StackSpaceRegion>(R->getMemorySpace()) || isNotInCurrentFrame(R, C))
2690b57cec5SDimitry Andric     return;
2700b57cec5SDimitry Andric 
2710b57cec5SDimitry Andric   // Returning a record by value is fine. (In this case, the returned
2720b57cec5SDimitry Andric   // expression will be a copy-constructor, possibly wrapped in an
2730b57cec5SDimitry Andric   // ExprWithCleanups node.)
2740b57cec5SDimitry Andric   if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE))
2750b57cec5SDimitry Andric     RetE = Cleanup->getSubExpr();
2760b57cec5SDimitry Andric   if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType())
2770b57cec5SDimitry Andric     return;
2780b57cec5SDimitry Andric 
2790b57cec5SDimitry Andric   // The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied
2800b57cec5SDimitry Andric   // so the stack address is not escaping here.
2815ffd83dbSDimitry Andric   if (const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) {
2820b57cec5SDimitry Andric     if (isa<BlockDataRegion>(R) &&
2830b57cec5SDimitry Andric         ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) {
2840b57cec5SDimitry Andric       return;
2850b57cec5SDimitry Andric     }
2860b57cec5SDimitry Andric   }
2870b57cec5SDimitry Andric 
2880b57cec5SDimitry Andric   EmitStackError(C, R, RetE);
2890b57cec5SDimitry Andric }
2900b57cec5SDimitry Andric 
checkEndFunction(const ReturnStmt * RS,CheckerContext & Ctx) const2910b57cec5SDimitry Andric void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
2920b57cec5SDimitry Andric                                               CheckerContext &Ctx) const {
2930b57cec5SDimitry Andric   if (!ChecksEnabled[CK_StackAddrEscapeChecker])
2940b57cec5SDimitry Andric     return;
2950b57cec5SDimitry Andric 
2960b57cec5SDimitry Andric   ProgramStateRef State = Ctx.getState();
2970b57cec5SDimitry Andric 
2980b57cec5SDimitry Andric   // Iterate over all bindings to global variables and see if it contains
2990b57cec5SDimitry Andric   // a memory region in the stack space.
3000b57cec5SDimitry Andric   class CallBack : public StoreManager::BindingsHandler {
3010b57cec5SDimitry Andric   private:
3020b57cec5SDimitry Andric     CheckerContext &Ctx;
303349cc55cSDimitry Andric     const StackFrameContext *PoppedFrame;
304349cc55cSDimitry Andric 
305349cc55cSDimitry Andric     /// Look for stack variables referring to popped stack variables.
306349cc55cSDimitry Andric     /// Returns true only if it found some dangling stack variables
307349cc55cSDimitry Andric     /// referred by an other stack variable from different stack frame.
308349cc55cSDimitry Andric     bool checkForDanglingStackVariable(const MemRegion *Referrer,
309349cc55cSDimitry Andric                                        const MemRegion *Referred) {
310349cc55cSDimitry Andric       const auto *ReferrerMemSpace =
311349cc55cSDimitry Andric           Referrer->getMemorySpace()->getAs<StackSpaceRegion>();
312349cc55cSDimitry Andric       const auto *ReferredMemSpace =
313349cc55cSDimitry Andric           Referred->getMemorySpace()->getAs<StackSpaceRegion>();
314349cc55cSDimitry Andric 
315349cc55cSDimitry Andric       if (!ReferrerMemSpace || !ReferredMemSpace)
316349cc55cSDimitry Andric         return false;
317349cc55cSDimitry Andric 
318349cc55cSDimitry Andric       const auto *ReferrerFrame = ReferrerMemSpace->getStackFrame();
319349cc55cSDimitry Andric       const auto *ReferredFrame = ReferredMemSpace->getStackFrame();
320349cc55cSDimitry Andric 
321349cc55cSDimitry Andric       if (ReferrerMemSpace && ReferredMemSpace) {
322349cc55cSDimitry Andric         if (ReferredFrame == PoppedFrame &&
323349cc55cSDimitry Andric             ReferrerFrame->isParentOf(PoppedFrame)) {
324349cc55cSDimitry Andric           V.emplace_back(Referrer, Referred);
325349cc55cSDimitry Andric           return true;
326349cc55cSDimitry Andric         }
327349cc55cSDimitry Andric       }
328349cc55cSDimitry Andric       return false;
329349cc55cSDimitry Andric     }
3300b57cec5SDimitry Andric 
3310b57cec5SDimitry Andric   public:
3320b57cec5SDimitry Andric     SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10> V;
3330b57cec5SDimitry Andric 
334349cc55cSDimitry Andric     CallBack(CheckerContext &CC) : Ctx(CC), PoppedFrame(CC.getStackFrame()) {}
3350b57cec5SDimitry Andric 
3360b57cec5SDimitry Andric     bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region,
3370b57cec5SDimitry Andric                        SVal Val) override {
338349cc55cSDimitry Andric       const MemRegion *VR = Val.getAsRegion();
339349cc55cSDimitry Andric       if (!VR)
340349cc55cSDimitry Andric         return true;
3410b57cec5SDimitry Andric 
342349cc55cSDimitry Andric       if (checkForDanglingStackVariable(Region, VR))
343349cc55cSDimitry Andric         return true;
344349cc55cSDimitry Andric 
345349cc55cSDimitry Andric       // Check the globals for the same.
3460b57cec5SDimitry Andric       if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace()))
3470b57cec5SDimitry Andric         return true;
348bdd1243dSDimitry Andric       if (VR && VR->hasStackStorage() && !isNotInCurrentFrame(VR, Ctx))
3490b57cec5SDimitry Andric         V.emplace_back(Region, VR);
3500b57cec5SDimitry Andric       return true;
3510b57cec5SDimitry Andric     }
3520b57cec5SDimitry Andric   };
3530b57cec5SDimitry Andric 
3540b57cec5SDimitry Andric   CallBack Cb(Ctx);
3550b57cec5SDimitry Andric   State->getStateManager().getStoreManager().iterBindings(State->getStore(),
3560b57cec5SDimitry Andric                                                           Cb);
3570b57cec5SDimitry Andric 
3580b57cec5SDimitry Andric   if (Cb.V.empty())
3590b57cec5SDimitry Andric     return;
3600b57cec5SDimitry Andric 
3610b57cec5SDimitry Andric   // Generate an error node.
3620b57cec5SDimitry Andric   ExplodedNode *N = Ctx.generateNonFatalErrorNode(State);
3630b57cec5SDimitry Andric   if (!N)
3640b57cec5SDimitry Andric     return;
3650b57cec5SDimitry Andric 
3660b57cec5SDimitry Andric   if (!BT_stackleak)
3675f757f3fSDimitry Andric     BT_stackleak =
3685f757f3fSDimitry Andric         std::make_unique<BugType>(CheckNames[CK_StackAddrEscapeChecker],
3695f757f3fSDimitry Andric                                   "Stack address stored into global variable");
3700b57cec5SDimitry Andric 
3710b57cec5SDimitry Andric   for (const auto &P : Cb.V) {
3725f757f3fSDimitry Andric     const MemRegion *Referrer = P.first->getBaseRegion();
373349cc55cSDimitry Andric     const MemRegion *Referred = P.second;
374349cc55cSDimitry Andric 
3750b57cec5SDimitry Andric     // Generate a report for this bug.
376349cc55cSDimitry Andric     const StringRef CommonSuffix =
377349cc55cSDimitry Andric         "upon returning to the caller.  This will be a dangling reference";
3780b57cec5SDimitry Andric     SmallString<128> Buf;
3790b57cec5SDimitry Andric     llvm::raw_svector_ostream Out(Buf);
380349cc55cSDimitry Andric     const SourceRange Range = genName(Out, Referred, Ctx.getASTContext());
381349cc55cSDimitry Andric 
38206c3fb27SDimitry Andric     if (isa<CXXTempObjectRegion, CXXLifetimeExtendedObjectRegion>(Referrer)) {
383349cc55cSDimitry Andric       Out << " is still referred to by a temporary object on the stack "
384349cc55cSDimitry Andric           << CommonSuffix;
385349cc55cSDimitry Andric       auto Report =
386349cc55cSDimitry Andric           std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
3875f757f3fSDimitry Andric       if (Range.isValid())
3885f757f3fSDimitry Andric         Report->addRange(Range);
389349cc55cSDimitry Andric       Ctx.emitReport(std::move(Report));
390349cc55cSDimitry Andric       return;
391349cc55cSDimitry Andric     }
392349cc55cSDimitry Andric 
393349cc55cSDimitry Andric     const StringRef ReferrerMemorySpace = [](const MemSpaceRegion *Space) {
394349cc55cSDimitry Andric       if (isa<StaticGlobalSpaceRegion>(Space))
395349cc55cSDimitry Andric         return "static";
396349cc55cSDimitry Andric       if (isa<GlobalsSpaceRegion>(Space))
397349cc55cSDimitry Andric         return "global";
398349cc55cSDimitry Andric       assert(isa<StackSpaceRegion>(Space));
399349cc55cSDimitry Andric       return "stack";
400349cc55cSDimitry Andric     }(Referrer->getMemorySpace());
401349cc55cSDimitry Andric 
4025f757f3fSDimitry Andric     // We should really only have VarRegions here.
4035f757f3fSDimitry Andric     // Anything else is really surprising, and we should get notified if such
4045f757f3fSDimitry Andric     // ever happens.
4055f757f3fSDimitry Andric     const auto *ReferrerVar = dyn_cast<VarRegion>(Referrer);
4065f757f3fSDimitry Andric     if (!ReferrerVar) {
4075f757f3fSDimitry Andric       assert(false && "We should have a VarRegion here");
4085f757f3fSDimitry Andric       continue; // Defensively skip this one.
4095f757f3fSDimitry Andric     }
410349cc55cSDimitry Andric     const std::string ReferrerVarName =
411349cc55cSDimitry Andric         ReferrerVar->getDecl()->getDeclName().getAsString();
412349cc55cSDimitry Andric 
413349cc55cSDimitry Andric     Out << " is still referred to by the " << ReferrerMemorySpace
414349cc55cSDimitry Andric         << " variable '" << ReferrerVarName << "' " << CommonSuffix;
415a7dea167SDimitry Andric     auto Report =
416a7dea167SDimitry Andric         std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
4170b57cec5SDimitry Andric     if (Range.isValid())
4180b57cec5SDimitry Andric       Report->addRange(Range);
4190b57cec5SDimitry Andric 
4200b57cec5SDimitry Andric     Ctx.emitReport(std::move(Report));
4210b57cec5SDimitry Andric   }
4220b57cec5SDimitry Andric }
4230b57cec5SDimitry Andric 
registerStackAddrEscapeBase(CheckerManager & mgr)4240b57cec5SDimitry Andric void ento::registerStackAddrEscapeBase(CheckerManager &mgr) {
4250b57cec5SDimitry Andric   mgr.registerChecker<StackAddrEscapeChecker>();
4260b57cec5SDimitry Andric }
4270b57cec5SDimitry Andric 
shouldRegisterStackAddrEscapeBase(const CheckerManager & mgr)4285ffd83dbSDimitry Andric bool ento::shouldRegisterStackAddrEscapeBase(const CheckerManager &mgr) {
4290b57cec5SDimitry Andric   return true;
4300b57cec5SDimitry Andric }
4310b57cec5SDimitry Andric 
4320b57cec5SDimitry Andric #define REGISTER_CHECKER(name)                                                 \
4330b57cec5SDimitry Andric   void ento::register##name(CheckerManager &Mgr) {                             \
4345ffd83dbSDimitry Andric     StackAddrEscapeChecker *Chk = Mgr.getChecker<StackAddrEscapeChecker>();    \
4350b57cec5SDimitry Andric     Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true;              \
4365ffd83dbSDimitry Andric     Chk->CheckNames[StackAddrEscapeChecker::CK_##name] =                       \
4375ffd83dbSDimitry Andric         Mgr.getCurrentCheckerName();                                           \
4380b57cec5SDimitry Andric   }                                                                            \
4390b57cec5SDimitry Andric                                                                                \
4405ffd83dbSDimitry Andric   bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
4410b57cec5SDimitry Andric 
4420b57cec5SDimitry Andric REGISTER_CHECKER(StackAddrEscapeChecker)
4430b57cec5SDimitry Andric REGISTER_CHECKER(StackAddrAsyncEscapeChecker)
444