23 #include "llvm/ADT/SmallString.h"
24 #include "llvm/Support/raw_ostream.h"
25 using namespace clang;
29 class 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};
46 CheckerNameRef CheckNames[CK_NumCheckKinds];
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 checkReturnedBlockCaptures(
const BlockDataRegion &B,
54 CheckerContext &C)
const;
55 void checkAsyncExecutedBlockCaptures(
const BlockDataRegion &B,
56 CheckerContext &C)
const;
57 void EmitStackError(CheckerContext &C,
const MemRegion *R,
58 const Expr *RetE)
const;
59 bool isSemaphoreCaptured(
const BlockDecl &B)
const;
60 static SourceRange genName(raw_ostream &os,
const MemRegion *R,
63 getCapturedStackRegions(
const BlockDataRegion &B, CheckerContext &C);
64 static bool isArcManagedBlock(
const MemRegion *R, CheckerContext &C);
65 static bool isNotInCurrentFrame(
const MemRegion *R, CheckerContext &C);
69 SourceRange StackAddrEscapeChecker::genName(raw_ostream &os,
const MemRegion *R,
72 R = R->getBaseRegion();
78 if (
const auto *CR = dyn_cast<CompoundLiteralRegion>(R)) {
80 os <<
"stack memory associated with a compound literal "
82 <<
SM.getExpansionLineNumber(CL->
getBeginLoc()) <<
" returned to caller";
84 }
else if (
const auto *AR = dyn_cast<AllocaRegion>(R)) {
85 const Expr *ARE = AR->getExpr();
88 os <<
"stack memory allocated by call to alloca() on line "
89 <<
SM.getExpansionLineNumber(L);
90 }
else if (
const auto *BR = dyn_cast<BlockDataRegion>(R)) {
91 const BlockDecl *BD = BR->getCodeRegion()->getDecl();
94 os <<
"stack-allocated block declared on line "
95 <<
SM.getExpansionLineNumber(L);
96 }
else if (
const auto *VR = dyn_cast<VarRegion>(R)) {
97 os <<
"stack memory associated with local variable '" << VR->getString()
99 range = VR->getDecl()->getSourceRange();
100 }
else if (
const auto *TOR = dyn_cast<CXXTempObjectRegion>(R)) {
101 QualType Ty = TOR->getValueType().getLocalUnqualifiedType();
102 os <<
"stack memory associated with temporary object of type '";
105 range = TOR->getExpr()->getSourceRange();
107 llvm_unreachable(
"Invalid region in ReturnStackAddressChecker.");
113 bool StackAddrEscapeChecker::isArcManagedBlock(
const MemRegion *R,
115 assert(R &&
"MemRegion should not be null");
116 return C.getASTContext().getLangOpts().ObjCAutoRefCount &&
117 isa<BlockDataRegion>(R);
120 bool StackAddrEscapeChecker::isNotInCurrentFrame(
const MemRegion *R,
122 const StackSpaceRegion *S = cast<StackSpaceRegion>(R->getMemorySpace());
123 return S->getStackFrame() !=
C.getStackFrame();
126 bool StackAddrEscapeChecker::isSemaphoreCaptured(
const BlockDecl &B)
const {
127 if (!dispatch_semaphore_tII)
129 for (
const auto &C : B.
captures()) {
130 const auto *T =
C.getVariable()->getType()->getAs<
TypedefType>();
131 if (T && T->getDecl()->getIdentifier() == dispatch_semaphore_tII)
138 StackAddrEscapeChecker::getCapturedStackRegions(
const BlockDataRegion &B,
141 BlockDataRegion::referenced_vars_iterator I = B.referenced_vars_begin();
142 BlockDataRegion::referenced_vars_iterator E = B.referenced_vars_end();
143 for (; I != E; ++I) {
144 SVal Val =
C.getState()->getSVal(I.getCapturedRegion());
145 const MemRegion *Region = Val.getAsRegion();
146 if (Region && isa<StackSpaceRegion>(Region->getMemorySpace()))
147 Regions.push_back(Region);
152 void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,
154 const Expr *RetE)
const {
155 ExplodedNode *N =
C.generateNonFatalErrorNode();
159 BT_returnstack = std::make_unique<BuiltinBug>(
160 CheckNames[CK_StackAddrEscapeChecker],
161 "Return of address to stack-allocated memory");
164 llvm::raw_svector_ostream os(buf);
166 os <<
" returned to caller";
168 std::make_unique<PathSensitiveBugReport>(*BT_returnstack, os.str(), N);
171 report->addRange(
range);
172 C.emitReport(std::move(report));
175 void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
176 const BlockDataRegion &B, CheckerContext &C)
const {
184 if (isSemaphoreCaptured(*B.getDecl()))
186 for (
const MemRegion *Region : getCapturedStackRegions(B, C)) {
193 if (isa<BlockDataRegion>(Region))
195 ExplodedNode *N =
C.generateNonFatalErrorNode();
198 if (!BT_capturedstackasync)
199 BT_capturedstackasync = std::make_unique<BuiltinBug>(
200 CheckNames[CK_StackAddrAsyncEscapeChecker],
201 "Address of stack-allocated memory is captured");
203 llvm::raw_svector_ostream Out(Buf);
205 Out <<
" is captured by an asynchronously-executed block";
206 auto Report = std::make_unique<PathSensitiveBugReport>(
207 *BT_capturedstackasync, Out.str(), N);
209 Report->addRange(Range);
210 C.emitReport(std::move(Report));
214 void StackAddrEscapeChecker::checkReturnedBlockCaptures(
215 const BlockDataRegion &B, CheckerContext &C)
const {
216 for (
const MemRegion *Region : getCapturedStackRegions(B, C)) {
217 if (isArcManagedBlock(Region, C) || isNotInCurrentFrame(Region, C))
219 ExplodedNode *N =
C.generateNonFatalErrorNode();
222 if (!BT_capturedstackret)
223 BT_capturedstackret = std::make_unique<BuiltinBug>(
224 CheckNames[CK_StackAddrEscapeChecker],
225 "Address of stack-allocated memory is captured");
227 llvm::raw_svector_ostream Out(Buf);
229 Out <<
" is captured by a returned block";
230 auto Report = std::make_unique<PathSensitiveBugReport>(*BT_capturedstackret,
233 Report->addRange(Range);
234 C.emitReport(std::move(Report));
238 void StackAddrEscapeChecker::checkPreCall(
const CallEvent &Call,
239 CheckerContext &C)
const {
240 if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
242 if (!
Call.isGlobalCFunction(
"dispatch_after") &&
243 !
Call.isGlobalCFunction(
"dispatch_async"))
245 for (
unsigned Idx = 0, NumArgs =
Call.getNumArgs(); Idx < NumArgs; ++Idx) {
246 if (
const BlockDataRegion *B = dyn_cast_or_null<BlockDataRegion>(
247 Call.getArgSVal(Idx).getAsRegion()))
248 checkAsyncExecutedBlockCaptures(*B, C);
252 void StackAddrEscapeChecker::checkPreStmt(
const ReturnStmt *RS,
253 CheckerContext &C)
const {
254 if (!ChecksEnabled[CK_StackAddrEscapeChecker])
262 SVal
V =
C.getSVal(RetE);
263 const MemRegion *R =
V.getAsRegion();
267 if (
const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R))
268 checkReturnedBlockCaptures(*B, C);
270 if (!isa<StackSpaceRegion>(R->getMemorySpace()) ||
271 isNotInCurrentFrame(R, C) || isArcManagedBlock(R, C))
278 RetE = Cleanup->getSubExpr();
284 if (
const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) {
285 if (isa<BlockDataRegion>(R) &&
286 ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) {
291 EmitStackError(C, R, RetE);
294 void StackAddrEscapeChecker::checkEndFunction(
const ReturnStmt *RS,
295 CheckerContext &Ctx)
const {
296 if (!ChecksEnabled[CK_StackAddrEscapeChecker])
303 class CallBack :
public StoreManager::BindingsHandler {
311 bool checkForDanglingStackVariable(
const MemRegion *Referrer,
312 const MemRegion *Referred) {
313 const auto *ReferrerMemSpace =
314 Referrer->getMemorySpace()->getAs<StackSpaceRegion>();
315 const auto *ReferredMemSpace =
316 Referred->getMemorySpace()->getAs<StackSpaceRegion>();
318 if (!ReferrerMemSpace || !ReferredMemSpace)
321 const auto *ReferrerFrame = ReferrerMemSpace->
getStackFrame();
322 const auto *ReferredFrame = ReferredMemSpace->
getStackFrame();
324 if (ReferrerMemSpace && ReferredMemSpace) {
325 if (ReferredFrame == PoppedFrame &&
326 ReferrerFrame->isParentOf(PoppedFrame)) {
327 V.emplace_back(Referrer, Referred);
337 CallBack(CheckerContext &CC) : Ctx(CC), PoppedFrame(CC.getStackFrame()) {}
339 bool HandleBinding(StoreManager &SMgr,
Store S,
const MemRegion *Region,
341 const MemRegion *VR = Val.getAsRegion();
345 if (checkForDanglingStackVariable(Region, VR))
349 if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace()))
351 if (VR && VR->hasStackStorage() && !isArcManagedBlock(VR, Ctx) &&
352 !isNotInCurrentFrame(VR, Ctx))
353 V.emplace_back(Region, VR);
359 State->getStateManager().getStoreManager().iterBindings(
State->getStore(),
366 ExplodedNode *N = Ctx.generateNonFatalErrorNode(
State);
371 BT_stackleak = std::make_unique<BuiltinBug>(
372 CheckNames[CK_StackAddrEscapeChecker],
373 "Stack address stored into global variable",
374 "Stack address was saved into a global variable. "
375 "This is dangerous because the address will become "
376 "invalid after returning from the function");
378 for (
const auto &
P : Cb.V) {
379 const MemRegion *Referrer =
P.first;
380 const MemRegion *Referred =
P.second;
383 const StringRef CommonSuffix =
384 "upon returning to the caller. This will be a dangling reference";
386 llvm::raw_svector_ostream Out(Buf);
389 if (isa<CXXTempObjectRegion>(Referrer)) {
390 Out <<
" is still referred to by a temporary object on the stack "
393 std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
394 Ctx.emitReport(std::move(Report));
398 const StringRef ReferrerMemorySpace = [](
const MemSpaceRegion *Space) {
399 if (isa<StaticGlobalSpaceRegion>(Space))
401 if (isa<GlobalsSpaceRegion>(Space))
403 assert(isa<StackSpaceRegion>(Space));
405 }(Referrer->getMemorySpace());
408 const VarRegion *ReferrerVar = cast<VarRegion>(Referrer->getBaseRegion());
410 ReferrerVar->getDecl()->getDeclName().getAsString();
412 Out <<
" is still referred to by the " << ReferrerMemorySpace
413 <<
" variable '" << ReferrerVarName <<
"' " << CommonSuffix;
415 std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
417 Report->addRange(Range);
419 Ctx.emitReport(std::move(Report));
423 void ento::registerStackAddrEscapeBase(CheckerManager &mgr) {
424 mgr.registerChecker<StackAddrEscapeChecker>();
427 bool ento::shouldRegisterStackAddrEscapeBase(
const CheckerManager &mgr) {
431 #define REGISTER_CHECKER(name) \
432 void ento::register##name(CheckerManager &Mgr) { \
433 StackAddrEscapeChecker *Chk = Mgr.getChecker<StackAddrEscapeChecker>(); \
434 Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true; \
435 Chk->CheckNames[StackAddrEscapeChecker::CK_##name] = \
436 Mgr.getCurrentCheckerName(); \
439 bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }