// 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 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 w1; zx_handle_t sb; if (zx_channel_create(0, w1.get_handle_address(), &sb)) return; zx_handle_close(sb); } template 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 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?