haowei updated this revision to Diff 109237.
haowei added a comment.

Add 1 line fix for the constraint on allocated handle in function 
allocateSingleHandle at line 647


https://reviews.llvm.org/D36022

Files:
  lib/StaticAnalyzer/Checkers/MagentaHandleChecker.cpp
  test/Analysis/mxhandle.c

Index: test/Analysis/mxhandle.c
===================================================================
--- /dev/null
+++ test/Analysis/mxhandle.c
@@ -0,0 +1,192 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.magenta.MagentaHandleChecker -analyzer-store=region -verify %s
+
+typedef __typeof__(sizeof(int)) size_t;
+typedef int mx_status_t;
+typedef __typeof__(sizeof(int)) mx_handle_t;
+typedef unsigned int uint32_t;
+#define NULL ((void *)0)
+
+#if defined(__clang__)
+#define MX_SYSCALL_PARAM_ATTR(x)   __attribute__((annotate("mx_" #x)))
+#else
+#define MX_SYSCALL_PARAM_ATTR(x)   // no-op
+#endif
+
+extern mx_status_t mx_channel_create(
+uint32_t options,
+    MX_SYSCALL_PARAM_ATTR(handle_acquire) mx_handle_t* out0,
+    MX_SYSCALL_PARAM_ATTR(handle_acquire) mx_handle_t* out1);
+
+extern mx_status_t mx_handle_close(
+MX_SYSCALL_PARAM_ATTR(handle_release_always) mx_handle_t handle);
+
+// Escape is the default behavior for any function take a handle as parameter
+// It should work with/without explicit annotations
+void escapeMethod01(mx_handle_t *in);
+
+void escapeMethod02(
+MX_SYSCALL_PARAM_ATTR(handle_escape)  mx_handle_t *in);
+
+void escapeMethod03(mx_handle_t in);
+
+void escapeMethod04(
+MX_SYSCALL_PARAM_ATTR(handle_escape)  mx_handle_t in);
+
+// Handle should not escape when mx_handle_use is present. It should work
+// for both value type and pointer type arguments.
+void useHandle01(
+MX_SYSCALL_PARAM_ATTR(handle_use) mx_handle_t handle);
+
+void useHandle02(
+MX_SYSCALL_PARAM_ATTR(handle_use) mx_handle_t *handle);
+
+// End of declaration
+
+void checkNoLeak01() {
+  mx_handle_t sa, sb;
+  mx_channel_create(0, &sa, &sb);
+  mx_handle_close(sa);
+  mx_handle_close(sb);
+}
+
+void checkNoLeak02() {
+  mx_handle_t ay[2];
+  mx_channel_create(0, &ay[0], &ay[1]);
+  mx_handle_close(ay[0]);
+  mx_handle_close(ay[1]);
+}
+
+void checkNoLeak03() {
+  mx_handle_t ay[2];
+  mx_channel_create(0, &ay[0], &ay[1]);
+  for (int i = 0; i < 2; i++) {
+    mx_handle_close(ay[i]);
+  }
+}
+
+mx_handle_t checkNoLeak04() {
+  mx_handle_t sa, sb;
+  mx_channel_create(0, &sa, &sb);
+  mx_handle_close(sa);
+  return sb; // no warning
+}
+
+mx_handle_t checkNoLeak05(mx_handle_t *out1) {
+  mx_handle_t sa, sb;
+  mx_channel_create(0, &sa, &sb);
+  *out1 = sa;
+  return sb; // no warning
+}
+
+void checkNoLeak10() {
+  mx_handle_t sa, sb;
+  if (mx_channel_create(0, &sa, &sb) < 0) {
+    return;
+  }
+  mx_handle_close(sa);
+  mx_handle_close(sb);
+}
+
+void checkNoLeak12(int tag) {
+  mx_handle_t sa, sb;
+  if (mx_channel_create(0, &sa, &sb) < 0) {
+    return;
+  }
+  if (tag) {
+    escapeMethod01(&sa);
+    escapeMethod01(&sb);
+  }
+  mx_handle_close(sa);
+  mx_handle_close(sb);
+}
+
+void checkNoLeak13(int tag) {
+  mx_handle_t sa, sb;
+  if (mx_channel_create(0, &sa, &sb) < 0) {
+    return;
+  }
+  if (tag) {
+    escapeMethod02(&sa);
+    escapeMethod02(&sb);
+  }
+  mx_handle_close(sa);
+  mx_handle_close(sb);
+}
+
+void checkNoLeak14(int tag) {
+  mx_handle_t sa, sb;
+  if (mx_channel_create(0, &sa, &sb) < 0) {
+    return;
+  }
+  if (tag) {
+    escapeMethod03(sa);
+    escapeMethod03(sb);
+  }
+  mx_handle_close(sa);
+  mx_handle_close(sb);
+}
+
+void checkNoLeak15(int tag) {
+  mx_handle_t sa, sb;
+  if (mx_channel_create(0, &sa, &sb) < 0) {
+    return;
+  }
+  if (tag) {
+    escapeMethod04(sa);
+    escapeMethod04(sb);
+  }
+  mx_handle_close(sa);
+  mx_handle_close(sb);
+}
+
+void checkLeak01() {
+  mx_handle_t sa, sb;
+  mx_channel_create(0, &sa, &sb);
+} // expected-warning {{Potential leak of handle}}
+
+
+void checkLeak02(int tag) {
+  mx_handle_t sa, sb;
+  mx_channel_create(0, &sa, &sb);
+  if (tag) {
+    mx_handle_close(sa);
+  }
+  mx_handle_close(sb); // expected-warning {{Potential leak of handle}}
+}
+
+void checkLeak07() {
+  mx_handle_t sa, sb;
+  mx_channel_create(0, &sa, &sb);
+
+  useHandle01(sa);
+  mx_handle_close(sb); // expected-warning {{Potential leak of handle}}
+}
+
+void checkLeak08() {
+  mx_handle_t sa, sb;
+  mx_channel_create(0, &sa, &sb);
+
+  useHandle02(&sa);
+  mx_handle_close(sb); // expected-warning {{Potential leak of handle}}
+}
+
+void checkDoubleRelease01(int tag) {
+  mx_handle_t sa, sb;
+  mx_channel_create(0, &sa, &sb);
+  if (tag) {
+    mx_handle_close(sa);
+  }
+  mx_handle_close(sa); // expected-warning {{Releasing a previously released handle}}
+  mx_handle_close(sb);
+}
+
+void checkUseAfterFree01(int tag) {
+  mx_handle_t sa, sb;
+  mx_channel_create(0, &sa, &sb);
+  if (tag) {
+    mx_handle_close(sa);
+  }
+  useHandle01(sa); // expected-warning {{Using a previously released handle}}
+  mx_handle_close(sa);
+  mx_handle_close(sb);
+}
Index: lib/StaticAnalyzer/Checkers/MagentaHandleChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/MagentaHandleChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MagentaHandleChecker.cpp
@@ -119,7 +119,7 @@
   // Functions that do not have any annotation at all
   UNANNOTATED_FUNC,
   // Functions have annotations, but cannot be processed correctly due to
