|  | // RUN: %clang_analyze_cc1 -analyzer-checker=core,fuchsia.HandleChecker -analyzer-output=text \ | 
|  | // RUN:     -verify %s | 
|  |  | 
|  | typedef __typeof__(sizeof(int)) size_t; | 
|  | typedef int zx_status_t; | 
|  | typedef __typeof__(sizeof(int)) zx_handle_t; | 
|  | typedef unsigned int uint32_t; | 
|  | #define NULL ((void *)0) | 
|  | #define ZX_HANDLE_INVALID 0 | 
|  |  | 
|  | #if defined(__clang__) | 
|  | #define ZX_HANDLE_ACQUIRE __attribute__((acquire_handle("Fuchsia"))) | 
|  | #define ZX_HANDLE_RELEASE __attribute__((release_handle("Fuchsia"))) | 
|  | #define ZX_HANDLE_USE __attribute__((use_handle("Fuchsia"))) | 
|  | #define ZX_HANDLE_ACQUIRE_UNOWNED __attribute__((acquire_handle("FuchsiaUnowned"))) | 
|  | #else | 
|  | #define ZX_HANDLE_ACQUIRE | 
|  | #define ZX_HANDLE_RELEASE | 
|  | #define ZX_HANDLE_USE | 
|  | #define ZX_HANDLE_ACQUIRE_UNOWNED | 
|  | #endif | 
|  |  | 
|  | zx_status_t zx_channel_create( | 
|  | uint32_t options, | 
|  | zx_handle_t *out0 ZX_HANDLE_ACQUIRE, | 
|  | zx_handle_t *out1 ZX_HANDLE_ACQUIRE); | 
|  |  | 
|  | zx_status_t zx_handle_close( | 
|  | zx_handle_t handle ZX_HANDLE_RELEASE); | 
|  |  | 
|  | ZX_HANDLE_ACQUIRE_UNOWNED | 
|  | zx_handle_t zx_process_self(); | 
|  |  | 
|  | void zx_process_self_param(zx_handle_t *out ZX_HANDLE_ACQUIRE_UNOWNED); | 
|  |  | 
|  | ZX_HANDLE_ACQUIRE | 
|  | zx_handle_t return_handle(); | 
|  |  | 
|  | void escape1(zx_handle_t *in); | 
|  | void escape2(zx_handle_t in); | 
|  | void (*escape3)(zx_handle_t) = escape2; | 
|  |  | 
|  | void use1(const zx_handle_t *in ZX_HANDLE_USE); | 
|  | void use2(zx_handle_t in ZX_HANDLE_USE); | 
|  |  | 
|  | void moreArgs(zx_handle_t, int, ...); | 
|  | void lessArgs(zx_handle_t, int a = 5); | 
|  |  | 
|  | // To test if argument indexes are OK for operator calls. | 
|  | struct MyType { | 
|  | ZX_HANDLE_ACQUIRE | 
|  | zx_handle_t operator+(zx_handle_t ZX_HANDLE_RELEASE replace); | 
|  | }; | 
|  |  | 
|  | void checkUnownedHandle01() { | 
|  | zx_handle_t h0; | 
|  | h0 = zx_process_self(); // expected-note {{Function 'zx_process_self' returns an unowned handle}} | 
|  | zx_handle_close(h0);    // expected-warning {{Releasing an unowned handle}} | 
|  | // expected-note@-1 {{Releasing an unowned handle}} | 
|  | } | 
|  |  | 
|  | void checkUnownedHandle02() { | 
|  | zx_handle_t h0; | 
|  | zx_process_self_param(&h0); // expected-note {{Unowned handle allocated through 1st parameter}} | 
|  | zx_handle_close(h0);        // expected-warning {{Releasing an unowned handle}} | 
|  | // expected-note@-1 {{Releasing an unowned handle}} | 
|  | } | 
|  |  | 
|  | void checkInvalidHandle01() { | 
|  | zx_handle_t sa, sb; | 
|  | zx_channel_create(0, &sa, &sb); | 
|  | if (sa == ZX_HANDLE_INVALID) | 
|  | ; | 
|  | // Will we ever see a warning like below? | 
|  | // We eagerly replace the symbol with a constant and lose info... | 
|  | use2(sa); // TODOexpected-warning {{Use of an invalid handle}} | 
|  | zx_handle_close(sb); | 
|  | zx_handle_close(sa); | 
|  | } | 
|  |  | 
|  | void checkInvalidHandle2() { | 
|  | zx_handle_t sa, sb; | 
|  | zx_channel_create(0, &sa, &sb); | 
|  | if (sb != ZX_HANDLE_INVALID) | 
|  | zx_handle_close(sb); | 
|  | if (sa != ZX_HANDLE_INVALID) | 
|  | zx_handle_close(sa); | 
|  | } | 
|  |  | 
|  | void handleDieBeforeErrorSymbol01() { | 
|  | zx_handle_t sa, sb; | 
|  | zx_status_t status = zx_channel_create(0, &sa, &sb); | 
|  | if (status < 0) | 
|  | return; | 
|  | __builtin_trap(); | 
|  | } | 
|  |  | 
|  | void handleDieBeforeErrorSymbol02() { | 
|  | zx_handle_t sa, sb; | 
|  | zx_status_t status = zx_channel_create(0, &sa, &sb); | 
|  | // FIXME: There appears to be non-determinism in choosing | 
|  | // which handle to report. | 
|  | // expected-note-re@-3 {{Handle allocated through {{(2nd|3rd)}} parameter}} | 
|  | if (status == 0) { // expected-note {{Assuming 'status' is equal to 0}} | 
|  | // expected-note@-1 {{Taking true branch}} | 
|  | return;          // expected-warning {{Potential leak of handle}} | 
|  | // expected-note@-1 {{Potential leak of handle}} | 
|  | } | 
|  | __builtin_trap(); | 
|  | } | 
|  |  | 
|  | void checkNoCrash01() { | 
|  | zx_handle_t sa, sb; | 
|  | zx_channel_create(0, &sa, &sb); | 
|  | moreArgs(sa, 1, 2, 3, 4, 5); | 
|  | lessArgs(sa); | 
|  | zx_handle_close(sa); | 
|  | zx_handle_close(sb); | 
|  | } | 
|  |  | 
|  | void checkNoLeak01() { | 
|  | zx_handle_t sa, sb; | 
|  | zx_channel_create(0, &sa, &sb); | 
|  | zx_handle_close(sa); | 
|  | zx_handle_close(sb); | 
|  | } | 
|  |  | 
|  | void checkNoLeak02() { | 
|  | zx_handle_t ay[2]; | 
|  | zx_channel_create(0, &ay[0], &ay[1]); | 
|  | zx_handle_close(ay[0]); | 
|  | zx_handle_close(ay[1]); | 
|  | } | 
|  |  | 
|  | void checkNoLeak03() { | 
|  | zx_handle_t ay[2]; | 
|  | zx_channel_create(0, &ay[0], &ay[1]); | 
|  | for (int i = 0; i < 2; i++) | 
|  | zx_handle_close(ay[i]); | 
|  | } | 
|  |  | 
|  | zx_handle_t checkNoLeak04() { | 
|  | zx_handle_t sa, sb; | 
|  | zx_channel_create(0, &sa, &sb); | 
|  | zx_handle_close(sa); | 
|  | return sb; // no warning | 
|  | } | 
|  |  | 
|  | zx_handle_t checkNoLeak05(zx_handle_t *out1) { | 
|  | zx_handle_t sa, sb; | 
|  | zx_channel_create(0, &sa, &sb); | 
|  | *out1 = sa; | 
|  | return sb; // no warning | 
|  | } | 
|  |  | 
|  | void checkNoLeak06() { | 
|  | zx_handle_t sa, sb; | 
|  | if (zx_channel_create(0, &sa, &sb)) | 
|  | return; | 
|  | zx_handle_close(sa); | 
|  | zx_handle_close(sb); | 
|  | } | 
|  |  | 
|  | void checkLeak01(int tag) { | 
|  | zx_handle_t sa, sb; | 
|  | if (zx_channel_create(0, &sa, &sb)) // expected-note    {{Handle allocated through 2nd parameter}} | 
|  | return;                           // expected-note@-1 {{Assuming the condition is false}} | 
|  | // expected-note@-2 {{Taking false branch}} | 
|  | use1(&sa); | 
|  | if (tag) // expected-note {{Assuming 'tag' is 0}} | 
|  | zx_handle_close(sa); | 
|  | // expected-note@-2 {{Taking false branch}} | 
|  | use2(sb); // expected-warning {{Potential leak of handle}} | 
|  | // expected-note@-1 {{Potential leak of handle}} | 
|  | zx_handle_close(sb); | 
|  | } | 
|  |  | 
|  | void checkLeakFromReturn01(int tag) { | 
|  | zx_handle_t sa = return_handle(); // expected-note {{Function 'return_handle' returns an open handle}} | 
|  | (void)sa; | 
|  | } // expected-note {{Potential leak of handle}} | 
|  | // expected-warning@-1 {{Potential leak of handle}} | 
|  |  | 
|  | void checkReportLeakOnOnePath(int tag) { | 
|  | zx_handle_t sa, sb; | 
|  | if (zx_channel_create(0, &sa, &sb)) // expected-note {{Handle allocated through 2nd parameter}} | 
|  | return;                           // expected-note@-1 {{Assuming the condition is false}} | 
|  | // expected-note@-2 {{Taking false branch}} | 
|  | zx_handle_close(sb); | 
|  | switch (tag) { // expected-note {{Control jumps to the 'default' case at line}} | 
|  | case 0: | 
|  | use2(sa); | 
|  | return; | 
|  | case 1: | 
|  | use2(sa); | 
|  | return; | 
|  | case 2: | 
|  | use2(sa); | 
|  | return; | 
|  | case 3: | 
|  | use2(sa); | 
|  | return; | 
|  | case 4: | 
|  | use2(sa); | 
|  | return; | 
|  | default: | 
|  | use2(sa); | 
|  | return; // expected-warning {{Potential leak of handle}} | 
|  | // expected-note@-1 {{Potential leak of handle}} | 
|  | } | 
|  | } | 
|  |  | 
|  | void checkDoubleRelease01(int tag) { | 
|  | zx_handle_t sa, sb; | 
|  | zx_channel_create(0, &sa, &sb); | 
|  | // expected-note@-1 {{Handle allocated through 2nd parameter}} | 
|  | if (tag)               // expected-note {{Assuming 'tag' is not equal to 0}} | 
|  | zx_handle_close(sa); // expected-note {{Handle released through 1st parameter}} | 
|  | // expected-note@-2 {{Taking true branch}} | 
|  | zx_handle_close(sa); // expected-warning {{Releasing a previously released handle}} | 
|  | // expected-note@-1 {{Releasing a previously released handle}} | 
|  | zx_handle_close(sb); | 
|  | } | 
|  |  | 
|  | void checkUseAfterFree01(int tag) { | 
|  | zx_handle_t sa, sb; | 
|  | zx_channel_create(0, &sa, &sb); | 
|  | // expected-note@-1 {{Handle allocated through 2nd parameter}} | 
|  | // expected-note@-2 {{Handle allocated through 3rd parameter}} | 
|  | // expected-note@+2 {{Taking true branch}} | 
|  | // expected-note@+1 {{Taking false branch}} | 
|  | if (tag) { | 
|  | // expected-note@-1 {{Assuming 'tag' is not equal to 0}} | 
|  | zx_handle_close(sa); // expected-note {{Handle released through 1st parameter}} | 
|  | use1(&sa);           // expected-warning {{Using a previously released handle}} | 
|  | // expected-note@-1 {{Using a previously released handle}} | 
|  | } | 
|  | // expected-note@-6 {{Assuming 'tag' is 0}} | 
|  | zx_handle_close(sb); // expected-note {{Handle released through 1st parameter}} | 
|  | use2(sb);            // expected-warning {{Using a previously released handle}} | 
|  | // expected-note@-1 {{Using a previously released handle}} | 
|  | } | 
|  |  | 
|  | void checkMemberOperatorIndices() { | 
|  | zx_handle_t sa, sb, sc; | 
|  | zx_channel_create(0, &sa, &sb); | 
|  | zx_handle_close(sb); | 
|  | MyType t; | 
|  | sc = t + sa; | 
|  | zx_handle_close(sc); | 
|  | } | 
|  |  | 
|  | struct HandleStruct { | 
|  | zx_handle_t h; | 
|  | }; | 
|  |  | 
|  | void close_handle_struct(HandleStruct hs ZX_HANDLE_RELEASE); | 
|  |  | 
|  | void use_handle_struct(HandleStruct hs ZX_HANDLE_USE); | 
|  |  | 
|  | void checkHandleInStructureUseAfterFree() { | 
|  | zx_handle_t sa, sb; | 
|  | zx_channel_create(0, &sa, &sb); // expected-note {{Handle allocated through 3rd parameter}} | 
|  | HandleStruct hs; | 
|  | hs.h = sb; | 
|  | use_handle_struct(hs); | 
|  | close_handle_struct(hs); // expected-note {{Handle released through 1st parameter}} | 
|  | zx_handle_close(sa); | 
|  |  | 
|  | use2(sb); // expected-warning {{Using a previously released handle}} | 
|  | // expected-note@-1 {{Using a previously released handle}} | 
|  | } | 
|  |  | 
|  | void checkHandleInStructureUseAfterFree2() { | 
|  | zx_handle_t sa, sb; | 
|  | zx_channel_create(0, &sa, &sb); // expected-note {{Handle allocated through 3rd parameter}} | 
|  | HandleStruct hs; | 
|  | hs.h = sb; | 
|  | use_handle_struct(hs); | 
|  | zx_handle_close(sb); // expected-note {{Handle released through 1st parameter}} | 
|  | zx_handle_close(sa); | 
|  |  | 
|  | use_handle_struct(hs); // expected-warning {{Using a previously released handle}} | 
|  | // expected-note@-1 {{Using a previously released handle}} | 
|  | } | 
|  |  | 
|  | void checkHandleInStructureLeak() { | 
|  | zx_handle_t sa, sb; | 
|  | zx_channel_create(0, &sa, &sb); // expected-note {{Handle allocated through 3rd parameter}} | 
|  | HandleStruct hs; | 
|  | hs.h = sb; | 
|  | zx_handle_close(sa); // expected-warning {{Potential leak of handle}} | 
|  | // expected-note@-1 {{Potential leak of handle}} | 
|  | } | 
|  |  | 
|  | struct HandlePtrStruct { | 
|  | zx_handle_t *h; | 
|  | }; | 
|  |  | 
|  | void close_handle_struct(HandlePtrStruct hs ZX_HANDLE_RELEASE); | 
|  |  | 
|  | void use_handle_struct(HandlePtrStruct hs ZX_HANDLE_USE); | 
|  |  | 
|  | void checkHandlePtrInStructureUseAfterFree() { | 
|  | zx_handle_t sa, sb; | 
|  | zx_channel_create(0, &sa, &sb); | 
|  | HandlePtrStruct hs; | 
|  | hs.h = &sb; | 
|  | use_handle_struct(hs); | 
|  | close_handle_struct(hs); // expected-note {{Handle released through 1st parameter}} | 
|  | zx_handle_close(sa); | 
|  |  | 
|  | use2(sb); // expected-warning {{Using a previously released handle}} | 
|  | // expected-note@-1 {{Using a previously released handle}} | 
|  | } | 
|  |  | 
|  | void checkHandlePtrInStructureUseAfterFree2() { | 
|  | zx_handle_t sa, sb; | 
|  | zx_channel_create(0, &sa, &sb); | 
|  | HandlePtrStruct hs; | 
|  | hs.h = &sb; | 
|  | use_handle_struct(hs); | 
|  | zx_handle_close(sb); // expected-note {{Handle released through 1st parameter}} | 
|  | zx_handle_close(sa); | 
|  |  | 
|  | use_handle_struct(hs); // expected-warning {{Using a previously released handle}} | 
|  | // expected-note@-1 {{Using a previously released handle}} | 
|  | } | 
|  |  | 
|  | void checkHandlePtrInStructureLeak() { | 
|  | zx_handle_t sa, sb; | 
|  | zx_channel_create(0, &sa, &sb); // expected-note {{Handle allocated through 3rd parameter}} | 
|  | HandlePtrStruct hs; | 
|  | hs.h = &sb; | 
|  | zx_handle_close(sa); // expected-warning {{Potential leak of handle}} | 
|  | // expected-note@-1 {{Potential leak of handle}} | 
|  | } | 
|  |  | 
|  | // Assume this function's declaration that has the release annotation is in one | 
|  | // header file while its implementation is in another file. We have to annotate | 
|  | // the declaration because it might be used outside the TU. | 
|  | // We also want to make sure it is okay to call the function within the same TU. | 
|  | zx_status_t test_release_handle(zx_handle_t handle ZX_HANDLE_RELEASE) { | 
|  | return zx_handle_close(handle); | 
|  | } | 
|  |  | 
|  | void checkReleaseImplementedFunc() { | 
|  | zx_handle_t a, b; | 
|  | zx_channel_create(0, &a, &b); | 
|  | zx_handle_close(a); | 
|  | test_release_handle(b); | 
|  | } | 
|  |  | 
|  | void use_handle(zx_handle_t handle) { | 
|  | // Do nothing. | 
|  | } | 
|  |  | 
|  | void test_call_by_value() { | 
|  | zx_handle_t a, b; | 
|  | zx_channel_create(0, &a, &b); | 
|  | zx_handle_close(a); | 
|  | use_handle(b); | 
|  | zx_handle_close(b); | 
|  | } | 
|  |  | 
|  | void test_call_by_value_leak() { | 
|  | zx_handle_t a, b; | 
|  | zx_channel_create(0, &a, &b); // expected-note {{Handle allocated through 3rd parameter}} | 
|  | zx_handle_close(a); | 
|  | // Here we are passing handle b as integer value to a function that could be | 
|  | // analyzed by the analyzer, thus the handle should not be considered escaped. | 
|  | // After the function 'use_handle', handle b is still tracked and should be | 
|  | // reported leaked. | 
|  | use_handle(b); | 
|  | } // expected-warning {{Potential leak of handle}} | 
|  | // expected-note@-1 {{Potential leak of handle}} | 
|  |  | 
|  | // RAII | 
|  |  | 
|  | template <typename T> | 
|  | struct HandleWrapper { | 
|  | ~HandleWrapper() { close(); } | 
|  | void close() { | 
|  | if (handle != ZX_HANDLE_INVALID) | 
|  | zx_handle_close(handle); | 
|  | } | 
|  | T *get_handle_address() { return &handle; } | 
|  |  | 
|  | private: | 
|  | T handle; | 
|  | }; | 
|  |  | 
|  | void doNotWarnOnRAII() { | 
|  | HandleWrapper<zx_handle_t> w1; | 
|  | zx_handle_t sb; | 
|  | if (zx_channel_create(0, w1.get_handle_address(), &sb)) | 
|  | return; | 
|  | zx_handle_close(sb); | 
|  | } | 
|  |  | 
|  | template <typename T> | 
|  | struct HandleWrapperUnkonwDtor { | 
|  | ~HandleWrapperUnkonwDtor(); | 
|  | void close() { | 
|  | if (handle != ZX_HANDLE_INVALID) | 
|  | zx_handle_close(handle); | 
|  | } | 
|  | T *get_handle_address() { return &handle; } | 
|  |  | 
|  | private: | 
|  | T handle; | 
|  | }; | 
|  |  | 
|  | void doNotWarnOnUnknownDtor() { | 
|  | HandleWrapperUnkonwDtor<zx_handle_t> w1; | 
|  | zx_handle_t sb; | 
|  | if (zx_channel_create(0, w1.get_handle_address(), &sb)) | 
|  | return; | 
|  | zx_handle_close(sb); | 
|  | } | 
|  |  | 
|  | // Various escaping scenarios | 
|  |  | 
|  | zx_handle_t *get_handle_address(); | 
|  |  | 
|  | void escape_store_to_escaped_region01() { | 
|  | zx_handle_t sb; | 
|  | if (zx_channel_create(0, get_handle_address(), &sb)) | 
|  | return; | 
|  | zx_handle_close(sb); | 
|  | } | 
|  |  | 
|  | struct object { | 
|  | zx_handle_t *get_handle_address(); | 
|  | }; | 
|  |  | 
|  | void escape_store_to_escaped_region02(object &o) { | 
|  | zx_handle_t sb; | 
|  | // Same as above. | 
|  | if (zx_channel_create(0, o.get_handle_address(), &sb)) | 
|  | return; | 
|  | zx_handle_close(sb); | 
|  | } | 
|  |  | 
|  | void escape_store_to_escaped_region03(object o) { | 
|  | zx_handle_t sb; | 
|  | // Should we consider the pointee of get_handle_address escaped? | 
|  | // Maybe we only should it consider escaped if o escapes? | 
|  | if (zx_channel_create(0, o.get_handle_address(), &sb)) | 
|  | return; | 
|  | zx_handle_close(sb); | 
|  | } | 
|  |  | 
|  | void escape_through_call(int tag) { | 
|  | zx_handle_t sa, sb; | 
|  | if (zx_channel_create(0, &sa, &sb)) | 
|  | return; | 
|  | escape1(&sa); | 
|  | if (tag) | 
|  | escape2(sb); | 
|  | else | 
|  | escape3(sb); | 
|  | } | 
|  |  | 
|  | struct have_handle { | 
|  | zx_handle_t h; | 
|  | zx_handle_t *hp; | 
|  | }; | 
|  |  | 
|  | void escape_through_store01(have_handle *handle) { | 
|  | zx_handle_t sa; | 
|  | if (zx_channel_create(0, &sa, handle->hp)) | 
|  | return; | 
|  | handle->h = sa; | 
|  | } | 
|  |  | 
|  | have_handle global; | 
|  | void escape_through_store02() { | 
|  | zx_handle_t sa; | 
|  | if (zx_channel_create(0, &sa, global.hp)) | 
|  | return; | 
|  | global.h = sa; | 
|  | } | 
|  |  | 
|  | have_handle escape_through_store03() { | 
|  | zx_handle_t sa, sb; | 
|  | if (zx_channel_create(0, &sa, &sb)) | 
|  | return {0, nullptr}; | 
|  | zx_handle_close(sb); | 
|  | return {sa, nullptr}; | 
|  | } | 
|  |  | 
|  | void escape_structs(have_handle *); | 
|  | void escape_transitively01() { | 
|  | zx_handle_t sa, sb; | 
|  | if (zx_channel_create(0, &sa, &sb)) | 
|  | return; | 
|  | have_handle hs[2]; | 
|  | hs[1] = {sa, &sb}; | 
|  | escape_structs(hs); | 
|  | } | 
|  |  | 
|  | void escape_top_level_pointees(zx_handle_t *h) { | 
|  | zx_handle_t h2; | 
|  | if (zx_channel_create(0, h, &h2)) | 
|  | return; | 
|  | zx_handle_close(h2); | 
|  | } // *h should be escaped here. Right? |