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