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   // expected-note@-1 {{Handle allocated through 2nd parameter}}
81   if (status == 0) { // expected-note {{Assuming 'status' is equal to 0}}
82                      // expected-note@-1 {{Taking true branch}}
83     return; // expected-warning {{Potential leak of handle}}
84             // expected-note@-1 {{Potential leak of handle}}
85   }
86   __builtin_trap();
87 }
88 
89 void checkNoCrash01() {
90   zx_handle_t sa, sb;
91   zx_channel_create(0, &sa, &sb);
92   moreArgs(sa, 1, 2, 3, 4, 5);
93   lessArgs(sa);
94   zx_handle_close(sa);
95   zx_handle_close(sb);
96 }
97 
98 void checkNoLeak01() {
99   zx_handle_t sa, sb;
100   zx_channel_create(0, &sa, &sb);
101   zx_handle_close(sa);
102   zx_handle_close(sb);
103 }
104 
105 void checkNoLeak02() {
106   zx_handle_t ay[2];
107   zx_channel_create(0, &ay[0], &ay[1]);
108   zx_handle_close(ay[0]);
109   zx_handle_close(ay[1]);
110 }
111 
112 void checkNoLeak03() {
113   zx_handle_t ay[2];
114   zx_channel_create(0, &ay[0], &ay[1]);
115   for (int i = 0; i < 2; i++)
116     zx_handle_close(ay[i]);
117 }
118 
119 zx_handle_t checkNoLeak04() {
120   zx_handle_t sa, sb;
121   zx_channel_create(0, &sa, &sb);
122   zx_handle_close(sa);
123   return sb; // no warning
124 }
125 
126 zx_handle_t checkNoLeak05(zx_handle_t *out1) {
127   zx_handle_t sa, sb;
128   zx_channel_create(0, &sa, &sb);
129   *out1 = sa;
130   return sb; // no warning
131 }
132 
133 void checkNoLeak06() {
134   zx_handle_t sa, sb;
135   if (zx_channel_create(0, &sa, &sb))
136     return;
137   zx_handle_close(sa);
138   zx_handle_close(sb);
139 }
140 
141 void checkLeak01(int tag) {
142   zx_handle_t sa, sb;
143   if (zx_channel_create(0, &sa, &sb)) // expected-note    {{Handle allocated through 2nd parameter}}
144     return;                           // expected-note@-1 {{Assuming the condition is false}}
145                                       // expected-note@-2 {{Taking false branch}}
146   use1(&sa);
147   if (tag) // expected-note {{Assuming 'tag' is 0}}
148     zx_handle_close(sa);
149   // expected-note@-2 {{Taking false branch}}
150   use2(sb); // expected-warning {{Potential leak of handle}}
151   // expected-note@-1 {{Potential leak of handle}}
152   zx_handle_close(sb);
153 }
154 
155 void checkLeakFromReturn01(int tag) {
156   zx_handle_t sa = return_handle(); // expected-note {{Function 'return_handle' returns an open handle}}
157   (void)sa;
158 } // expected-note {{Potential leak of handle}}
159   // expected-warning@-1 {{Potential leak of handle}}
160 
161 void checkReportLeakOnOnePath(int tag) {
162   zx_handle_t sa, sb;
163   if (zx_channel_create(0, &sa, &sb)) // expected-note {{Handle allocated through 2nd parameter}}
164     return;                           // expected-note@-1 {{Assuming the condition is false}}
165                                       // expected-note@-2 {{Taking false branch}}
166   zx_handle_close(sb);
167   switch(tag) { // expected-note {{Control jumps to the 'default' case at line}}
168     case 0:
169       use2(sa);
170       return;
171     case 1:
172       use2(sa);
173       return;
174     case 2:
175       use2(sa);
176       return;
177     case 3:
178       use2(sa);
179       return;
180     case 4:
181       use2(sa);
182       return;
183     default:
184       use2(sa);
185       return; // expected-warning {{Potential leak of handle}}
186               // expected-note@-1 {{Potential leak of handle}}
187   }
188 }
189 
190 void checkDoubleRelease01(int tag) {
191   zx_handle_t sa, sb;
192   zx_channel_create(0, &sa, &sb);
193   // expected-note@-1 {{Handle allocated through 2nd parameter}}
194   if (tag) // expected-note {{Assuming 'tag' is not equal to 0}}
195     zx_handle_close(sa); // expected-note {{Handle released through 1st parameter}}
196   // expected-note@-2 {{Taking true branch}}
197   zx_handle_close(sa); // expected-warning {{Releasing a previously released handle}}
198   // expected-note@-1 {{Releasing a previously released handle}}
199   zx_handle_close(sb);
200 }
201 
202 void checkUseAfterFree01(int tag) {
203   zx_handle_t sa, sb;
204   zx_channel_create(0, &sa, &sb);
205   // expected-note@-1 {{Handle allocated through 2nd parameter}}
206   // expected-note@-2 {{Handle allocated through 3rd parameter}}
207   // expected-note@+2 {{Taking true branch}}
208   // expected-note@+1 {{Taking false branch}}
209   if (tag) {
210     // expected-note@-1 {{Assuming 'tag' is not equal to 0}}
211     zx_handle_close(sa); // expected-note {{Handle released through 1st parameter}}
212     use1(&sa); // expected-warning {{Using a previously released handle}}
213     // expected-note@-1 {{Using a previously released handle}}
214   }
215   // expected-note@-6 {{Assuming 'tag' is 0}}
216   zx_handle_close(sb); // expected-note {{Handle released through 1st parameter}}
217   use2(sb); // expected-warning {{Using a previously released handle}}
218   // expected-note@-1 {{Using a previously released handle}}
219 }
220 
221 void checkMemberOperatorIndices() {
222   zx_handle_t sa, sb, sc;
223   zx_channel_create(0, &sa, &sb);
224   zx_handle_close(sb);
225   MyType t;
226   sc = t + sa;
227   zx_handle_close(sc);
228 }
229 
230 // RAII
231 
232 template <typename T>
233 struct HandleWrapper {
234   ~HandleWrapper() { close(); }
235   void close() {
236     if (handle != ZX_HANDLE_INVALID)
237       zx_handle_close(handle);
238   }
239   T *get_handle_address() { return &handle; }
240 private:
241   T handle;
242 };
243 
244 void doNotWarnOnRAII() {
245   HandleWrapper<zx_handle_t> w1;
246   zx_handle_t sb;
247   if (zx_channel_create(0, w1.get_handle_address(), &sb))
248     return;
249   zx_handle_close(sb);
250 }
251 
252 template <typename T>
253 struct HandleWrapperUnkonwDtor {
254   ~HandleWrapperUnkonwDtor();
255   void close() {
256     if (handle != ZX_HANDLE_INVALID)
257       zx_handle_close(handle);
258   }
259   T *get_handle_address() { return &handle; }
260 private:
261   T handle;
262 };
263 
264 void doNotWarnOnUnkownDtor() {
265   HandleWrapperUnkonwDtor<zx_handle_t> w1;
266   zx_handle_t sb;
267   if (zx_channel_create(0, w1.get_handle_address(), &sb))
268     return;
269   zx_handle_close(sb);
270 }
271 
272 // Various escaping scenarios
273 
274 zx_handle_t *get_handle_address();
275 
276 void escape_store_to_escaped_region01() {
277   zx_handle_t sb;
278   if (zx_channel_create(0, get_handle_address(), &sb))
279     return;
280   zx_handle_close(sb);
281 }
282 
283 struct object {
284   zx_handle_t *get_handle_address();
285 };
286 
287 void escape_store_to_escaped_region02(object &o) {
288   zx_handle_t sb;
289   // Same as above.
290   if (zx_channel_create(0, o.get_handle_address(), &sb))
291     return;
292   zx_handle_close(sb);
293 }
294 
295 void escape_store_to_escaped_region03(object o) {
296   zx_handle_t sb;
297   // Should we consider the pointee of get_handle_address escaped?
298   // Maybe we only should it consider escaped if o escapes?
299   if (zx_channel_create(0, o.get_handle_address(), &sb))
300     return;
301   zx_handle_close(sb);
302 }
303 
304 void escape_through_call(int tag) {
305   zx_handle_t sa, sb;
306   if (zx_channel_create(0, &sa, &sb))
307     return;
308   escape1(&sa);
309   if (tag)
310     escape2(sb);
311   else
312     escape3(sb);
313 }
314 
315 struct have_handle {
316   zx_handle_t h;
317   zx_handle_t *hp;
318 };
319 
320 void escape_through_store01(have_handle *handle) {
321   zx_handle_t sa;
322   if (zx_channel_create(0, &sa, handle->hp))
323     return;
324   handle->h = sa;
325 }
326 
327 have_handle global;
328 void escape_through_store02() {
329   zx_handle_t sa;
330   if (zx_channel_create(0, &sa, global.hp))
331     return;
332   global.h = sa;
333 }
334 
335 have_handle escape_through_store03() {
336   zx_handle_t sa, sb;
337   if (zx_channel_create(0, &sa, &sb))
338     return {0, nullptr};
339   zx_handle_close(sb);
340   return {sa, nullptr};
341 }
342 
343 void escape_structs(have_handle *);
344 void escape_transitively01() {
345   zx_handle_t sa, sb;
346   if (zx_channel_create(0, &sa, &sb))
347     return;
348   have_handle hs[2];
349   hs[1] = {sa, &sb};
350   escape_structs(hs);
351 }
352 
353 void escape_top_level_pointees(zx_handle_t *h) {
354   zx_handle_t h2;
355   if (zx_channel_create(0, h, &h2))
356     return;
357   zx_handle_close(h2);
358 } // *h should be escaped here. Right?
359