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