1 | // RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core,alpha.core.StackAddressAsyncEscape -fblocks -fobjc-arc -verify %s |
2 | |
3 | typedef struct dispatch_queue_s *dispatch_queue_t; |
4 | typedef void (^dispatch_block_t)(void); |
5 | void dispatch_async(dispatch_queue_t queue, dispatch_block_t block); |
6 | typedef long dispatch_once_t; |
7 | void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block); |
8 | typedef long dispatch_time_t; |
9 | void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block); |
10 | void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block); |
11 | |
12 | extern dispatch_queue_t queue; |
13 | extern dispatch_once_t *predicate; |
14 | extern dispatch_time_t when; |
15 | |
16 | void test_block_expr_async() { |
17 | int x = 123; |
18 | int *p = &x; |
19 | |
20 | dispatch_async(queue, ^{ |
21 | *p = 321; |
22 | }); |
23 | // expected-warning@-3 {{Address of stack memory associated with local variable 'x' \ |
24 | is captured by an asynchronously-executed block}} |
25 | } |
26 | |
27 | void test_block_expr_once_no_leak() { |
28 | int x = 123; |
29 | int *p = &x; |
30 | // synchronous, no warning |
31 | dispatch_once(predicate, ^{ |
32 | *p = 321; |
33 | }); |
34 | } |
35 | |
36 | void test_block_expr_after() { |
37 | int x = 123; |
38 | int *p = &x; |
39 | dispatch_after(when, queue, ^{ |
40 | *p = 321; |
41 | }); |
42 | // expected-warning@-3 {{Address of stack memory associated with local variable 'x' \ |
43 | is captured by an asynchronously-executed block}} |
44 | } |
45 | |
46 | void test_block_expr_async_no_leak() { |
47 | int x = 123; |
48 | int *p = &x; |
49 | // no leak |
50 | dispatch_async(queue, ^{ |
51 | int y = x; |
52 | ++y; |
53 | }); |
54 | } |
55 | |
56 | void test_block_var_async() { |
57 | int x = 123; |
58 | int *p = &x; |
59 | void (^b)(void) = ^void(void) { |
60 | *p = 1; |
61 | }; |
62 | dispatch_async(queue, b); |
63 | // expected-warning@-1 {{Address of stack memory associated with local variable 'x' \ |
64 | is captured by an asynchronously-executed block}} |
65 | } |
66 | |
67 | void test_block_with_ref_async() { |
68 | int x = 123; |
69 | int &r = x; |
70 | void (^b)(void) = ^void(void) { |
71 | r = 1; |
72 | }; |
73 | dispatch_async(queue, b); |
74 | // expected-warning@-1 {{Address of stack memory associated with local variable 'x' \ |
75 | is captured by an asynchronously-executed block}} |
76 | } |
77 | |
78 | dispatch_block_t get_leaking_block() { |
79 | int leaked_x = 791; |
80 | int *p = &leaked_x; |
81 | return ^void(void) { |
82 | *p = 1; |
83 | }; |
84 | // expected-warning@-3 {{Address of stack memory associated with local variable 'leaked_x' \ |
85 | is captured by a returned block}} |
86 | } |
87 | |
88 | void test_returned_from_func_block_async() { |
89 | dispatch_async(queue, get_leaking_block()); |
90 | // expected-warning@-1 {{Address of stack memory associated with local variable 'leaked_x' \ |
91 | is captured by an asynchronously-executed block}} |
92 | } |
93 | |
94 | // synchronous, no leak |
95 | void test_block_var_once() { |
96 | int x = 123; |
97 | int *p = &x; |
98 | void (^b)(void) = ^void(void) { |
99 | *p = 1; |
100 | }; |
101 | dispatch_once(predicate, b); // no-warning |
102 | } |
103 | |
104 | void test_block_var_after() { |
105 | int x = 123; |
106 | int *p = &x; |
107 | void (^b)(void) = ^void(void) { |
108 | *p = 1; |
109 | }; |
110 | dispatch_after(when, queue, b); |
111 | // expected-warning@-1 {{Address of stack memory associated with local variable 'x' \ |
112 | is captured by an asynchronously-executed block}} |
113 | } |
114 | |
115 | void test_block_var_async_no_leak() { |
116 | int x = 123; |
117 | int *p = &x; |
118 | void (^b)(void) = ^void(void) { |
119 | int y = x; |
120 | ++y; |
121 | }; |
122 | dispatch_async(queue, b); // no-warning |
123 | } |
124 | |
125 | void test_block_inside_block_async_no_leak() { |
126 | int x = 123; |
127 | int *p = &x; |
128 | void (^inner)(void) = ^void(void) { |
129 | int y = x; |
130 | ++y; |
131 | }; |
132 | void (^outer)(void) = ^void(void) { |
133 | int z = x; |
134 | ++z; |
135 | inner(); |
136 | }; |
137 | dispatch_async(queue, outer); // no-warning |
138 | } |
139 | |
140 | dispatch_block_t accept_and_pass_back_block(dispatch_block_t block) { |
141 | block(); |
142 | return block; // no-warning |
143 | } |
144 | |
145 | void test_passing_continuation_no_leak() { |
146 | int x = 123; |
147 | int *p = &x; |
148 | void (^cont)(void) = ^void(void) { |
149 | *p = 128; |
150 | }; |
151 | accept_and_pass_back_block(cont); // no-warning |
152 | } |
153 | |
154 | @interface NSObject |
155 | @end |
156 | @protocol OS_dispatch_semaphore |
157 | @end |
158 | typedef NSObject<OS_dispatch_semaphore> *dispatch_semaphore_t; |
159 | dispatch_semaphore_t dispatch_semaphore_create(long value); |
160 | long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout); |
161 | long dispatch_semaphore_signal(dispatch_semaphore_t dsema); |
162 | |
163 | void test_no_leaks_on_semaphore_pattern() { |
164 | int x = 0; |
165 | int *p = &x; |
166 | dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); |
167 | dispatch_async(queue, ^{ |
168 | *p = 1; |
169 | // Some work. |
170 | dispatch_semaphore_signal(semaphore); |
171 | }); // no-warning |
172 | |
173 | // Do some other work concurrently with the asynchronous work |
174 | // Wait for the asynchronous work to finish |
175 | dispatch_semaphore_wait(semaphore, 1000); |
176 | } |
177 | |
178 | void test_dispatch_barrier_sync() { |
179 | int buf[16]; |
180 | for (int n = 0; n < 16; ++n) { |
181 | int *ptr = &buf[n]; |
182 | // FIXME: Should not warn. The dispatch_barrier_sync() call ensures |
183 | // that the block does not outlive 'buf'. |
184 | dispatch_async(queue, ^{ // expected-warning{{Address of stack memory associated with local variable 'buf' is captured by an asynchronously-executed block}} |
185 | (void)ptr; |
186 | }); |
187 | } |
188 | dispatch_barrier_sync(queue, ^{}); |
189 | } |
190 | |