xazax.hun updated this revision to Diff 230668.
xazax.hun added a comment.

- Fix member operator modeling.
- Added new lines to the end of files.
- Added documentation.
- Minor typo fixes in tests.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D70470/new/

https://reviews.llvm.org/D70470

Files:
  clang/docs/analyzer/checkers.rst
  clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
  clang/lib/Driver/ToolChains/Clang.cpp
  clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
  clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
  clang/test/Analysis/fuchsia_handle.cpp

Index: clang/test/Analysis/fuchsia_handle.cpp
===================================================================
--- /dev/null
+++ clang/test/Analysis/fuchsia_handle.cpp
@@ -0,0 +1,127 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,fuchsia.HandleChecker -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)
+
+#if defined(__clang__)
+#define ZX_HANDLE_ACQUIRE  __attribute__((acquire_handle))
+#define ZX_HANDLE_RELEASE  __attribute__((release_handle))
+#define ZX_HANDLE_USE  __attribute__((use_handle))
+#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_ACQUIRE zx_handle_t* out0,
+    zx_handle_t* out1 ZX_HANDLE_ACQUIRE);
+
+zx_status_t zx_handle_close(
+    zx_handle_t handle ZX_HANDLE_RELEASE);
+
+void escape1(zx_handle_t *in);
+void escape2(zx_handle_t in);
+
+void use1(const zx_handle_t *in ZX_HANDLE_USE);
+void use2(zx_handle_t in ZX_HANDLE_USE);
+
+// To test if argument indexes are OK for operator calls.
+struct MyType {
+  ZX_HANDLE_ACQUIRE
+  zx_handle_t operator+(zx_handle_t replace ZX_HANDLE_RELEASE);
+};
+
+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 checkNoLeak07(int tag) {
+  zx_handle_t sa, sb;
+  if (zx_channel_create(0, &sa, &sb))
+    return;
+  escape1(&sa);
+  escape2(sb);
+}
+
+void checkLeak01(int tag) {
+  zx_handle_t sa, sb;
+  zx_channel_create(0, &sa, &sb);
+  use1(&sa);
+  if (tag)
+    zx_handle_close(sa);
+  use2(sb); // expected-warning {{Potential leak of handle}}
+  zx_handle_close(sb);
+}
+
+void checkDoubleRelease01(int tag) {
+  zx_handle_t sa, sb;
+  zx_channel_create(0, &sa, &sb);
+  if (tag)
+    zx_handle_close(sa);
+  zx_handle_close(sa); // expected-warning {{Releasing a previously released handle}}
+  zx_handle_close(sb);
+}
+
+void checkUseAfterFree01(int tag) {
+  zx_handle_t sa, sb;
+  zx_channel_create(0, &sa, &sb);
+  if (tag) {
+    zx_handle_close(sa);
+    use1(&sa); // expected-warning {{Using a previously released handle}}
+  }
+  zx_handle_close(sb);
+  use2(sb); // expected-warning {{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);
+}
Index: clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
===================================================================
--- /dev/null
+++ clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
@@ -0,0 +1,457 @@
+//=== FuchsiaHandleChecker.cpp - Find handle leaks/double closes -*- C++ -*--=//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker checks if the handle of Fuchsia is properly used according to
+// following rules.
+//   - If a handle is acquired, it should be released before execution
+//        ends.
+//   - If a handle is released, it should not be released again.
+//   - If a handle is released, it should not be used for other purposes
+//        such as I/O.
+//
+// In this checker, each tracked handle is associated with a state. When the
+// handle variable is passed to different function calls or syscalls, its state
+// changes. The state changes can be generally represented by following ASCII
+// Art:
+//
+//    +-----------------------------------------------------------+
+//    |                                                           |
+//    |                        release_func failed                |
+//    |                           +---------+                     |
+//    |                           |         |                     |
+//    |                           |         |                     |
+//    |                         +-+---------v-+                   |
+//    |  acquire_func succeeded |             |    Escape         |
+//    |       +----------------->  Allocated  +-------------------+
+//    |       |                 |             |
+//    |       |                 +-----+------++
+//    |       |                       |      |
+//    |       |         release_func  |      |
+//    |       |          succeeded    |      +--+
+//    |       |                       |         | handle   +--------+
+//    |       |                  +----v-----+   | dies     |        |
+//    |       |       Escape     |          |   +----------> Leaked |
+// +--v-------+--+  +------------+ Released |              |(REPORT)|
+// |             |  |            |          |              +--------+
+// | Not tracked <--+            +----+---+-+
+// |             |  |                 |   |        As argument by value
+// +------+------+  |    release_func |   +------+ in function call
+//        |         |                 |          | or by reference in
+//        |         |                 |          | use_func call
+//        |         |            +----v-----+    |     +-----------+
+//        +---------+            |          |    |     |           |
+//        acquire_func failed    | Double   |    +-----> Use after |
+//                               | released |          | released  |
+//                               | (REPORT) |          | (REPORT)  |
+//                               +----------+          +-----------+
+//
+// acquire_func represents the functions or syscalls that may acquire a handle.
+// release_func represents the functions or syscalls that may release a handle.
+// use_func represents the functions or syscall that requires an open handle.
+//
+// If a tracked handle dies in "Released" or "Not Tracked" state, we assume it
+// is properly used. Otherwise a bug and will be reported.
+//
+// Note that, the analyzer does not always know for sure if a function failed
+// or succeeded. In those cases we use the states MaybeReleased and
+// MaybeAllocated. Thus, the diagramm above captures the intent, not
+// implementation details.
+//
+// Due to the fact that the number of handle related syscalls in Fuchsia
+// is large, we adopt the annotation attributes to descript syscalls'
+// operations(acquire/release/use) on handles instead of hardcoding
+// everything in the checker.
+//
+// We use following annotation attributes for handle related syscalls or
+// functions:
+//  1. __attribute__((acquire_handle)) |handle will be acquired
+//  2. __attribute__((release_handle)) |handle will be released
+//  3. __attribute__((use_handle)) |handle will not transit to
+//     escaped state, it also needs to be open.
+//
+// For example, an annotated syscall:
+//   zx_status_t zx_channel_create(
+//   uint32_t options,
+//   zx_handle_t* out0 __attribute__((acquire_handle)) ,
+//   zx_handle_t* out1 __attribute__((acquire_handle)));
+// denotes a syscall which will acquire two handles and save them to 'out0' and
+// 'out1' when succeeded.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/Type.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+static const StringRef HandleTypeName = "zx_handle_t";
+static const StringRef ErrorTypeName = "zx_status_t";
+
+class HandleState {
+private:
+  enum class Kind { MaybeAllocated, Allocated, MaybeReleased, Released } K;
+  SymbolRef ErrorSym;
+  HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {}
+
+public:
+  bool operator==(const HandleState &Other) const {
+    return K == Other.K && ErrorSym == Other.ErrorSym;
+  }
+  bool isAllocated() const { return K == Kind::Allocated; }
+  bool maybeAllocated() const { return K == Kind::MaybeAllocated; }
+  bool isReleased() const { return K == Kind::Released; }
+  bool maybeReleased() const { return K == Kind::MaybeReleased; }
+
+  static HandleState getMaybeAllocated(SymbolRef ErrorSym) {
+    return HandleState(Kind::MaybeAllocated, ErrorSym);
+  }
+  static HandleState getAllocated(ProgramStateRef State, HandleState S) {
+    assert(S.maybeAllocated() || S.maybeReleased());
+    assert(State->getConstraintManager()
+               .isNull(State, S.getErrorSym())
+               .isConstrained());
+    return HandleState(Kind::Allocated, nullptr);
+  }
+  static HandleState getMaybeReleased(SymbolRef ErrorSym) {
+    return HandleState(Kind::MaybeReleased, ErrorSym);
+  }
+  static HandleState getReleased(ProgramStateRef State, HandleState S) {
+    assert(S.maybeReleased());
+    assert(State->getConstraintManager()
+               .isNull(State, S.getErrorSym())
+               .isConstrained());
+    return HandleState(Kind::Released, nullptr);
+  }
+
+  SymbolRef getErrorSym() const { return ErrorSym; }
+
+  void Profile(llvm::FoldingSetNodeID &ID) const {
+    ID.AddInteger(static_cast<int>(K));
+    ID.AddPointer(ErrorSym);
+  }
+
+  LLVM_DUMP_METHOD void dump(raw_ostream &OS) const {
+    switch (K) {
+#define CASE(ID)                                                               \
+  case ID:                                                                     \
+    OS << #ID;                                                                 \
+    break;
+      CASE(Kind::MaybeAllocated)
+      CASE(Kind::Allocated)
+      CASE(Kind::MaybeReleased)
+      CASE(Kind::Released)
+    }
+  }
+
+  LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); }
+};
+
+class FuchsiaHandleChecker
+    : public Checker<check::PostCall, check::PreCall, check::DeadSymbols,
+                     check::PointerEscape, eval::Assume> {
+  BugType LeakBugType{this, "Fuchsia handle leak", "Fuchsia Handle Error",
+                      /*SuppressOnSink=*/true};
+  BugType DoubleReleaseBugType{this, "Fuchsia handle double release",
+                               "Fuchsia Handle Error"};
+  BugType UseAfterFreeBugType{this, "Fuchsia handle use after release",
+                              "Fuchsia Handle Error"};
+
+public:
+  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+  ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
+                             bool Assumption) const;
+  ProgramStateRef checkPointerEscape(ProgramStateRef State,
+                                     const InvalidatedSymbols &Escaped,
+                                     const CallEvent *Call,
+                                     PointerEscapeKind Kind) const;
+
+  ExplodedNode *reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
+                            CheckerContext &C, ExplodedNode *Pred) const;
+
+  void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range,
+                           CheckerContext &C) const;
+
+  void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range,
+                          CheckerContext &C) const;
+
+  void reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C,
+                 const SourceRange *Range, const BugType &Type,
+                 StringRef Msg) const;
+
+  void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
+                  const char *Sep) const override;
+};
+} // end anonymous namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState)
+
+/// Returns the symbols extracted from the argument or null if it cannot be
+/// found.
+SymbolRef getFuchsiaHandleSymbol(QualType QT, SVal Arg, ProgramStateRef State) {
+  int PtrToHandleLevel = 0;
+  while (QT->isAnyPointerType() || QT->isReferenceType()) {
+    ++PtrToHandleLevel;
+    QT = QT->getPointeeType();
+  }
+  if (const auto *HandleType = QT->getAs<TypedefType>()) {
+    if (HandleType->getDecl()->getName() != HandleTypeName)
+      return nullptr;
+    if (PtrToHandleLevel > 1) {
+      // Not supported yet.
+      return nullptr;
+    }
+
+    if (PtrToHandleLevel == 0) {
+      return Arg.getAsSymbol();
+    } else {
+      assert(PtrToHandleLevel == 1);
+      if (Optional<Loc> ArgLoc = Arg.getAs<Loc>())
+        return State->getSVal(*ArgLoc).getAsSymbol();
+    }
+  }
+  return nullptr;
+}
+
+void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call,
+                                        CheckerContext &C) const {
+  const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+  if (!FuncDecl)
+    return;
+
+  ProgramStateRef State = C.getState();
+
+  for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
+    const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
+    SymbolRef HandleSym =
+        getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State);
+    if (!HandleSym)
+      continue;
+
+    // Handled in checkPostCall.
+    if (PVD->hasAttr<ReleaseHandleAttr>() || PVD->hasAttr<AcquireHandleAttr>())
+      continue;
+
+    const HandleState *HState = State->get<HStateMap>(HandleSym);
+    if (!HState)
+        continue;
+
+    if (PVD->hasAttr<UseHandleAttr>() || PVD->getType()->isIntegerType()) {
+      if (HState->isReleased() || HState->maybeReleased()) {
+        reportUseAfterFree(HandleSym, Call.getArgSourceRange(Arg), C);
+        return;
+      }
+    }
+    if (!PVD->hasAttr<UseHandleAttr>() &&
+               PVD->getType()->isIntegerType()) {
+      // Working around integer by-value escapes.
+      State = State->remove<HStateMap>(HandleSym);
+    }
+  }
+  C.addTransition(State);
+}
+
+void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
+                                         CheckerContext &C) const {
+  const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+  if (!FuncDecl)
+    return;
+
+  ProgramStateRef State = C.getState();
+
+  SymbolRef ResultSymbol = nullptr;
+  if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>())
+    if (TypeDefTy->getDecl()->getName() == ErrorTypeName)
+      ResultSymbol = Call.getReturnValue().getAsSymbol();
+
+  // Function returns an open handle.
+  if (FuncDecl->hasAttr<AcquireHandleAttr>()) {
+    SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
+    State =
+        State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr));
+  }
+
+  for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
+    const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
+    SymbolRef HandleSym =
+        getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State);
+    if (!HandleSym)
+      continue;
+
+    const HandleState *HState = State->get<HStateMap>(HandleSym);
+    if (PVD->hasAttr<ReleaseHandleAttr>()) {
+      if (HState && (HState->isReleased() || HState->maybeReleased())) {
+        reportDoubleRelease(HandleSym, Call.getArgSourceRange(Arg), C);
+        return;
+      } else
+        State = State->set<HStateMap>(
+            HandleSym, HandleState::getMaybeReleased(ResultSymbol));
+    } else if (PVD->hasAttr<AcquireHandleAttr>()) {
+      State = State->set<HStateMap>(
+          HandleSym, HandleState::getMaybeAllocated(ResultSymbol));
+    }
+  }
+  C.addTransition(State);
+}
+
+void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper,
+                                            CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+  SmallVector<SymbolRef, 2> LeakedSyms;
+  HStateMapTy TrackedHandles = State->get<HStateMap>();
+  for (auto &CurItem : TrackedHandles) {
+    if (!SymReaper.isDead(CurItem.first))
+      continue;
+    if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated())
+      LeakedSyms.push_back(CurItem.first);
+    State = State->remove<HStateMap>(CurItem.first);
+  }
+
+  ExplodedNode *N = C.getPredecessor();
+  if (!LeakedSyms.empty())
+    N = reportLeaks(LeakedSyms, C, N);
+
+  C.addTransition(State, N);
+}
+
+ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State,
+                                                 SVal Cond,
+                                                 bool Assumption) const {
+  ConstraintManager &Cmr = State->getConstraintManager();
+  HStateMapTy TrackedHandles = State->get<HStateMap>();
+  for (auto &CurItem : TrackedHandles) {
+    SymbolRef ErrorSym = CurItem.second.getErrorSym();
+    if (!ErrorSym)
+      continue;
+    ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym);
+    if (ErrorVal.isConstrainedTrue()) {
+      // Allocation/release succeeded.
+      if (CurItem.second.maybeReleased())
+        State = State->set<HStateMap>(
+            CurItem.first, HandleState::getReleased(State, CurItem.second));
+      else if (CurItem.second.maybeAllocated())
+        State = State->set<HStateMap>(
+            CurItem.first, HandleState::getAllocated(State, CurItem.second));
+    } else if (ErrorVal.isConstrainedFalse()) {
+      // Allocation/release failed.
+      if (CurItem.second.maybeReleased())
+        State = State->set<HStateMap>(
+            CurItem.first, HandleState::getAllocated(State, CurItem.second));
+      else if (CurItem.second.maybeAllocated())
+        State = State->remove<HStateMap>(CurItem.first);
+    }
+  }
+  return State;
+}
+
+ProgramStateRef FuchsiaHandleChecker::checkPointerEscape(
+    ProgramStateRef State, const InvalidatedSymbols &Escaped,
+    const CallEvent *Call, PointerEscapeKind Kind) const {
+  const FunctionDecl *FuncDecl =
+      Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl()) : nullptr;
+  for (const SymbolRef &EscapedSymbol : Escaped) {
+    if (State->contains<HStateMap>(EscapedSymbol)) {
+      bool IsUnescapedUse = false;
+      // Not all calls should escape our symbols.
+      if (FuncDecl && (Kind == PSK_DirectEscapeOnCall ||
+                       Kind == PSK_IndirectEscapeOnCall)) {
+        for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) {
+          const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
+          SymbolRef HandleSym = getFuchsiaHandleSymbol(
+              PVD->getType(), Call->getArgSVal(Arg), State);
+          if (!HandleSym)
+            continue;
+          if (HandleSym == EscapedSymbol && PVD->hasAttr<UseHandleAttr>())
+            IsUnescapedUse = true;
+        }
+      }
+      if (!IsUnescapedUse)
+        State = State->remove<HStateMap>(EscapedSymbol);
+    }
+  }
+  return State;
+}
+
+ExplodedNode *
+FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
+                                  CheckerContext &C, ExplodedNode *Pred) const {
+  ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred);
+  for (SymbolRef LeakedHandle : LeakedHandles) {
+    reportBug(LeakedHandle, ErrNode, C, nullptr, LeakBugType,
+              "Potential leak of handle");
+  }
+  return ErrNode;
+}
+
+void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym,
+                                               const SourceRange &Range,
+                                               CheckerContext &C) const {
+  ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
+  reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType,
+            "Releasing a previously released handle");
+}
+
+void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym,
+                                              const SourceRange &Range,
+                                              CheckerContext &C) const {
+  ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
+  reportBug(HandleSym, ErrNode, C, &Range, UseAfterFreeBugType,
+            "Using a previously released handle");
+}
+
+void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode,
+                                     CheckerContext &C,
+                                     const SourceRange *Range,
+                                     const BugType &Type, StringRef Msg) const {
+  if (!ErrorNode)
+    return;
+
+  auto R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode);
+  if (Range)
+    R->addRange(*Range);
+  R->markInteresting(Sym);
+  C.emitReport(std::move(R));
+}
+
+void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {
+  mgr.registerChecker<FuchsiaHandleChecker>();
+}
+
+bool ento::shouldRegisterFuchsiaHandleChecker(const LangOptions &LO) {
+  return true;
+}
+
+void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State,
+                                      const char *NL, const char *Sep) const {
+
+  HStateMapTy StateMap = State->get<HStateMap>();
+
+  if (!StateMap.isEmpty()) {
+    Out << Sep << "FuchsiaHandleChecker :" << NL;
+    for (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E;
+         ++I) {
+      I.getKey()->dumpToStream(Out);
+      Out << " : ";
+      I.getData().dump(Out);
+      Out << NL;
+    }
+  }
+}
Index: clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -37,6 +37,7 @@
   EnumCastOutOfRangeChecker.cpp
   ExprInspectionChecker.cpp
   FixedAddressChecker.cpp
