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,12 +27,15 @@ 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: @@ -92,8 +96,9 @@ return range; } -void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R, - const Expr *RetE) const { +void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, + const MemRegion *R, + const Expr *RetE) const { ExplodedNode *N = C.generateErrorNode(); if (!N) @@ -116,6 +121,47 @@ C.emitReport(std::move(report)); } +void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (!Call.isGlobalCFunction("dispatch_after") && + !Call.isGlobalCFunction("dispatch_async") && + !Call.isGlobalCFunction("dispatch_once")) + return; + + for (unsigned Idx = 0, NumArgs = Call.getNumArgs(); Idx < NumArgs; ++Idx) { + const BlockDataRegion *Block = + dyn_cast_or_null(Call.getArgSVal(Idx).getAsRegion()); + if (!Block) + continue; + BlockDataRegion::referenced_vars_iterator I = + Block->referenced_vars_begin(); + BlockDataRegion::referenced_vars_iterator E = Block->referenced_vars_end(); + for (; I != E; ++I) { + SVal Val = Call.getState()->getSVal(I.getCapturedRegion()); + const MemRegion *Region = Val.getAsRegion(); + if (!Region) + return; + if (dyn_cast_or_null(Region->getMemorySpace())) { + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + if (!BT_capturestackleak) + BT_capturestackleak = llvm::make_unique( + this, "Capture of address of stack-allocated memory"); + SmallString<512> Buf; + llvm::raw_svector_ostream Out(Buf); + genName(Out, Region, C.getASTContext()); + Out << " was captured by the block which will be executed " + "asynchronously"; + auto report = + llvm::make_unique(*BT_capturestackleak, Out.str(), N); + report->addRange(Call.getArgExpr(Idx)->getSourceRange()); + C.emitReport(std::move(report)); + } + } + } +} + void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { @@ -132,7 +178,7 @@ return; const StackSpaceRegion *SS = - dyn_cast_or_null(R->getMemorySpace()); + dyn_cast_or_null(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,97 @@ +// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core -analyzer-store=region -fblocks -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' was captured by the block \ +which will be executed asynchronously}} +} + +void test_block_expr_once() { + int x = 123; + int *p = &x; + dispatch_once(predicate, ^{ + *p = 321; + }); + // expected-warning@-3 {{Address of stack memory associated with local variable 'x' was captured by the block \ +which will be executed asynchronously}} +} + +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' was captured by the block \ +which will be executed asynchronously}} +} + +void test_block_expr_async_no_leak() { + int x = 123; + int *p = &x; + + 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' was captured by the block \ +which will be executed asynchronously}} +} + +void test_block_var_once() { + int x = 123; + int *p = &x; + void (^b)(void) = ^void(void) { + *p = 1; + }; + dispatch_once(predicate, b); + // expected-warning@-1 {{Address of stack memory associated with local variable 'x' was captured by the block \ +which will be executed asynchronously}} +} + +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' was captured by the block \ +which will be executed asynchronously}} +} + +void test_block_var_async_no_leak() { + int x = 123; + int *p = &x; + void (^b)(void) = ^void(void) { + int y = x; + ++y; + }; + dispatch_async(queue, b); +}