| // 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"))) |
| #else |
| #define ZX_HANDLE_ACQUIRE |
| #define ZX_HANDLE_RELEASE |
| #define ZX_HANDLE_USE |
| #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 |
| 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 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? |