24#include "llvm/ADT/STLExtras.h"
25#include "llvm/Support/raw_ostream.h"
30class StackAddrEscapeChecker
31 :
public CheckerFamily<check::PreCall, check::PreStmt<ReturnStmt>,
33 mutable IdentifierInfo *dispatch_semaphore_tII =
nullptr;
36 StringRef getDebugTag()
const override {
return "StackAddrEscapeChecker"; }
38 CheckerFrontend StackAddrEscape;
39 CheckerFrontend StackAddrAsyncEscape;
41 const BugType StackLeak{&StackAddrEscape,
42 "Stack address leaks outside of stack frame"};
43 const BugType ReturnStack{&StackAddrEscape,
44 "Return of address to stack-allocated memory"};
45 const BugType CapturedStackAsync{
46 &StackAddrAsyncEscape,
"Address of stack-allocated memory is captured"};
48 void checkPreCall(
const CallEvent &
Call, CheckerContext &
C)
const;
49 void checkPreStmt(
const ReturnStmt *RS, CheckerContext &
C)
const;
50 void checkEndFunction(
const ReturnStmt *RS, CheckerContext &Ctx)
const;
53 void checkAsyncExecutedBlockCaptures(
const BlockDataRegion &B,
54 CheckerContext &
C)
const;
55 void EmitReturnLeakError(CheckerContext &
C,
const MemRegion *LeakedRegion,
56 const Expr *RetE)
const;
57 bool isSemaphoreCaptured(
const BlockDecl &B)
const;
58 static SourceRange genName(raw_ostream &os,
const MemRegion *R,
60 static SmallVector<std::pair<const MemRegion *, const StackSpaceRegion *>, 4>
61 getCapturedStackRegions(
const BlockDataRegion &B, CheckerContext &
C);
62 static bool isNotInCurrentFrame(
const StackSpaceRegion *MS,
76 if (
const auto *CR = dyn_cast<CompoundLiteralRegion>(R)) {
77 const CompoundLiteralExpr *CL = CR->getLiteralExpr();
78 os <<
"stack memory associated with a compound literal "
82 }
else if (
const auto *AR = dyn_cast<AllocaRegion>(R)) {
83 const Expr *ARE = AR->getExpr();
86 os <<
"stack memory allocated by call to alloca() on line "
87 <<
SM.getExpansionLineNumber(L);
88 }
else if (
const auto *BR = dyn_cast<BlockDataRegion>(R)) {
89 const BlockDecl *BD = BR->getCodeRegion()->getDecl();
92 os <<
"stack-allocated block declared on line "
93 <<
SM.getExpansionLineNumber(L);
94 }
else if (
const auto *VR = dyn_cast<VarRegion>(R)) {
95 os <<
"stack memory associated with local variable '" << VR->getString()
97 range = VR->getDecl()->getSourceRange();
98 }
else if (
const auto *LER = dyn_cast<CXXLifetimeExtendedObjectRegion>(R)) {
99 QualType Ty = LER->getValueType().getLocalUnqualifiedType();
100 os <<
"stack memory associated with temporary object of type '";
102 os <<
"' lifetime extended by local variable";
103 if (
const IdentifierInfo *ID = LER->getExtendingDecl()->getIdentifier())
104 os <<
" '" <<
ID->getName() <<
'\'';
105 range = LER->getExpr()->getSourceRange();
106 }
else if (
const auto *TOR = dyn_cast<CXXTempObjectRegion>(R)) {
107 QualType Ty = TOR->getValueType().getLocalUnqualifiedType();
108 os <<
"stack memory associated with temporary object of type '";
111 range = TOR->getExpr()->getSourceRange();
113 llvm_unreachable(
"Invalid region in ReturnStackAddressChecker.");
119bool StackAddrEscapeChecker::isNotInCurrentFrame(
const StackSpaceRegion *MS,
124bool StackAddrEscapeChecker::isSemaphoreCaptured(
const BlockDecl &B)
const {
125 if (!dispatch_semaphore_tII)
128 const auto *
T =
C.getVariable()->getType()->
getAs<TypedefType>();
129 if (
T &&
T->getDecl()->getIdentifier() == dispatch_semaphore_tII)
135SmallVector<std::pair<const MemRegion *, const StackSpaceRegion *>, 4>
136StackAddrEscapeChecker::getCapturedStackRegions(
const BlockDataRegion &B,
138 SmallVector<std::pair<const MemRegion *, const StackSpaceRegion *>, 4>
142 SVal Val = State->getSVal(Var.getCapturedRegion());
144 if (
const auto *Space =
145 Region->getMemorySpaceAs<StackSpaceRegion>(State)) {
146 Regions.emplace_back(Region, Space);
157 OS <<
" is captured by a returned block";
163 OS <<
" returned to caller";
166void StackAddrEscapeChecker::EmitReturnLeakError(CheckerContext &
C,
168 const Expr *RetE)
const {
169 ExplodedNode *N =
C.generateNonFatalErrorNode();
174 SmallString<128> buf;
175 llvm::raw_svector_ostream os(buf);
178 SourceRange
range = genName(os, R,
C.getASTContext());
182 std::make_unique<PathSensitiveBugReport>(ReturnStack, os.str(), N);
185 report->addRange(range);
186 C.emitReport(std::move(report));
189void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
190 const BlockDataRegion &B, CheckerContext &
C)
const {
198 if (isSemaphoreCaptured(*B.
getDecl()))
200 auto Regions = getCapturedStackRegions(B,
C);
201 for (
const MemRegion *Region : llvm::make_first_range(Regions)) {
210 ExplodedNode *N =
C.generateNonFatalErrorNode();
213 SmallString<128> Buf;
214 llvm::raw_svector_ostream
Out(Buf);
215 SourceRange
Range = genName(Out, Region,
C.getASTContext());
216 Out <<
" is captured by an asynchronously-executed block";
217 auto Report = std::make_unique<PathSensitiveBugReport>(CapturedStackAsync,
221 C.emitReport(std::move(
Report));
225void StackAddrEscapeChecker::checkPreCall(
const CallEvent &
Call,
226 CheckerContext &
C)
const {
229 if (!
Call.isGlobalCFunction(
"dispatch_after") &&
230 !
Call.isGlobalCFunction(
"dispatch_async"))
232 for (
unsigned Idx = 0, NumArgs =
Call.getNumArgs(); Idx < NumArgs; ++Idx) {
233 if (
const BlockDataRegion *B = dyn_cast_or_null<BlockDataRegion>(
234 Call.getArgSVal(Idx).getAsRegion()))
235 checkAsyncExecutedBlockCaptures(*B,
C);
255 : Ctxt(Ctxt), PoppedStackFrame(Ctxt.getStackFrame()),
256 EscapingStackRegions(StorageForStackRegions) {}
264 return VisitBlockDataRegionCaptures(BDR);
270 void SaveIfEscapes(
const MemRegion *MR) {
277 if (CapturedSFC == PoppedStackFrame ||
279 EscapingStackRegions.push_back(MR);
282 bool VisitBlockDataRegionCaptures(
const BlockDataRegion *BDR) {
284 SVal Val = Ctxt.getState()->getSVal(Var.getCapturedRegion());
287 SaveIfEscapes(Region);
288 VisitMemRegion(Region);
313 RetE = Cleanup->getSubExpr();
314 bool IsConstructExpr =
319 bool IsCopyAndAutoreleaseBlockObj =
false;
320 if (
const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) {
321 IsCopyAndAutoreleaseBlockObj =
322 isa_and_nonnull<BlockDataRegion>(RetRegion) &&
323 ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject;
326 for (
const MemRegion *MR : MaybeEscaped) {
327 if (RetRegion == MR && (IsCopyAndAutoreleaseBlockObj || IsConstructExpr))
330 WillEscape.push_back(MR);
338static SmallVector<const MemRegion *>
344 Scanner.scan(RetVal);
349void StackAddrEscapeChecker::checkPreStmt(
const ReturnStmt *RS,
350 CheckerContext &
C)
const {
359 SVal
V =
C.getSVal(RetE);
361 SmallVector<const MemRegion *> EscapedStackRegions =
364 for (
const MemRegion *ER : EscapedStackRegions)
365 EmitReturnLeakError(
C, ER, RetE);
378 if (
const auto *OriginReg = SymReg->getSymbol()->getOriginRegion())
386 while (
const auto *SymReg = dyn_cast<SymbolicRegion>(Reg)) {
387 const auto *OriginReg = SymReg->getSymbol()->getOriginRegion();
398 const StringRef ReferrerMemorySpace = [](
const MemSpaceRegion *Space) {
409 if (
const auto *SymReg = dyn_cast<SymbolicRegion>(Referrer);
410 SymReg && SymReg->getSymbol()->getOriginRegion()) {
411 Referrer = SymReg->getSymbol()->getOriginRegion()->
getBaseRegion();
422 assert(
false &&
"Unexpected referrer region type.");
430 llvm::raw_string_ostream os(buf);
431 os << ReferrerMemorySpace <<
" variable ";
444 const auto *DerS = dyn_cast<SymbolDerived>(Symbol);
445 return DerS && isa_and_nonnull<SymbolConjured>(DerS->getParentSymbol());
448void StackAddrEscapeChecker::checkEndFunction(
const ReturnStmt *RS,
449 CheckerContext &Ctx)
const {
455 bool ExitingTopFrame =
458 if (ExitingTopFrame &&
470 class CallBack :
public StoreManager::BindingsHandler {
474 const StackFrameContext *PoppedFrame;
480 bool checkForDanglingStackVariable(
const MemRegion *Referrer,
481 const MemRegion *Referred) {
482 const auto *ReferrerMemSpace =
484 const auto *ReferredMemSpace =
487 if (!ReferrerMemSpace || !ReferredMemSpace)
490 const auto *ReferrerStackSpace =
491 ReferrerMemSpace->getAs<StackSpaceRegion>();
493 if (!ReferrerStackSpace)
496 if (
const auto *ReferredFrame = ReferredMemSpace->getStackFrame();
497 ReferredFrame != PoppedFrame) {
501 if (ReferrerStackSpace->getStackFrame()->isParentOf(PoppedFrame)) {
502 V.emplace_back(Referrer, Referred);
508 ReferrerStackSpace->getStackFrame() == PoppedFrame && TopFrame) {
510 V.emplace_back(Referrer, Referred);
520 void recordInInvalidatedRegions(
const MemRegion *Region) {
526 SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10>
V;
531 llvm::SmallPtrSet<const MemRegion *, 4> ExcludedRegions;
533 CallBack(CheckerContext &CC,
bool TopFrame)
534 : Ctx(CC), State(CC.getState()), PoppedFrame(CC.getStackFrame()),
535 TopFrame(TopFrame) {}
537 bool HandleBinding(StoreManager &SMgr,
Store S,
const MemRegion *Region,
539 recordInInvalidatedRegions(Region);
544 if (checkForDanglingStackVariable(Region, VR))
548 if (!isa_and_nonnull<GlobalsSpaceRegion>(
554 S && !isNotInCurrentFrame(S, Ctx)) {
555 V.emplace_back(Region, VR);
562 CallBack Cb(Ctx, ExitingTopFrame);
564 State->getStateManager().getStoreManager().iterBindings(State->getStore(),
575 for (
const auto &P : Cb.V) {
577 const MemRegion *Referred = P.second;
583 const StringRef CommonSuffix =
584 " upon returning to the caller. This will be a dangling reference";
585 SmallString<128> Buf;
586 llvm::raw_svector_ostream
Out(Buf);
590 Out <<
" is still referred to by a temporary object on the stack"
593 std::make_unique<PathSensitiveBugReport>(StackLeak,
Out.str(), N);
601 if (!ReferrerVariable) {
605 Out <<
" is still referred to by the " << *ReferrerVariable << CommonSuffix;
607 std::make_unique<PathSensitiveBugReport>(StackLeak,
Out.str(), N);
615#define REGISTER_CHECKER(NAME) \
616 void ento::register##NAME##Checker(CheckerManager &Mgr) { \
617 Mgr.getChecker<StackAddrEscapeChecker>()->NAME.enable(Mgr); \
620 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