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