-  // mismatch to the arguments.
+  // mismatches to the arguments.
   UNPROCESSED_FUNC,
   // When a bug is found in function with this flag, do not report this bug
   // Used to suppress known false positives.
@@ -192,11 +192,55 @@
   }
 };
 
+class HandleState {
+private:
+  enum Kind {
+    // Handle is allocated
+    Allocated,
+    // Handle is released
+    Released,
+    // Handle is no longer trackable
+    Escaped
+  } K;
+  // To record allocation site information.
+  const MemRegion *Region;
+  HandleState(Kind k, const MemRegion *region) : K(k), Region(region) {}
+
+public:
+  bool operator==(const HandleState &X) const { return K == X.K; }
+  bool isAllocated() const { return K == Allocated; }
+  bool isReleased() const { return K == Released; }
+  bool isEscaped() const { return K == Escaped; }
+  const MemRegion *getRegion() const { return Region; }
+
+  static HandleState getAllocated(const MemRegion *region) {
+    return HandleState(Allocated, region);
+  }
+
+  static HandleState getReleased(const HandleState *HS) {
+    return HandleState(Released, HS->getRegion());
+  }
+
+  static HandleState getEscaped(const HandleState *HS) {
+    return HandleState(Escaped, HS->getRegion());
+  }
+
+  void Profile(llvm::FoldingSetNodeID &ID) const {
+    ID.AddInteger(K);
+    ID.AddPointer(Region);
+  }
+};
+
 static const char *ANNOTATION_PREFIX = "mx_";
 static const char *HANDLE_TYPE_NAME = "mx_handle_t";
 static const char *SYSCALL_RETURN_TYPE_NAME = "mx_status_t";
 
