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<BuiltinBug> BT_stackleak;
34 mutable std::unique_ptr<BuiltinBug> BT_returnstack;
35 mutable std::unique_ptr<BuiltinBug> BT_capturedstackasync;
36 mutable std::unique_ptr<BuiltinBug> 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 *TOR = dyn_cast<CXXTempObjectRegion>(R)) {
100 QualType Ty = TOR->getValueType().getLocalUnqualifiedType();
101 os <<
"stack memory associated with temporary object of type '";
104 range = TOR->getExpr()->getSourceRange();
106 llvm_unreachable(
"Invalid region in ReturnStackAddressChecker.");
112bool StackAddrEscapeChecker::isNotInCurrentFrame(
const MemRegion *R,
115 return S->getStackFrame() !=
C.getStackFrame();
118bool StackAddrEscapeChecker::isSemaphoreCaptured(
const BlockDecl &B)
const {
119 if (!dispatch_semaphore_tII)
122 const auto *T =
C.getVariable()->getType()->getAs<
TypedefType>();
123 if (T && T->getDecl()->getIdentifier() == dispatch_semaphore_tII)
130StackAddrEscapeChecker::getCapturedStackRegions(
const BlockDataRegion &B,
135 for (; I != E; ++I) {
139 Regions.push_back(Region);
146 const Expr *RetE)
const {
151 BT_returnstack = std::make_unique<BuiltinBug>(
152 CheckNames[CK_StackAddrEscapeChecker],
153 "Return of address to stack-allocated memory");
156 llvm::raw_svector_ostream os(buf);
158 os <<
" returned to caller";
160 std::make_unique<PathSensitiveBugReport>(*BT_returnstack, os.str(), N);
163 report->addRange(range);
164 C.emitReport(std::move(report));
167void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
176 if (isSemaphoreCaptured(*B.
getDecl()))
178 for (
const MemRegion *Region : getCapturedStackRegions(B,
C)) {
185 if (isa<BlockDataRegion>(Region))
190 if (!BT_capturedstackasync)
191 BT_capturedstackasync = std::make_unique<BuiltinBug>(
192 CheckNames[CK_StackAddrAsyncEscapeChecker],
193 "Address of stack-allocated memory is captured");
195 llvm::raw_svector_ostream Out(Buf);
197 Out <<
" is captured by an asynchronously-executed block";
198 auto Report = std::make_unique<PathSensitiveBugReport>(
199 *BT_capturedstackasync, Out.str(), N);
201 Report->addRange(
Range);
202 C.emitReport(std::move(Report));
206void StackAddrEscapeChecker::checkReturnedBlockCaptures(
208 for (
const MemRegion *Region : getCapturedStackRegions(B,
C)) {
209 if (isNotInCurrentFrame(Region,
C))
214 if (!BT_capturedstackret)
215 BT_capturedstackret = std::make_unique<BuiltinBug>(
216 CheckNames[CK_StackAddrEscapeChecker],
217 "Address of stack-allocated memory is captured");
219 llvm::raw_svector_ostream Out(Buf);
221 Out <<
" is captured by a returned block";
222 auto Report = std::make_unique<PathSensitiveBugReport>(*BT_capturedstackret,
225 Report->addRange(
Range);
226 C.emitReport(std::move(Report));
230void StackAddrEscapeChecker::checkPreCall(
const CallEvent &Call,
232 if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
234 if (!
Call.isGlobalCFunction(
"dispatch_after") &&
235 !
Call.isGlobalCFunction(
"dispatch_async"))
237 for (
unsigned Idx = 0, NumArgs =
Call.getNumArgs(); Idx < NumArgs; ++Idx) {
239 Call.getArgSVal(Idx).getAsRegion()))
240 checkAsyncExecutedBlockCaptures(*B,
C);
244void StackAddrEscapeChecker::checkPreStmt(
const ReturnStmt *RS,
246 if (!ChecksEnabled[CK_StackAddrEscapeChecker])
260 checkReturnedBlockCaptures(*B,
C);
262 if (!isa<StackSpaceRegion>(R->
getMemorySpace()) || isNotInCurrentFrame(R,
C))
269 RetE = Cleanup->getSubExpr();
275 if (
const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) {
276 if (isa<BlockDataRegion>(R) &&
277 ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) {
282 EmitStackError(
C, R, RetE);
285void StackAddrEscapeChecker::checkEndFunction(
const ReturnStmt *RS,
287 if (!ChecksEnabled[CK_StackAddrEscapeChecker])
302 bool checkForDanglingStackVariable(
const MemRegion *Referrer,
304 const auto *ReferrerMemSpace =
306 const auto *ReferredMemSpace =
309 if (!ReferrerMemSpace || !ReferredMemSpace)
312 const auto *ReferrerFrame = ReferrerMemSpace->
getStackFrame();
313 const auto *ReferredFrame = ReferredMemSpace->
getStackFrame();
315 if (ReferrerMemSpace && ReferredMemSpace) {
316 if (ReferredFrame == PoppedFrame &&
317 ReferrerFrame->isParentOf(PoppedFrame)) {
318 V.emplace_back(Referrer, Referred);
328 CallBack(
CheckerContext &CC) : Ctx(CC), PoppedFrame(CC.getStackFrame()) {}
336 if (checkForDanglingStackVariable(Region, VR))
343 V.emplace_back(Region, VR);
349 State->getStateManager().getStoreManager().iterBindings(State->getStore(),
361 BT_stackleak = std::make_unique<BuiltinBug>(
362 CheckNames[CK_StackAddrEscapeChecker],
363 "Stack address stored into global variable",
364 "Stack address was saved into a global variable. "
365 "This is dangerous because the address will become "
366 "invalid after returning from the function");
368 for (
const auto &
P : Cb.V) {
373 const StringRef CommonSuffix =
374 "upon returning to the caller. This will be a dangling reference";
376 llvm::raw_svector_ostream Out(Buf);
379 if (isa<CXXTempObjectRegion>(Referrer)) {
380 Out <<
" is still referred to by a temporary object on the stack "
383 std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
388 const StringRef ReferrerMemorySpace = [](
const MemSpaceRegion *Space) {
389 if (isa<StaticGlobalSpaceRegion>(Space))
391 if (isa<GlobalsSpaceRegion>(Space))
393 assert(isa<StackSpaceRegion>(Space));
399 const std::string ReferrerVarName =
402 Out <<
" is still referred to by the " << ReferrerMemorySpace
403 <<
" variable '" << ReferrerVarName <<
"' " << CommonSuffix;
405 std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
407 Report->addRange(
Range);
417bool ento::shouldRegisterStackAddrEscapeBase(
const CheckerManager &mgr) {
421#define REGISTER_CHECKER(name) \
422 void ento::register##name(CheckerManager &Mgr) { \
423 StackAddrEscapeChecker *Chk = Mgr.getChecker<StackAddrEscapeChecker>(); \
424 Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true; \
425 Chk->CheckNames[StackAddrEscapeChecker::CK_##name] = \
426 Mgr.getCurrentCheckerName(); \
429 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
std::string getAsString() const
Retrieve the human-readable string for this name.
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.
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
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
LLVM_ATTRIBUTE_RETURNS_NONNULL const VarRegion * getCapturedRegion() const
BlockDataRegion - A region that represents a block instance.
LLVM_ATTRIBUTE_RETURNS_NONNULL const BlockDecl * getDecl() const
referenced_vars_iterator referenced_vars_begin() const
referenced_vars_iterator referenced_vars_end() 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 VarDecl * getDecl() const override=0
const void * Store
Store - This opaque type encapsulates an immutable mapping from locations to values.
bool Call(InterpState &S, CodePtr &PC, const Function *Func)
@ C
Languages that the frontend can parse and compile.