23#include "llvm/ADT/SmallString.h"
24#include "llvm/Support/raw_ostream.h"
29class StackAddrEscapeChecker
30 :
public Checker<check::PreCall, check::PreStmt<ReturnStmt>,
33 mutable std::unique_ptr<BugType> BT_stackleak;
34 mutable std::unique_ptr<BugType> BT_returnstack;
35 mutable std::unique_ptr<BugType> BT_capturedstackasync;
36 mutable std::unique_ptr<BugType> BT_capturedstackret;
40 CK_StackAddrEscapeChecker,
41 CK_StackAddrAsyncEscapeChecker,
45 bool ChecksEnabled[CK_NumCheckKinds] = {
false};
58 const Expr *RetE)
const;
59 bool isSemaphoreCaptured(
const BlockDecl &B)
const;
77 if (
const auto *CR = dyn_cast<CompoundLiteralRegion>(R)) {
79 os <<
"stack memory associated with a compound literal "
81 <<
SM.getExpansionLineNumber(CL->
getBeginLoc()) <<
" returned to caller";
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 MemRegion *R,
123 return S->getStackFrame() !=
C.getStackFrame();
126bool StackAddrEscapeChecker::isSemaphoreCaptured(
const BlockDecl &B)
const {
127 if (!dispatch_semaphore_tII)
130 const auto *
T =
C.getVariable()->getType()->getAs<
TypedefType>();
131 if (
T &&
T->getDecl()->getIdentifier() == dispatch_semaphore_tII)
138StackAddrEscapeChecker::getCapturedStackRegions(
const BlockDataRegion &B,
142 SVal Val =
C.getState()->getSVal(Var.getCapturedRegion());
145 Regions.push_back(Region);
152 const Expr *RetE)
const {
157 BT_returnstack = std::make_unique<BugType>(
158 CheckNames[CK_StackAddrEscapeChecker],
159 "Return of address to stack-allocated memory");
162 llvm::raw_svector_ostream os(buf);
164 os <<
" returned to caller";
166 std::make_unique<PathSensitiveBugReport>(*BT_returnstack, os.str(), N);
169 report->addRange(range);
170 C.emitReport(std::move(report));
173void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
182 if (isSemaphoreCaptured(*B.
getDecl()))
184 for (
const MemRegion *Region : getCapturedStackRegions(B,
C)) {
191 if (isa<BlockDataRegion>(Region))
196 if (!BT_capturedstackasync)
197 BT_capturedstackasync = std::make_unique<BugType>(
198 CheckNames[CK_StackAddrAsyncEscapeChecker],
199 "Address of stack-allocated memory is captured");
201 llvm::raw_svector_ostream Out(Buf);
203 Out <<
" is captured by an asynchronously-executed block";
204 auto Report = std::make_unique<PathSensitiveBugReport>(
205 *BT_capturedstackasync, Out.str(), N);
207 Report->addRange(
Range);
208 C.emitReport(std::move(Report));
212void StackAddrEscapeChecker::checkReturnedBlockCaptures(
214 for (
const MemRegion *Region : getCapturedStackRegions(B,
C)) {
215 if (isNotInCurrentFrame(Region,
C))
220 if (!BT_capturedstackret)
221 BT_capturedstackret = std::make_unique<BugType>(
222 CheckNames[CK_StackAddrEscapeChecker],
223 "Address of stack-allocated memory is captured");
225 llvm::raw_svector_ostream Out(Buf);
227 Out <<
" is captured by a returned block";
228 auto Report = std::make_unique<PathSensitiveBugReport>(*BT_capturedstackret,
231 Report->addRange(
Range);
232 C.emitReport(std::move(Report));
236void StackAddrEscapeChecker::checkPreCall(
const CallEvent &
Call,
238 if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
240 if (!
Call.isGlobalCFunction(
"dispatch_after") &&
241 !
Call.isGlobalCFunction(
"dispatch_async"))
243 for (
unsigned Idx = 0, NumArgs =
Call.getNumArgs(); Idx < NumArgs; ++Idx) {
245 Call.getArgSVal(Idx).getAsRegion()))
246 checkAsyncExecutedBlockCaptures(*B,
C);
250void StackAddrEscapeChecker::checkPreStmt(
const ReturnStmt *RS,
252 if (!ChecksEnabled[CK_StackAddrEscapeChecker])
266 checkReturnedBlockCaptures(*B,
C);
268 if (!isa<StackSpaceRegion>(R->
getMemorySpace()) || isNotInCurrentFrame(R,
C))
275 RetE = Cleanup->getSubExpr();
281 if (
const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) {
282 if (isa<BlockDataRegion>(R) &&
283 ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) {
288 EmitStackError(
C, R, RetE);
291void StackAddrEscapeChecker::checkEndFunction(
const ReturnStmt *RS,
293 if (!ChecksEnabled[CK_StackAddrEscapeChecker])
308 bool checkForDanglingStackVariable(
const MemRegion *Referrer,
310 const auto *ReferrerMemSpace =
312 const auto *ReferredMemSpace =
315 if (!ReferrerMemSpace || !ReferredMemSpace)
318 const auto *ReferrerFrame = ReferrerMemSpace->
getStackFrame();
319 const auto *ReferredFrame = ReferredMemSpace->
getStackFrame();
321 if (ReferrerMemSpace && ReferredMemSpace) {
322 if (ReferredFrame == PoppedFrame &&
323 ReferrerFrame->isParentOf(PoppedFrame)) {
324 V.emplace_back(Referrer, Referred);
334 CallBack(
CheckerContext &CC) : Ctx(CC), PoppedFrame(CC.getStackFrame()) {}
342 if (checkForDanglingStackVariable(Region, VR))
349 V.emplace_back(Region, VR);
355 State->getStateManager().getStoreManager().iterBindings(State->getStore(),
368 std::make_unique<BugType>(CheckNames[CK_StackAddrEscapeChecker],
369 "Stack address stored into global variable");
371 for (
const auto &
P : Cb.V) {
372 const MemRegion *Referrer =
P.first->getBaseRegion();
376 const StringRef CommonSuffix =
377 "upon returning to the caller. This will be a dangling reference";
379 llvm::raw_svector_ostream Out(Buf);
382 if (isa<CXXTempObjectRegion, CXXLifetimeExtendedObjectRegion>(Referrer)) {
383 Out <<
" is still referred to by a temporary object on the stack "
386 std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
388 Report->addRange(
Range);
393 const StringRef ReferrerMemorySpace = [](
const MemSpaceRegion *Space) {
394 if (isa<StaticGlobalSpaceRegion>(Space))
396 if (isa<GlobalsSpaceRegion>(Space))
398 assert(isa<StackSpaceRegion>(Space));
405 const auto *ReferrerVar = dyn_cast<VarRegion>(Referrer);
407 assert(
false &&
"We should have a VarRegion here");
410 const std::string ReferrerVarName =
411 ReferrerVar->getDecl()->getDeclName().getAsString();
413 Out <<
" is still referred to by the " << ReferrerMemorySpace
414 <<
" variable '" << ReferrerVarName <<
"' " << CommonSuffix;
416 std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
418 Report->addRange(
Range);
428bool ento::shouldRegisterStackAddrEscapeBase(
const CheckerManager &mgr) {
432#define REGISTER_CHECKER(name) \
433 void ento::register##name(CheckerManager &Mgr) { \
434 StackAddrEscapeChecker *Chk = Mgr.getChecker<StackAddrEscapeChecker>(); \
435 Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true; \
436 Chk->CheckNames[StackAddrEscapeChecker::CK_##name] = \
437 Mgr.getCurrentCheckerName(); \
440 bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
Defines the clang::Expr interface and subclasses for C++ expressions.
Defines the SourceManager interface.
#define REGISTER_CHECKER(name)
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
Represents a block literal declaration, which is like an unnamed FunctionDecl.
ArrayRef< Capture > captures() const
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
CompoundLiteralExpr - [C99 6.5.2.5].
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.
One of these records is kept for each identifier that is lexed.
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
A (possibly-)qualified type.
void print(raw_ostream &OS, const PrintingPolicy &Policy, const Twine &PlaceHolder=Twine(), unsigned Indentation=0) const
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
Encodes a location in the source.
This class handles loading and caching of source files into memory.
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
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
Represents an abstract call to a function or method along a particular path.
ASTContext & getASTContext()
const ProgramStateRef & getState() const
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 * registerChecker(AT &&... Args)
Used to register checkers.
This wrapper is used to ensure that only StringRefs originating from the CheckerRegistry are used as ...
MemRegion - The root abstract class for all memory regions.
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemSpaceRegion * getMemorySpace() const
bool hasStackStorage() const
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getBaseRegion() const
const RegionTy * getAs() const
MemSpaceRegion - A memory region that represents a "memory space"; for example, the set of global var...
A Range represents the closed range [from, to].
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
const MemRegion * getAsRegion() const
LLVM_ATTRIBUTE_RETURNS_NONNULL const StackFrameContext * getStackFrame() const
virtual bool HandleBinding(StoreManager &SMgr, Store store, const MemRegion *region, SVal val)=0
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.
const FunctionProtoType * T