-class MagentaHandleChecker : public Checker<eval::Call> {
+class MagentaHandleChecker
+    : public Checker<check::DeadSymbols, check::PointerEscape, eval::Call,
+                     check::PostCall> {
+  std::unique_ptr<BugType> LeakBugType;
+  std::unique_ptr<BugType> DoubleReleaseBugType;
+  std::unique_ptr<BugType> UseAfterFreeBugType;
 
   mutable llvm::DenseMap<const FunctionDecl *, FuncSpec> FuncDeclMap;
   mutable llvm::StringMap<FuncSpec> SpecialFuncDeclMap;
@@ -206,20 +250,83 @@
 public:
   MagentaHandleChecker();
   // Checker callbacks
+  void checkPostCall(const CallEvent &Call, CheckerContext &Ctx) const;
+  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &Ctx) const;
   bool evalCall(const CallExpr *CE, CheckerContext &Ctx) const;
+  ProgramStateRef checkPointerEscape(ProgramStateRef State,
+                                     const InvalidatedSymbols &Escaped,
+                                     const CallEvent *Call,
+                                     PointerEscapeKind Kind) const;
 
 private:
   // Evaluate handle related function call with annotation information
   bool evalCallWithFuncSpec(const CallExpr *CE, const FunctionDecl *FD,
                             CheckerContext &Ctx, FuncSpec &FS) const;
+  // Process handle escapes that are not processed by checkPointerEscape()
+  void processUninlinedCalls(ProgramStateRef State, const CallEvent &Call,
+                             CheckerContext &Ctx) const;
+
+  // Bug reporting functions
+  void reportLeaks(ArrayRef<SymbolRef> LeakedHandles, CheckerContext &Ctx,
+                   ExplodedNode *ErrorNode) const;
+
+  void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range,
+                           CheckerContext &Ctx) const;
+
+  void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range,
+                          CheckerContext &Ctx) const;
+
+  void reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C,
+                 const SourceRange *Range, const std::unique_ptr<BugType> &Type,
+                 StringRef Msg) const;
+  // Utility functions
   // Generate function spec info based on inline annotations in declaration
   bool generateSpecForFuncionDecl(const FunctionDecl *FuncDecl) const;
   // Extract magenta related annotation string from declarations
   StringRef extractAnnotationWithoutPrefix(const Decl *D) const;
+
+  ProgramStateRef processHandleArrayArgWithFuncSpec(const CallExpr *CE,
+                                                    ProgramStateRef State,
+                                                    CheckerContext &Ctx,
+                                                    ArgSpec &CurArgSpec) const;
+
+  ProgramStateRef conjureFailedState(const CallExpr *CE, ProgramStateRef State,
+                                     CheckerContext &Ctx) const;
+
+  ProgramStateRef allocateSingleHandle(const CallExpr *CE, const Expr *ArgExpr,
+                                       ProgramStateRef State,
+                                       CheckerContext &Ctx) const;
+
+  // Return true only if it is certain that Sym will be Zero
+  static bool CheckSymbolConstraintToZero(SymbolRef Sym, ProgramStateRef State);
+
+  bool isLeaked(SymbolRef SymHandle, const HandleState &CurHandleState,
+                bool isSymDead, ProgramStateRef State) const;
+
+  // This is how CSA determines if a pointer is escaped in assignment
+  // statement. Copied from ExprEngine::processPointerEscapedOnBind
+  bool isArgumentEscaped(SVal Loc) const {
+    if (Optional<loc::MemRegionVal> regionLoc =
+            Loc.getAs<loc::MemRegionVal>()) {
+      if (!regionLoc->getRegion()->hasStackStorage())
+        return true;
+    }
+    return false;
+  }
 };
 } // end anonymous namespace
 
