llvm/clang/test/Analysis/fuchsia_handle.cpp

// 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?