Index: lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -18,6 +18,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "llvm/ADT/SmallString.h" @@ -26,15 +27,19 @@ using namespace ento; namespace { -class StackAddrEscapeChecker : public Checker< check::PreStmt, +class StackAddrEscapeChecker : public Checker< check::PreCall, + check::PreStmt, check::EndFunction > { mutable std::unique_ptr BT_stackleak; mutable std::unique_ptr BT_returnstack; + mutable std::unique_ptr BT_capturestackleak; public: + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; void checkEndFunction(CheckerContext &Ctx) const; private: + void checkBlockCaptures(const BlockDataRegion &B, CheckerContext &C) const; void EmitStackError(CheckerContext &C, const MemRegion *R, const Expr *RetE) const; static SourceRange genName(raw_ostream &os, const MemRegion *R, @@ -84,17 +89,17 @@ Ty.print(os, Ctx.getPrintingPolicy()); os << "'"; range = TOR->getExpr()->getSourceRange(); - } - else { + } else { llvm_unreachable("Invalid region in ReturnStackAddressChecker."); } return range; } -void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R, - const Expr *RetE) const { - ExplodedNode *N = C.generateErrorNode(); +void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, + const MemRegion *R, + const Expr *RetE) const { + ExplodedNode *N = C.generateNonFatalErrorNode(); if (!N) return; @@ -116,6 +121,51 @@ C.emitReport(std::move(report)); } +void StackAddrEscapeChecker::checkBlockCaptures(const BlockDataRegion &B, + CheckerContext &C) const { + BlockDataRegion::referenced_vars_iterator I = B.referenced_vars_begin(); + BlockDataRegion::referenced_vars_iterator E = B.referenced_vars_end(); + for (; I != E; ++I) { + SVal Val = C.getState()->getSVal(I.getCapturedRegion()); + const MemRegion *Region = Val.getAsRegion(); + if (!Region) + continue; + if (C.getASTContext().getLangOpts().ObjCAutoRefCount && + isa(Region)) + continue; + if (dyn_cast(Region->getMemorySpace())) { + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + continue; + if (!BT_capturestackleak) + BT_capturestackleak = llvm::make_unique( + this, "Address of stack-allocated memory"); + SmallString<512> Buf; + llvm::raw_svector_ostream Out(Buf); + SourceRange Range = genName(Out, Region, C.getASTContext()); + Out << " might leak via the block capture"; + auto Report = + llvm::make_unique(*BT_capturestackleak, Out.str(), N); + if (Range.isValid()) + Report->addRange(Range); + C.emitReport(std::move(Report)); + } + } +} + +void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (!Call.isGlobalCFunction("dispatch_after") && + !Call.isGlobalCFunction("dispatch_async")) + return; + + for (unsigned Idx = 0, NumArgs = Call.getNumArgs(); Idx < NumArgs; ++Idx) { + if (const BlockDataRegion *B = dyn_cast_or_null( + Call.getArgSVal(Idx).getAsRegion())) + checkBlockCaptures(*B, C); + } +} + void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { @@ -131,8 +181,11 @@ if (!R) return; + if (const BlockDataRegion *B = dyn_cast(R)) + checkBlockCaptures(*B, C); + const StackSpaceRegion *SS = - dyn_cast_or_null(R->getMemorySpace()); + dyn_cast(R->getMemorySpace()); if (!SS) return; Index: test/Analysis/stack-async-leak.m =================================================================== --- test/Analysis/stack-async-leak.m +++ test/Analysis/stack-async-leak.m @@ -0,0 +1,132 @@ +// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core -fblocks -fobjc-arc -verify %s + +typedef struct dispatch_queue_s *dispatch_queue_t; +typedef void (^dispatch_block_t)(void); +void dispatch_async(dispatch_queue_t queue, dispatch_block_t block); +typedef long dispatch_once_t; +void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block); +typedef long dispatch_time_t; +void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block); + +extern dispatch_queue_t queue; +extern dispatch_once_t *predicate; +extern dispatch_time_t when; + +void test_block_expr_async() { + int x = 123; + int *p = &x; + + dispatch_async(queue, ^{ + *p = 321; + }); + // expected-warning@-3 {{Address of stack memory associated with local variable 'x' \ +might leak via the block capture}} +} + +void test_block_expr_once_no_leak() { + int x = 123; + int *p = &x; + // synchronous, no warning + dispatch_once(predicate, ^{ + *p = 321; + }); +} + +void test_block_expr_after() { + int x = 123; + int *p = &x; + dispatch_after(when, queue, ^{ + *p = 321; + }); + // expected-warning@-3 {{Address of stack memory associated with local variable 'x' \ +might leak via the block capture}} +} + +void test_block_expr_async_no_leak() { + int x = 123; + int *p = &x; + // no leak + dispatch_async(queue, ^{ + int y = x; + ++y; + }); +} + +void test_block_var_async() { + int x = 123; + int *p = &x; + void (^b)(void) = ^void(void) { + *p = 1; + }; + dispatch_async(queue, b); + // expected-warning@-1 {{Address of stack memory associated with local variable 'x' \ +might leak via the block capture}} +} + +dispatch_block_t get_leaking_block() { + int leaked_x = 791; + int *p = &leaked_x; + return ^void(void) { + *p = 1; + }; + // expected-warning@-3 {{Address of stack memory associated with local variable 'leaked_x' \ +might leak via the block capture}} +} + +void test_returned_from_func_block_async() { + dispatch_async(queue, get_leaking_block()); + // expected-warning@-1 {{Address of stack memory associated with local variable 'leaked_x' \ +might leak via the block capture}} +} + +// synchronous, no leak +void test_block_var_once() { + int x = 123; + int *p = &x; + void (^b)(void) = ^void(void) { + *p = 1; + }; + // no leak + dispatch_once(predicate, b); +} + +void test_block_var_after() { + int x = 123; + int *p = &x; + void (^b)(void) = ^void(void) { + *p = 1; + }; + dispatch_after(when, queue, b); + // expected-warning@-1 {{Address of stack memory associated with local variable 'x' \ +might leak via the block capture}} +} + +void test_block_var_async_no_leak() { + int x = 123; + int *p = &x; + void (^b)(void) = ^void(void) { + int y = x; + ++y; + }; + // no leak + dispatch_async(queue, b); +} + +void test_block_inside_block_async_no_leak() { + int x = 123; + int *p = &x; + void (^inner)(void) = ^void(void) { + int y = x; + ++y; + }; + void (^outer)(void) = ^void(void) { + int z = x; + ++z; + inner(); + }; + // no leak + dispatch_async(queue, outer); +} + + +