+// Handle -> HandleState map
+REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState)
+
 MagentaHandleChecker::MagentaHandleChecker() {
+  // Initialize the bug types.
+  LeakBugType.reset(
+      new BugType(this, "MX handle leak", "Magenta Handle Error"));
+  DoubleReleaseBugType.reset(
+      new BugType(this, "MX handle double release", "Magenta Handle Error"));
+  UseAfterFreeBugType.reset(
+      new BugType(this, "MX handle use after free", "Magenta Handle Error"));
 
   // Initialize the map for supported annotations.
   AnnotationMap = {{"handle_acquire", ALLOCATE},
@@ -283,17 +390,302 @@
   return false;
 }
 
+void MagentaHandleChecker::checkPostCall(const CallEvent &Call,
+                                         CheckerContext &Ctx) const {
+  ProgramStateRef State = Ctx.getState();
+  const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+  if (!FD || !FuncKindMap.count(FD) || (FuncKindMap[FD] != ANNOATATED_FUNC)) {
+    processUninlinedCalls(State, Call, Ctx);
+  }
+}
+
+ProgramStateRef MagentaHandleChecker::checkPointerEscape(
+    ProgramStateRef State, const InvalidatedSymbols &Escaped,
+    const CallEvent *Call, PointerEscapeKind Kind) const {
+  for (const SymbolRef &EscapedSymbol : Escaped) {
+    const HandleState *CurHS = State->get<HStateMap>(EscapedSymbol);
+    if (CurHS) {
+      State = State->remove<HStateMap>(EscapedSymbol);
+      State =
+          State->set<HStateMap>(EscapedSymbol, HandleState::getEscaped(CurHS));
+    }
+  }
+  return State;
+}
+
+void MagentaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper,
+                                            CheckerContext &Ctx) const {
+  ProgramStateRef State = Ctx.getState();
+  SmallVector<SymbolRef, 2> LeakVector;
+  HStateMapTy TrackedHandle = State->get<HStateMap>();
+  for (auto &CurItem : TrackedHandle) {
+    SymbolRef Sym = CurItem.first;
+    // Workaround for zombie symbol issue.
+    bool IsSymDead = SymReaper.maybeDead(Sym);
+    const HandleState &CurHandleState = CurItem.second;
+    if (isLeaked(Sym, CurHandleState, IsSymDead, State))
+      LeakVector.push_back(Sym);
+    if (IsSymDead)
+      State = State->remove<HStateMap>(Sym);
+  }
+  if (!LeakVector.empty()) {
+    ExplodedNode *N = Ctx.generateNonFatalErrorNode(State);
+    if (!N)
+      return;
+    reportLeaks(LeakVector, Ctx, N);
+  }
+  Ctx.addTransition(State);
+}
+
 //  Evaluate functions with annotation attributes.
 //  If process failed, fallback to conservativeEvalCall by returning false.
 bool MagentaHandleChecker::evalCallWithFuncSpec(const CallExpr *CE,
                                                 const FunctionDecl *FD,
                                                 CheckerContext &Ctx,
                                                 FuncSpec &FS) const {
-  // Here is just a place holder. The real handle analysis code will be
-  // included in follow up commits
+  if (FS.RetAction == DONOTREPORT) {
+    FuncKindMap[FD] = DONOTREPORT_FUNC;
+    return false;
+  }
+  SmallVector<SVal, 3> invalidateSVal;
+  ProgramStateRef State = Ctx.getState();
+  ASTContext &ASTCtx = State->getStateManager().getContext();
+  const LocationContext *LCtx = Ctx.getLocationContext();
+
+  // Check the return type
+  bool IsRetTypeInt = false;
+  QualType RetType = CE->getCallReturnType(ASTCtx);
+  if (RetType->isIntegralType(ASTCtx))
+    IsRetTypeInt = true;
+
+  // Conjure a failed state if this function has this flag and return type is
+  // int. When failed, the return value is less than 0
+  ProgramStateRef FailedState = nullptr;
+  if (IsRetTypeInt && FS.RetAction == BIFURCATE)
+    FailedState = conjureFailedState(CE, State, Ctx);
+
+  // Invalidate the pointers arguments that are not annotated
+  llvm::SmallSet<int, 4> PreserveArgs;
+  for (ArgSpec &CurArgSpec : FS.ArgSpecVec) {
+    if (CurArgSpec.getIndex() != -1)
+      PreserveArgs.insert(CurArgSpec.getIndex());
+    if (CurArgSpec.isArray()) {
+      if (CurArgSpec.getInputCArgIdx() != -1)
+        PreserveArgs.insert(CurArgSpec.getInputCArgIdx());
+      if (CurArgSpec.getOutputCArgIdx() != -1)
+        PreserveArgs.insert(CurArgSpec.getOutputCArgIdx());
+    }
+  }
+  for (int Idx = 0, Count = CE->getNumArgs(); Idx < Count; ++Idx) {
+    if (!PreserveArgs.count(Idx)) {
+      const Expr *CurHandleExpr = CE->getArg(Idx);
+      if (!CurHandleExpr) {
+        FuncKindMap[FD] = UNPROCESSED_FUNC;
+        return false;
+      }
+      SVal CurArgSVal = State->getSVal(CurHandleExpr, LCtx);
+      invalidateSVal.push_back(CurArgSVal);
+    }
+  }
+  if (invalidateSVal.size())
+    State = State->invalidateRegions(invalidateSVal, CE, Ctx.blockCount(), LCtx,
+                                     true, nullptr, nullptr, nullptr);
+
+  // Return 0 in successful state
+  if (IsRetTypeInt && FS.RetAction == BIFURCATE) {
+    SVal StatusSVal = Ctx.getSValBuilder().makeIntVal(llvm::APSInt::get(0));
+    State = State->BindExpr(CE, LCtx, StatusSVal);
+  } else {
+    SVal StatusSVal = Ctx.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx,
+                                                            Ctx.blockCount());
+    State = State->BindExpr(CE, LCtx, StatusSVal);
+  }
+
+  for (ArgSpec &CurArgSpec : FS.ArgSpecVec) {
+    const Expr *CurHandleExpr = CE->getArg(CurArgSpec.getIndex());
+    if (CurArgSpec.isArray()) {
+      // Special treatment for array of handles
+      State = processHandleArrayArgWithFuncSpec(CE, State, Ctx, CurArgSpec);
+      if (!State)
+        return false;
+      continue;
+    }
+    // Not an array of handles
+    if (CurArgSpec.isAllocation()) {
+      // Single handle acquire
+      State = allocateSingleHandle(CE, CurHandleExpr, State, Ctx);
+      if (!State)
+        return false;
+    } else if (CurArgSpec.isEscape() || CurArgSpec.isRelease() ||
+               CurArgSpec.isNoEscape()) {
+      SVal SValHandle;
+      if (CurArgSpec.isPointer()) {
+        SVal ArgSVal = State->getSVal(CurHandleExpr, LCtx);
+        const MemRegion *MemHandle = ArgSVal.getAsRegion();
+        if (!MemHandle)
+          return false;
+        SValHandle = State->getSVal(MemHandle);
+      } else {
+        SValHandle = State->getSVal(CurHandleExpr, LCtx);
+      }
+      SymbolRef SymHandle = SValHandle.getAsSymbol();
+      if (!SymHandle)
+        return false;
+      const HandleState *CurState = State->get<HStateMap>(SymHandle);
+      if (!CurState)
+        continue;
+      // Check use after release
+      if ((CurArgSpec.isEscape() || CurArgSpec.isNoEscape()) &&
+          CurState->isReleased())
+        reportUseAfterFree(SymHandle, CE->getSourceRange(), Ctx);
+
+      if ((CurArgSpec.isEscape())) {
+        // Handle escape through value
+        State =
+            State->set<HStateMap>(SymHandle, HandleState::getEscaped(CurState));
+      } else if (CurArgSpec.isRelease()) {
+        if (CurState->isReleased())
+          reportDoubleRelease(SymHandle, CE->getSourceRange(), Ctx);
+        State = State->set<HStateMap>(SymHandle,
+                                      HandleState::getReleased(CurState));
+      }
+    }
+  }
+
+  if (FailedState)
+    Ctx.addTransition(FailedState);
+  Ctx.addTransition(State);
   FuncKindMap[FD] = ANNOATATED_FUNC;
