1 // RUN: %clang_analyze_cc1 -analyzer-checker=core,fuchsia.HandleChecker -analyzer-output=text \
2 // RUN:     -verify %s
3 
4 typedef __typeof__(sizeof(int)) size_t;
5 typedef int zx_status_t;
6 typedef __typeof__(sizeof(int)) zx_handle_t;
7 typedef unsigned int uint32_t;
8 #define NULL ((void *)0)
9 #define ZX_HANDLE_INVALID 0
10 
11 #if defined(__clang__)
12 #define ZX_HANDLE_ACQUIRE  __attribute__((acquire_handle("Fuchsia")))
13 #define ZX_HANDLE_RELEASE  __attribute__((release_handle("Fuchsia")))
14 #define ZX_HANDLE_USE  __attribute__((use_handle("Fuchsia")))
15 #else
16 #define ZX_HANDLE_ACQUIRE
17 #define ZX_HANDLE_RELEASE
18 #define ZX_HANDLE_USE
19 #endif
20 
21 zx_status_t zx_channel_create(
22     uint32_t options,
23     zx_handle_t *out0 ZX_HANDLE_ACQUIRE,
24     zx_handle_t *out1 ZX_HANDLE_ACQUIRE);
25 
26 zx_status_t zx_handle_close(
27     zx_handle_t handle ZX_HANDLE_RELEASE);
28 
29 ZX_HANDLE_ACQUIRE
30 zx_handle_t return_handle();
31 
32 void escape1(zx_handle_t *in);
33 void escape2(zx_handle_t in);
34 void (*escape3)(zx_handle_t) = escape2;
35 
36 void use1(const zx_handle_t *in ZX_HANDLE_USE);
37 void use2(zx_handle_t in ZX_HANDLE_USE);
38 
39 void moreArgs(zx_handle_t, int, ...);
40 void lessArgs(zx_handle_t, int a = 5);
41 
42 // To test if argument indexes are OK for operator calls.
43 struct MyType {
44   ZX_HANDLE_ACQUIRE
45   zx_handle_t operator+(zx_handle_t ZX_HANDLE_RELEASE replace);
46 };
47 
48 void checkInvalidHandle01() {
49   zx_handle_t sa, sb;
50   zx_channel_create(0, &sa, &sb);
51   if (sa == ZX_HANDLE_INVALID)
52     ;
53   // Will we ever see a warning like below?
54   // We eagerly replace the symbol with a constant and lose info...
55   use2(sa); // TODOexpected-warning {{Use of an invalid handle}}
56   zx_handle_close(sb);
57   zx_handle_close(sa);
58 }
59 
60 void checkInvalidHandle2() {
61   zx_handle_t sa, sb;
62   zx_channel_create(0, &sa, &sb);
63   if (sb != ZX_HANDLE_INVALID)
64     zx_handle_close(sb);
65   if (sa != ZX_HANDLE_INVALID)
66     zx_handle_close(sa);
67 }
68 
69 void handleDieBeforeErrorSymbol01() {
70   zx_handle_t sa, sb;
71   zx_status_t status = zx_channel_create(0, &sa, &sb);
72   if (status < 0)
73     return;
74   __builtin_trap();
75 }
76 
77 void handleDieBeforeErrorSymbol02() {
78   zx_handle_t sa, sb;
79   zx_status_t status = zx_channel_create(0, &sa, &sb);
80   // FIXME: There appears to be non-determinism in choosing
81   // which handle to report.
82   // expected-note-re@-3 {{Handle allocated through {{(2nd|3rd)}} parameter}}
83   if (status == 0) { // expected-note {{Assuming 'status' is equal to 0}}
84                      // expected-note@-1 {{Taking true branch}}
85     return; // expected-warning {{Potential leak of handle}}
86             // expected-note@-1 {{Potential leak of handle}}
87   }
88   __builtin_trap();
89 }
90 
91 void checkNoCrash01() {
92   zx_handle_t sa, sb;
93   zx_channel_create(0, &sa, &sb);
94   moreArgs(sa, 1, 2, 3, 4, 5);
95   lessArgs(sa);
96   zx_handle_close(sa);
97   zx_handle_close(sb);
98 }
99 
100 void checkNoLeak01() {
101   zx_handle_t sa, sb;
102   zx_channel_create(0, &sa, &sb);
103   zx_handle_close(sa);
104   zx_handle_close(sb);
105 }
106 
107 void checkNoLeak02() {
108   zx_handle_t ay[2];
109   zx_channel_create(0, &ay[0], &ay[1]);
110   zx_handle_close(ay[0]);
111   zx_handle_close(ay[1]);
112 }
113 
114 void checkNoLeak03() {
115   zx_handle_t ay[2];
116   zx_channel_create(0, &ay[0], &ay[1]);
117   for (int i = 0; i < 2; i++)
118     zx_handle_close(ay[i]);
119 }
120 
121 zx_handle_t checkNoLeak04() {
122   zx_handle_t sa, sb;
123   zx_channel_create(0, &sa, &sb);
124   zx_handle_close(sa);
125   return sb; // no warning
126 }
127 
128 zx_handle_t checkNoLeak05(zx_handle_t *out1) {
129   zx_handle_t sa, sb;
130   zx_channel_create(0, &sa, &sb);
131   *out1 = sa;
132   return sb; // no warning
133 }
134 
135 void checkNoLeak06() {
136   zx_handle_t sa, sb;
137   if (zx_channel_create(0, &sa, &sb))
138     return;
139   zx_handle_close(sa);
140   zx_handle_close(sb);
141 }
142 
143 void checkLeak01(int tag) {
144   zx_handle_t sa, sb;
145   if (zx_channel_create(0, &sa, &sb)) // expected-note    {{Handle allocated through 2nd parameter}}
146     return;                           // expected-note@-1 {{Assuming the condition is false}}
147                                       // expected-note@-2 {{Taking false branch}}
148   use1(&sa);
149   if (tag) // expected-note {{Assuming 'tag' is 0}}
150     zx_handle_close(sa);
151   // expected-note@-2 {{Taking false branch}}
152   use2(sb); // expected-warning {{Potential leak of handle}}
153   // expected-note@-1 {{Potential leak of handle}}
154   zx_handle_close(sb);
155 }
156 
157 void checkLeakFromReturn01(int tag) {
158   zx_handle_t sa = return_handle(); // expected-note {{Function 'return_handle' returns an open handle}}
159   (void)sa;
160 } // expected-note {{Potential leak of handle}}
161   // expected-warning@-1 {{Potential leak of handle}}
162 
163 void checkReportLeakOnOnePath(int tag) {
164   zx_handle_t sa, sb;
165   if (zx_channel_create(0, &sa, &sb)) // expected-note {{Handle allocated through 2nd parameter}}
166     return;                           // expected-note@-1 {{Assuming the condition is false}}
167                                       // expected-note@-2 {{Taking false branch}}
168   zx_handle_close(sb);
169   switch(tag) { // expected-note {{Control jumps to the 'default' case at line}}
170     case 0:
171       use2(sa);
172       return;
173     case 1:
174       use2(sa);
175       return;
176     case 2:
177       use2(sa);
178       return;
179     case 3:
180       use2(sa);
181       return;
182     case 4:
183       use2(sa);
184       return;
185     default:
186       use2(sa);
187       return; // expected-warning {{Potential leak of handle}}
188               // expected-note@-1 {{Potential leak of handle}}
189   }
190 }
191 
192 void checkDoubleRelease01(int tag) {
193   zx_handle_t sa, sb;
194   zx_channel_create(0, &sa, &sb);
195   // expected-note@-1 {{Handle allocated through 2nd parameter}}
196   if (tag) // expected-note {{Assuming 'tag' is not equal to 0}}
197     zx_handle_close(sa); // expected-note {{Handle released through 1st parameter}}
198   // expected-note@-2 {{Taking true branch}}
199   zx_handle_close(sa); // expected-warning {{Releasing a previously released handle}}
200   // expected-note@-1 {{Releasing a previously released handle}}
201   zx_handle_close(sb);
202 }
203 
204 void checkUseAfterFree01(int tag) {
205   zx_handle_t sa, sb;
206   zx_channel_create(0, &sa, &sb);
207   // expected-note@-1 {{Handle allocated through 2nd parameter}}
208   // expected-note@-2 {{Handle allocated through 3rd parameter}}
209   // expected-note@+2 {{Taking true branch}}
210   // expected-note@+1 {{Taking false branch}}
211   if (tag) {
212     // expected-note@-1 {{Assuming 'tag' is not equal to 0}}
213     zx_handle_close(sa); // expected-note {{Handle released through 1st parameter}}
214     use1(&sa); // expected-warning {{Using a previously released handle}}
215     // expected-note@-1 {{Using a previously released handle}}
216   }
217   // expected-note@-6 {{Assuming 'tag' is 0}}
218   zx_handle_close(sb); // expected-note {{Handle released through 1st parameter}}
219   use2(sb); // expected-warning {{Using a previously released handle}}
220   // expected-note@-1 {{Using a previously released handle}}
221 }
222 
223 void checkMemberOperatorIndices() {
224   zx_handle_t sa, sb, sc;
225   zx_channel_create(0, &sa, &sb);
226   zx_handle_close(sb);
227   MyType t;
228   sc = t + sa;
229   zx_handle_close(sc);
230 }
231 
232 // RAII
233 
234 template <typename T>
235 struct HandleWrapper {
236   ~HandleWrapper() { close(); }
237   void close() {
238     if (handle != ZX_HANDLE_INVALID)
239       zx_handle_close(handle);
240   }
241   T *get_handle_address() { return &handle; }
242 private:
243   T handle;
244 };
245 
246 void doNotWarnOnRAII() {
247   HandleWrapper<zx_handle_t> w1;
248   zx_handle_t sb;
249   if (zx_channel_create(0, w1.get_handle_address(), &sb))
250     return;
251   zx_handle_close(sb);
252 }
253 
254 template <typename T>
255 struct HandleWrapperUnkonwDtor {
256   ~HandleWrapperUnkonwDtor();
257   void close() {
258     if (handle != ZX_HANDLE_INVALID)
259       zx_handle_close(handle);
260   }
261   T *get_handle_address() { return &handle; }
262 private:
263   T handle;
264 };
265 
266 void doNotWarnOnUnknownDtor() {
267   HandleWrapperUnkonwDtor<zx_handle_t> w1;
268   zx_handle_t sb;
269   if (zx_channel_create(0, w1.get_handle_address(), &sb))
270     return;
271   zx_handle_close(sb);
272 }
273 
274 // Various escaping scenarios
275 
276 zx_handle_t *get_handle_address();
277 
278 void escape_store_to_escaped_region01() {
279   zx_handle_t sb;
280   if (zx_channel_create(0, get_handle_address(), &sb))
281     return;
282   zx_handle_close(sb);
283 }
284 
285 struct object {
286   zx_handle_t *get_handle_address();
287 };
288 
289 void escape_store_to_escaped_region02(object &o) {
290   zx_handle_t sb;
291   // Same as above.
292   if (zx_channel_create(0, o.get_handle_address(), &sb))
293     return;
294   zx_handle_close(sb);
295 }
296 
297 void escape_store_to_escaped_region03(object o) {
298   zx_handle_t sb;
299   // Should we consider the pointee of get_handle_address escaped?
300   // Maybe we only should it consider escaped if o escapes?
301   if (zx_channel_create(0, o.get_handle_address(), &sb))
302     return;
303   zx_handle_close(sb);
304 }
305 
306 void escape_through_call(int tag) {
307   zx_handle_t sa, sb;
308   if (zx_channel_create(0, &sa, &sb))
309     return;
310   escape1(&sa);
311   if (tag)
312     escape2(sb);
313   else
314     escape3(sb);
315 }
316 
317 struct have_handle {
318   zx_handle_t h;
319   zx_handle_t *hp;
320 };
321 
322 void escape_through_store01(have_handle *handle) {
323   zx_handle_t sa;
324   if (zx_channel_create(0, &sa, handle->hp))
325     return;
326   handle->h = sa;
327 }
328 
329 have_handle global;
330 void escape_through_store02() {
331   zx_handle_t sa;
332   if (zx_channel_create(0, &sa, global.hp))
333     return;
334   global.h = sa;
335 }
336 
337 have_handle escape_through_store03() {
338   zx_handle_t sa, sb;
339   if (zx_channel_create(0, &sa, &sb))
340     return {0, nullptr};
341   zx_handle_close(sb);
342   return {sa, nullptr};
343 }
344 
345 void escape_structs(have_handle *);
346 void escape_transitively01() {
347   zx_handle_t sa, sb;
348   if (zx_channel_create(0, &sa, &sb))
349     return;
350   have_handle hs[2];
351   hs[1] = {sa, &sb};
352   escape_structs(hs);
353 }
354 
355 void escape_top_level_pointees(zx_handle_t *h) {
356   zx_handle_t h2;
357   if (zx_channel_create(0, h, &h2))
358     return;
359   zx_handle_close(h2);
360 } // *h should be escaped here. Right?
361