| // RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core,alpha.core.StackAddressAsyncEscape -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); |
| void dispatch_barrier_sync(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' \ |
| is captured by an asynchronously-executed block}} |
| } |
| |
| 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' \ |
| is captured by an asynchronously-executed block}} |
| } |
| |
| 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' \ |
| is captured by an asynchronously-executed block}} |
| } |
| |
| void test_block_with_ref_async() { |
| int x = 123; |
| int &r = x; |
| void (^b)(void) = ^void(void) { |
| r = 1; |
| }; |
| dispatch_async(queue, b); |
| // expected-warning@-1 {{Address of stack memory associated with local variable 'x' \ |
| is captured by an asynchronously-executed block}} |
| } |
| |
| 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' \ |
| is captured by a returned block}} |
| } |
| |
| 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' \ |
| is captured by an asynchronously-executed block}} |
| } |
| |
| // synchronous, no leak |
| void test_block_var_once() { |
| int x = 123; |
| int *p = &x; |
| void (^b)(void) = ^void(void) { |
| *p = 1; |
| }; |
| dispatch_once(predicate, b); // no-warning |
| } |
| |
| 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' \ |
| is captured by an asynchronously-executed block}} |
| } |
| |
| 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); // no-warning |
| } |
| |
| 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(); |
| }; |
| dispatch_async(queue, outer); // no-warning |
| } |
| |
| dispatch_block_t accept_and_pass_back_block(dispatch_block_t block) { |
| block(); |
| return block; // no-warning |
| } |
| |
| void test_passing_continuation_no_leak() { |
| int x = 123; |
| int *p = &x; |
| void (^cont)(void) = ^void(void) { |
| *p = 128; |
| }; |
| accept_and_pass_back_block(cont); // no-warning |
| } |
| |
| @interface NSObject |
| @end |
| @protocol OS_dispatch_semaphore |
| @end |
| typedef NSObject<OS_dispatch_semaphore> *dispatch_semaphore_t; |
| dispatch_semaphore_t dispatch_semaphore_create(long value); |
| long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout); |
| long dispatch_semaphore_signal(dispatch_semaphore_t dsema); |
| |
| void test_no_leaks_on_semaphore_pattern() { |
| int x = 0; |
| int *p = &x; |
| dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); |
| dispatch_async(queue, ^{ |
| *p = 1; |
| // Some work. |
| dispatch_semaphore_signal(semaphore); |
| }); // no-warning |
| |
| // Do some other work concurrently with the asynchronous work |
| // Wait for the asynchronous work to finish |
| dispatch_semaphore_wait(semaphore, 1000); |
| } |
| |
| void test_dispatch_barrier_sync() { |
| int buf[16]; |
| for (int n = 0; n < 16; ++n) { |
| int *ptr = &buf[n]; |
| // FIXME: Should not warn. The dispatch_barrier_sync() call ensures |
| // that the block does not outlive 'buf'. |
| dispatch_async(queue, ^{ // expected-warning{{Address of stack memory associated with local variable 'buf' is captured by an asynchronously-executed block}} |
| (void)ptr; |
| }); |
| } |
| dispatch_barrier_sync(queue, ^{}); |
| } |