diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -310,6 +310,9 @@ /// referred by an other stack variable from different stack frame. bool checkForDanglingStackVariable(const MemRegion *Referrer, const MemRegion *Referred) { + // Under ARC, blocks are retained and released automatically: + if (isArcManagedBlock(Referred, Ctx)) + return false; const auto *ReferrerMemSpace = Referrer->getMemorySpace()->getAs(); const auto *ReferredMemSpace = diff --git a/clang/test/Analysis/stack-capture-leak-arc.mm b/clang/test/Analysis/stack-capture-leak-arc.mm --- a/clang/test/Analysis/stack-capture-leak-arc.mm +++ b/clang/test/Analysis/stack-capture-leak-arc.mm @@ -8,6 +8,7 @@ typedef long dispatch_time_t; void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block); void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block); +void f(int); extern dispatch_queue_t queue; extern dispatch_once_t *predicate; @@ -187,3 +188,40 @@ } dispatch_barrier_sync(queue, ^{}); } + +void output_block(dispatch_block_t * blk) { + int x = 0; + *blk = ^{ f(x); }; +} + +// Block objects themselves can never leak under ARC. +void test_no_block_leak() { + __block dispatch_block_t blk; + int x = 0; + dispatch_block_t p = ^{ + blk = ^{ + f(x); + }; + }; + p(); + blk(); + output_block(&blk); + blk(); +} + +// Block objects do not leak under ARC but stack variables of +// non-object kind indirectly referred by a block can leak. +dispatch_block_t test_block_referencing_variable_leak() { + int x = 0; + __block int * p = &x; + __block int * q = &x; + + dispatch_async(queue, ^{// expected-warning {{Address of stack memory associated with local variable 'x' is captured by an asynchronously-executed block \ +[alpha.core.StackAddressAsyncEscape]}} + ++(*p); + }); + return (dispatch_block_t) ^{// expected-warning {{Address of stack memory associated with local variable 'x' is captured by a returned block \ +[core.StackAddressEscape]}} + ++(*q); + }; +} diff --git a/clang/test/Analysis/stack-capture-leak-no-arc.mm b/clang/test/Analysis/stack-capture-leak-no-arc.mm --- a/clang/test/Analysis/stack-capture-leak-no-arc.mm +++ b/clang/test/Analysis/stack-capture-leak-no-arc.mm @@ -4,6 +4,7 @@ typedef void (^dispatch_block_t)(void); void dispatch_async(dispatch_queue_t queue, dispatch_block_t block); extern dispatch_queue_t queue; +void f(int); void test_block_inside_block_async_no_leak() { int x = 123; @@ -35,3 +36,46 @@ return outer; // expected-warning-re{{Address of stack-allocated block declared on line {{.+}} is captured by a returned block}} } +// The block literal defined in this function could leak once being +// called. +void output_block(dispatch_block_t * blk) { + int x = 0; + *blk = ^{ f(x); }; // expected-warning {{Address of stack-allocated block declared on line 43 is still referred to by the stack variable 'blk' upon returning to the caller. This will be a dangling reference [core.StackAddressEscape]}} +} + +// The block literal captures nothing thus is treated as a constant. +void output_constant_block(dispatch_block_t * blk) { + *blk = ^{ }; +} + +// A block can leak if it captures at least one variable and is not +// under ARC when its' stack frame expires. +void test_block_leak() { + __block dispatch_block_t blk; + int x = 0; + dispatch_block_t p = ^{ + blk = ^{ // expected-warning {{Address of stack-allocated block declared on line 57 is still referred to by the stack variable 'blk' upon returning to the caller. This will be a dangling reference [core.StackAddressEscape]}} + f(x); + }; + }; + + p(); + blk(); + output_block(&blk); + blk(); +} + +// A block captures nothing is a constant thus never leaks. +void test_constant_block_no_leak() { + __block dispatch_block_t blk; + dispatch_block_t p = ^{ + blk = ^{ + f(0); + }; + }; + + p(); + blk(); + output_constant_block(&blk); + blk(); +}