24#include "llvm/ADT/STLExtras.h"
25#include "llvm/ADT/SmallPtrSet.h"
26#include "llvm/Support/raw_ostream.h"
31class StackAddrEscapeChecker
32 :
public CheckerFamily<check::PreCall, check::PreStmt<ReturnStmt>,
34 mutable IdentifierInfo *dispatch_semaphore_tII =
nullptr;
37 StringRef getDebugTag()
const override {
return "StackAddrEscapeChecker"; }
39 CheckerFrontend StackAddrEscape;
40 CheckerFrontend StackAddrAsyncEscape;
42 const BugType StackLeak{&StackAddrEscape,
43 "Stack address leaks outside of stack frame"};
44 const BugType ReturnStack{&StackAddrEscape,
45 "Return of address to stack-allocated memory"};
46 const BugType CapturedStackAsync{
47 &StackAddrAsyncEscape,
"Address of stack-allocated memory is captured"};
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;
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,
61 static SmallVector<std::pair<const MemRegion *, const StackSpaceRegion *>, 4>
62 getCapturedStackRegions(
const BlockDataRegion &B, CheckerContext &
C);
63 static bool isNotInCurrentFrame(
const StackSpaceRegion *MS,
77 if (
const auto *CR = dyn_cast<CompoundLiteralRegion>(R)) {
78 const CompoundLiteralExpr *CL = CR->getLiteralExpr();
79 os <<
"stack memory associated with a compound literal "
83 }
else if (
const auto *AR = dyn_cast<AllocaRegion>(R)) {
84 const Expr *ARE = AR->getExpr();
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();
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()
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 '";
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 '";
112 range = TOR->getExpr()->getSourceRange();
114 llvm_unreachable(
"Invalid region in ReturnStackAddressChecker.");
120bool StackAddrEscapeChecker::isNotInCurrentFrame(
const StackSpaceRegion *MS,
125bool StackAddrEscapeChecker::isSemaphoreCaptured(
const BlockDecl &B)
const {
126 if (!dispatch_semaphore_tII)
129 const auto *
T =
C.getVariable()->getType()->
getAs<TypedefType>();
130 if (
T &&
T->getDecl()->getIdentifier() == dispatch_semaphore_tII)
136SmallVector<std::pair<const MemRegion *, const StackSpaceRegion *>, 4>
137StackAddrEscapeChecker::getCapturedStackRegions(
const BlockDataRegion &B,
139 SmallVector<std::pair<const MemRegion *, const StackSpaceRegion *>, 4>
143 SVal Val = State->getSVal(Var.getCapturedRegion());
145 if (
const auto *Space =
146 Region->getMemorySpaceAs<StackSpaceRegion>(State)) {
147 Regions.emplace_back(Region, Space);
158 OS <<
" is captured by a returned block";
164 OS <<
" returned to caller";
167void StackAddrEscapeChecker::EmitReturnLeakError(CheckerContext &
C,
169 const Expr *RetE)
const {
170 ExplodedNode *N =
C.generateNonFatalErrorNode();
175 SmallString<128> buf;
176 llvm::raw_svector_ostream os(buf);
179 SourceRange
range = genName(os, R,
C.getASTContext());
183 std::make_unique<PathSensitiveBugReport>(ReturnStack, os.str(), N);
186 report->addRange(range);
187 C.emitReport(std::move(report));
190void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
191 const BlockDataRegion &B, CheckerContext &
C)
const {
199 if (isSemaphoreCaptured(*B.
getDecl()))
201 auto Regions = getCapturedStackRegions(B,
C);
202 for (
const MemRegion *Region : llvm::make_first_range(Regions)) {
211 ExplodedNode *N =
C.generateNonFatalErrorNode();
214 SmallString<128> Buf;
215 llvm::raw_svector_ostream
Out(Buf);
216 SourceRange
Range = genName(Out, Region,
C.getASTContext());
217 Out <<
" is captured by an asynchronously-executed block";
218 auto Report = std::make_unique<PathSensitiveBugReport>(CapturedStackAsync,
222 C.emitReport(std::move(
Report));
226void StackAddrEscapeChecker::checkPreCall(
const CallEvent &
Call,
227 CheckerContext &
C)
const {
230 if (!
Call.isGlobalCFunction(
"dispatch_after") &&
231 !
Call.isGlobalCFunction(
"dispatch_async"))
233 for (
unsigned Idx = 0, NumArgs =
Call.getNumArgs(); Idx < NumArgs; ++Idx) {
234 if (
const BlockDataRegion *B = dyn_cast_or_null<BlockDataRegion>(
235 Call.getArgSVal(Idx).getAsRegion()))
236 checkAsyncExecutedBlockCaptures(*B,
C);
257 : Ctxt(Ctxt), PoppedStackFrame(Ctxt.getStackFrame()),
258 EscapingStackRegions(StorageForStackRegions) {}
263 if (!VisitedRegions.insert(MR).second)
269 return VisitBlockDataRegionCaptures(BDR);
275 void SaveIfEscapes(
const MemRegion *MR) {
282 if (CapturedSFC == PoppedStackFrame ||
284 EscapingStackRegions.push_back(MR);
287 bool VisitBlockDataRegionCaptures(
const BlockDataRegion *BDR) {
289 SVal Val = Ctxt.getState()->getSVal(Var.getCapturedRegion());
292 SaveIfEscapes(Region);
293 VisitMemRegion(Region);
318 RetE = Cleanup->getSubExpr();
319 bool IsConstructExpr =
324 bool IsCopyAndAutoreleaseBlockObj =
false;
325 if (
const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) {
326 IsCopyAndAutoreleaseBlockObj =
327 isa_and_nonnull<BlockDataRegion>(RetRegion) &&
328 ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject;
331 for (
const MemRegion *MR : MaybeEscaped) {
332 if (RetRegion == MR && (IsCopyAndAutoreleaseBlockObj || IsConstructExpr))
335 WillEscape.push_back(MR);
343static SmallVector<const MemRegion *>
349 Scanner.scan(RetVal);
354void StackAddrEscapeChecker::checkPreStmt(
const ReturnStmt *RS,
355 CheckerContext &
C)
const {
364 SVal
V =
C.getSVal(RetE);
366 SmallVector<const MemRegion *> EscapedStackRegions =
369 for (
const MemRegion *ER : EscapedStackRegions)
370 EmitReturnLeakError(
C, ER, RetE);
383 if (
const auto *OriginReg = SymReg->getSymbol()->getOriginRegion())
391 while (
const auto *SymReg = dyn_cast<SymbolicRegion>(Reg)) {
392 const auto *OriginReg = SymReg->getSymbol()->getOriginRegion();
403 const StringRef ReferrerMemorySpace = [](
const MemSpaceRegion *Space) {
414 if (
const auto *SymReg = dyn_cast<SymbolicRegion>(Referrer);
415 SymReg && SymReg->getSymbol()->getOriginRegion()) {
416 Referrer = SymReg->getSymbol()->getOriginRegion()->
getBaseRegion();
427 assert(
false &&
"Unexpected referrer region type.");
435 llvm::raw_string_ostream os(buf);
436 os << ReferrerMemorySpace <<
" variable ";
449 const auto *DerS = dyn_cast<SymbolDerived>(Symbol);
450 return DerS && isa_and_nonnull<SymbolConjured>(DerS->getParentSymbol());
453void StackAddrEscapeChecker::checkEndFunction(
const ReturnStmt *RS,
454 CheckerContext &Ctx)
const {
460 bool ExitingTopFrame =
463 if (ExitingTopFrame &&
475 class CallBack :
public StoreManager::BindingsHandler {
479 const StackFrameContext *PoppedFrame;
485 bool checkForDanglingStackVariable(
const MemRegion *Referrer,
486 const MemRegion *Referred) {
487 const auto *ReferrerMemSpace =
489 const auto *ReferredMemSpace =
492 if (!ReferrerMemSpace || !ReferredMemSpace)
495 const auto *ReferrerStackSpace =
496 ReferrerMemSpace->getAs<StackSpaceRegion>();
498 if (!ReferrerStackSpace)
501 if (
const auto *ReferredFrame = ReferredMemSpace->getStackFrame();
502 ReferredFrame != PoppedFrame) {
506 if (ReferrerStackSpace->getStackFrame()->isParentOf(PoppedFrame)) {
507 V.emplace_back(Referrer, Referred);
513 ReferrerStackSpace->getStackFrame() == PoppedFrame && TopFrame) {
515 V.emplace_back(Referrer, Referred);
525 void recordInInvalidatedRegions(
const MemRegion *Region) {
531 SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10>
V;
536 llvm::SmallPtrSet<const MemRegion *, 4> ExcludedRegions;
538 CallBack(CheckerContext &CC,
bool TopFrame)
539 : Ctx(CC), State(CC.getState()), PoppedFrame(CC.getStackFrame()),
540 TopFrame(TopFrame) {}
542 bool HandleBinding(StoreManager &SMgr,
Store S,
const MemRegion *Region,
544 recordInInvalidatedRegions(Region);
549 if (checkForDanglingStackVariable(Region, VR))
553 if (!isa_and_nonnull<GlobalsSpaceRegion>(
559 S && !isNotInCurrentFrame(S, Ctx)) {
560 V.emplace_back(Region, VR);
567 CallBack Cb(Ctx, ExitingTopFrame);
569 State->getStateManager().getStoreManager().iterBindings(State->getStore(),
580 for (
const auto &P : Cb.V) {
582 const MemRegion *Referred = P.second;
588 const StringRef CommonSuffix =
589 " upon returning to the caller. This will be a dangling reference";
590 SmallString<128> Buf;
591 llvm::raw_svector_ostream
Out(Buf);
595 Out <<
" is still referred to by a temporary object on the stack"
598 std::make_unique<PathSensitiveBugReport>(StackLeak,
Out.str(), N);
606 if (!ReferrerVariable) {
610 Out <<
" is still referred to by the " << *ReferrerVariable << CommonSuffix;
612 std::make_unique<PathSensitiveBugReport>(StackLeak,
Out.str(), N);
620#define REGISTER_CHECKER(NAME) \
621 void ento::register##NAME##Checker(CheckerManager &Mgr) { \
622 Mgr.getChecker<StackAddrEscapeChecker>()->NAME.enable(Mgr); \
625 bool ento::shouldRegister##NAME##Checker(const CheckerManager &) { \
#define REGISTER_CHECKER(name)
Defines the clang::Expr interface and subclasses for C++ expressions.
Defines the SourceManager interface.
static SmallVector< const MemRegion * > FilterReturnExpressionLeaks(const SmallVectorImpl< const MemRegion * > &MaybeEscaped, CheckerContext &C, const Expr *RetE, SVal &RetVal)
Given some memory regions that are flagged by FindStackRegionsSymbolVisitor, this function filters ou...
static bool isInvalidatedSymbolRegion(const MemRegion *Region)
Check whether Region refers to a freshly minted symbol after an opaque function call.
static void EmitReturnedAsPartOfError(llvm::raw_ostream &OS, SVal ReturnedVal, const MemRegion *LeakedRegion)
static std::optional< std::string > printReferrer(ProgramStateRef State, const MemRegion *Referrer)
static const MemSpaceRegion * getStackOrGlobalSpaceRegion(ProgramStateRef State, const MemRegion *R)
static const MemRegion * getOriginBaseRegion(const MemRegion *Reg)
static SmallVector< const MemRegion * > FindEscapingStackRegions(CheckerContext &C, const Expr *RetE, SVal RetVal)
For use in finding regions that live on the checker context's current stack frame,...
A visitor made for use with a ScanReachableSymbols scanner, used for finding stack regions within an ...
bool VisitMemRegion(const MemRegion *MR) override
FindStackRegionsSymbolVisitor(CheckerContext &Ctxt, SmallVectorImpl< const MemRegion * > &StorageForStackRegions)
bool VisitSymbol(SymbolRef sym) override
A visitor method invoked by ProgramStateManager::scanReachableSymbols.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
SourceManager & getSourceManager()
const clang::PrintingPolicy & getPrintingPolicy() const
ArrayRef< Capture > captures() const
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
SourceLocation getBeginLoc() const LLVM_READONLY
ASTContext & getASTContext() const LLVM_READONLY
SourceLocation getBeginLoc() const LLVM_READONLY
Represents an expression – generally a full-expression – that introduces cleanups to be run at the en...
This represents one expression.
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
bool isParentOf(const LocationContext *LC) const
const StackFrameContext * getStackFrame() const
virtual bool inTopFrame() const
const ProgramPointTag * getTag() const
void print(raw_ostream &OS, const PrintingPolicy &Policy, const Twine &PlaceHolder=Twine(), unsigned Indentation=0) const
A trivial tuple used to represent a source range.
It represents a stack frame of the call stack (based on CallEvent).
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
SourceLocation getBeginLoc() const LLVM_READONLY
const T * getAs() const
Member-template getAs<specific type>'.
bool isRecordType() const
BlockDataRegion - A region that represents a block instance.
LLVM_ATTRIBUTE_RETURNS_NONNULL const BlockDecl * getDecl() const
llvm::iterator_range< referenced_vars_iterator > referenced_vars() const
ExplodedNode * getPredecessor()
Returns the previous node in the exploded graph, which includes the state of the program before the c...
ASTContext & getASTContext()
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
Checker families (where a single backend class implements multiple related frontends) should derive f...
const ProgramStateRef & getState() const
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
const LocationContext * getLocationContext() const
ExplodedNode * getFirstPred()
static const ProgramPointTag * cleanupNodeTag()
A tag to track convenience transitions, which can be removed at cleanup.
MemRegion - The root abstract class for all memory regions.
const MemSpace * getMemorySpaceAs(ProgramStateRef State) const
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemSpaceRegion * getMemorySpace(ProgramStateRef State) const
Returns the most specific memory space for this memory region in the given ProgramStateRef.
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getBaseRegion() const
virtual void printPretty(raw_ostream &os) const
Print the region for use in diagnostics.
const RegionTy * getAs() const
virtual bool canPrintPretty() const
Returns true if this region can be printed in a user-friendly way.
MemSpaceRegion - A memory region that represents a "memory space"; for example, the set of global var...
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
const MemRegion * getAsRegion() const
A utility class that visits the reachable symbols using a custom SymbolVisitor.
LLVM_ATTRIBUTE_RETURNS_NONNULL const StackFrameContext * getStackFrame() const
SymbolicRegion - A special, "non-concrete" region.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const SymExpr * SymbolRef
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
const void * Store
Store - This opaque type encapsulates an immutable mapping from locations to values.
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
const FunctionProtoType * T