+  FuchsiaHandleChecker.cpp
   GCDAntipatternChecker.cpp
   GenericTaintChecker.cpp
   GTestChecker.cpp
Index: clang/lib/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ clang/lib/Driver/ToolChains/Clang.cpp
@@ -2664,6 +2664,8 @@
 
     if (Triple.isOSDarwin())
       CmdArgs.push_back("-analyzer-checker=osx");
+    else if (Triple.isOSFuchsia())
+      CmdArgs.push_back("-analyzer-checker=fuchsia");
 
     CmdArgs.push_back("-analyzer-checker=deadcode");
 
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
@@ -213,6 +213,22 @@
     return addTransition(State, (Tag ? Tag : Location.getTag()));
   }
 
+  /// Generate a transition to a node that will be used to report
+  /// an error. This node will not be a sink. That is, exploration will
+  /// continue along this path.
+  ///
+  /// @param State The state of the generated node.
+  /// @param Pred The transition will be generated from the specified Pred node
+  ///             to the newly generated node.
+  /// @param Tag The tag to uniquely identify the creation site. If null,
+  ///        the default tag for the checker will be used.
+  ExplodedNode *
+  generateNonFatalErrorNode(ProgramStateRef State,
+                            ExplodedNode *Pred,
+                            const ProgramPointTag *Tag = nullptr) {
+    return addTransition(State, Pred, (Tag ? Tag : Location.getTag()));
+  }
+
   /// Emit the diagnostics report.
   void emitReport(std::unique_ptr<BugReport> R) {
     Changed = true;
Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
===================================================================
--- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -108,6 +108,8 @@
 
 def NonDeterminismAlpha : Package<"nondeterminism">, ParentPackage<Alpha>;
 
+def Fuchsia : Package<"fuchsia">;
+
 //===----------------------------------------------------------------------===//
 // Core Checkers.
 //===----------------------------------------------------------------------===//
@@ -1412,3 +1414,16 @@
   Documentation<HasDocumentation>;
 
 } // end alpha.nondeterminism