+  return true;
+}
+
+ProgramStateRef MagentaHandleChecker::processHandleArrayArgWithFuncSpec(
+    const CallExpr *CE, ProgramStateRef State, CheckerContext &Ctx,
+    ArgSpec &CurArgSpec) const {
+  // A place holder. This function is used to process handle acquire/release
+  // via an array. Its code will be included in follow up commits
+  return nullptr;
+}
+
+// Process the handle escaped situation.
+// When a function is not inlined for any reasons, if one of its
+// arguments is an acquired handle, treat it as an escaped handle.
+// It should be processed by checkPointerEscape in the future.
+void MagentaHandleChecker::processUninlinedCalls(ProgramStateRef State,
+                                                 const CallEvent &Call,
+                                                 CheckerContext &Ctx) const {
+  if (Ctx.wasInlined)
+    return;
+  unsigned ArgsCount = Call.getNumArgs();
+  bool StateChanged = false;
+  if (!ArgsCount)
+    // No argument, ignore it
+    return;
+  for (unsigned i = 0; i < ArgsCount; i++) {
+    SymbolRef ArgSym = Call.getArgSVal(i).getAsSymbol();
+    // Check if the argument is a pointer. If so, it should have been handled
+    // by checkPointerEscape, can be ignored here.
+    if (!ArgSym)
+      continue;
+
+    const HandleState *CurHandleState = State->get<HStateMap>(ArgSym);
+    // Check if the handle is tracked by analyzer. If not, we ignore it here.
+    if (!CurHandleState)
+      continue;
+
+    if (CurHandleState->isReleased())
+      // Use after free
+      reportUseAfterFree(ArgSym, Call.getSourceRange(), Ctx);
+    State =
+        State->set<HStateMap>(ArgSym, HandleState::getEscaped(CurHandleState));
+    StateChanged = true;
+  }
+  if (StateChanged)
+    Ctx.addTransition(State);
+}
+
+// When a magenta syscall failed. It will return an integer less than 0.
+ProgramStateRef MagentaHandleChecker::conjureFailedState(
+    const CallExpr *CE, ProgramStateRef State, CheckerContext &Ctx) const {
+  const LocationContext *LCtx = Ctx.getLocationContext();
+  DefinedOrUnknownSVal FailedSVal = Ctx.getSValBuilder().conjureSymbolVal(
+      nullptr, CE, LCtx, Ctx.blockCount());
+  ProgramStateRef FailedState = State->BindExpr(CE, LCtx, FailedSVal);
+  FailedState = FailedState->assumeInclusiveRange(
+      FailedSVal, llvm::APSInt::get(INT_MIN), llvm::APSInt::get(-1), true);
+  return FailedState;
+}
+
+ProgramStateRef MagentaHandleChecker::allocateSingleHandle(
+    const CallExpr *CE, const Expr *ArgExpr, ProgramStateRef State,
+    CheckerContext &Ctx) const {
+  const LocationContext *LCtx = Ctx.getLocationContext();
+  SValBuilder &Bldr = Ctx.getSValBuilder();
+  if (!State || !LCtx || !CE)
+    return nullptr;
+  SVal ArgSVal = State->getSVal(ArgExpr, LCtx);
+  // It is a pointer, so get the mem region first.
+  const MemRegion *MemHandle = ArgSVal.getAsRegion();
+  if (!MemHandle)
+    return nullptr;
+  // Retrive thet type of handle
+  QualType HandleType = ArgExpr->getType();
+  if (!HandleType->isPointerType())
+    return nullptr;
+  HandleType = dyn_cast<PointerType>(HandleType.getTypePtr())->getPointeeType();
+  // Bind allocated handle
+  Loc HandleLoc = Bldr.makeLoc(MemHandle);
+  DefinedOrUnknownSVal ConjuredHandleSVal =
+      Bldr.conjureSymbolVal(MemHandle, CE, LCtx, HandleType, Ctx.blockCount());
+  State = State->bindLoc(HandleLoc, ConjuredHandleSVal, LCtx);
+
+  SymbolRef SymHandle = ConjuredHandleSVal.getAsSymbol();
+  if (!SymHandle)
+    return nullptr;
+  if (ConjuredHandleSVal.getBaseKind() == SVal::NonLocKind)
+    // Allocated handle should be larger than 0
+    State =
+        State->assumeInclusiveRange(ConjuredHandleSVal, llvm::APSInt::get(1),
+                                    llvm::APSInt::get(INT_MAX), true);
+
+  HandleState HS = HandleState::getAllocated(MemHandle);
+  // Workaround for a rare case
+  //  TopFunction(mx_handle_t * out) {
+  //    handle_allocate(out);
+  // }
+  // It should be considered as handle escape.
+  if (isArgumentEscaped(ArgSVal))
+    return State->set<HStateMap>(SymHandle, HandleState::getEscaped(&HS));
+  return State->set<HStateMap>(SymHandle, HS);
+}
+
+bool MagentaHandleChecker::isLeaked(SymbolRef SymHandle,
+                                    const HandleState &CurHandleState,
+                                    bool isSymDead,
+                                    ProgramStateRef State) const {
+  if (isSymDead && CurHandleState.isAllocated()) {
+    // Check if handle value is MX_HANDLE_INVALID. If so, not a leak.
+    if (CheckSymbolConstraintToZero(SymHandle, State))
+      return false;
+    // Handle is allocated and dead. Leaked
+    return true;
+  }
+  // Handle not dead. Not a leak.
   return false;
 }