+
+//===----------------------------------------------------------------------===//
+// Fuchsia checkers.
+//===----------------------------------------------------------------------===//
+
+let ParentPackage = Fuchsia in {
+
+def FuchsiaHandleChecker : Checker<"HandleChecker">,
+  HelpText<"A Checker that detect leaks related to Fuchsia handles">,
+  Documentation<HasDocumentation>;
+
+} // end fuchsia
+
Index: clang/docs/analyzer/checkers.rst
===================================================================
--- clang/docs/analyzer/checkers.rst
+++ clang/docs/analyzer/checkers.rst
@@ -1335,6 +1335,29 @@
                                 &kCFTypeArrayCallBacks); // warn
  }
 
+Fuchsia
+^^^^^^^
+
+Fuchsia Checkers.
+
+.. _fuchsia-HandleChecker:
+
+fuchsia.HandleChecker
+""""""""""""""""""""""""""""
+Handles identify resources. Similar to pointers they can be leaked,
+double freed, or use after freed. This check attempts to find such problems.
+
+.. code-block:: cpp
+
+ void checkLeak08(int tag) {
+   zx_handle_t sa, sb;
+   zx_channel_create(0, &sa, &sb);
+   if (tag)
+     zx_handle_close(sa);
+   use(sb); // Warn: Potential leak of handle
+   zx_handle_close(sb);
+ }
+
 
 .. _alpha-checkers:
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to