+
+bool MagentaHandleChecker::CheckSymbolConstraintToZero(SymbolRef Sym,
+                                                       ProgramStateRef State) {
+  ConstraintManager &CMgr = State->getConstraintManager();
+  ConditionTruthVal IsZero = CMgr.isNull(State, Sym);
+  if (IsZero.isConstrained()) {
+    // The value can be compared to zero
+    if (IsZero.isConstrainedTrue())
+      return true;
+  }
+  return false;
+}
+
 // Parse annotation attributes from given FunctionDecl. If the annotation is
 // not valid or no annotation available, return false.
 bool MagentaHandleChecker::generateSpecForFuncionDecl(
@@ -388,6 +780,49 @@
   return StringRef();
 }
 
+void MagentaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode,
+                                     CheckerContext &C,
+                                     const SourceRange *Range,
+                                     const std::unique_ptr<BugType> &Type,
+                                     StringRef Msg) const {
+  if (!ErrorNode || !Sym)
+    return;
+  auto R = llvm::make_unique<BugReport>(*Type, Msg, ErrorNode);
+  if (Range)
+    R->addRange(*Range);
+  R->markInteresting(Sym);
+  C.emitReport(std::move(R));
+}
+
+void MagentaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
+                                       CheckerContext &C,
+                                       ExplodedNode *ErrorNode) const {
+  for (SymbolRef LeakedHandle : LeakedHandles) {
+    reportBug(LeakedHandle, ErrorNode, C, nullptr, LeakBugType,
+              "Potential leak of handle");
+  }
+}
+
+void MagentaHandleChecker::reportDoubleRelease(SymbolRef HandleSym,
+                                               const SourceRange &Range,
+                                               CheckerContext &C) const {
+  ExplodedNode *ErrNode = C.generateErrorNode();
+  if (!ErrNode)
+    return;
+  reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType,
+            "Releasing a previously released handle");
+}
+
+void MagentaHandleChecker::reportUseAfterFree(SymbolRef HandleSym,
+                                              const SourceRange &Range,
+                                              CheckerContext &C) const {
+  ExplodedNode *ErrNode = C.generateErrorNode();
+  if (!ErrNode)
+    return;
+  reportBug(HandleSym, ErrNode, C, &Range, UseAfterFreeBugType,
+            "Using a previously released handle");
+}
+
 void ento::registerMagentaHandleChecker(CheckerManager &mgr) {
   mgr.registerChecker<MagentaHandleChecker>();
 }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to