Author: Gabor Horvath
Date: 2020-01-27T13:55:56-08:00
New Revision: f4c26d993bdcb8e5250d774c0d2a6d91fee5ca00

URL: 
https://github.com/llvm/llvm-project/commit/f4c26d993bdcb8e5250d774c0d2a6d91fee5ca00
DIFF: 
https://github.com/llvm/llvm-project/commit/f4c26d993bdcb8e5250d774c0d2a6d91fee5ca00.diff

LOG: [analyzer] Add FuchsiaLockChecker and C11LockChecker

These are mostly trivial additions as both of them are reusing existing
PThreadLockChecker logic. I only needed to add the list of functions to
check and do some plumbing to make sure that we display the right
checker name in the diagnostic.

Differential Revision: https://reviews.llvm.org/D73376

Added: 
    clang/test/Analysis/c11lock.c
    clang/test/Analysis/fuchsia_lock.c

Modified: 
    clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
    clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td 
b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index fc1529f2ea1c..83d78e4f8994 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -227,6 +227,16 @@ def StackAddrAsyncEscapeChecker : 
Checker<"StackAddressAsyncEscape">,
   Dependencies<[StackAddrEscapeBase]>,
   Documentation<HasAlphaDocumentation>;
 
+def PthreadLockBase : Checker<"PthreadLockBase">,
+  HelpText<"Helper registering multiple checks.">,
+  Documentation<NotDocumented>,
+  Hidden;
+
+def C11LockChecker : Checker<"C11Lock">,
+  HelpText<"Simple lock -> unlock checker">,
+  Dependencies<[PthreadLockBase]>,
+  Documentation<HasAlphaDocumentation>;
+
 } // end "alpha.core"
 
 
//===----------------------------------------------------------------------===//
@@ -431,6 +441,7 @@ def ChrootChecker : Checker<"Chroot">,
 
 def PthreadLockChecker : Checker<"PthreadLock">,
   HelpText<"Simple lock -> unlock checker">,
+  Dependencies<[PthreadLockBase]>,
   Documentation<HasAlphaDocumentation>;
 
 def StreamChecker : Checker<"Stream">,
@@ -1442,5 +1453,10 @@ def FuchsiaHandleChecker : Checker<"HandleChecker">,
   HelpText<"A Checker that detect leaks related to Fuchsia handles">,
   Documentation<HasDocumentation>;
 
+def FuchsiaLockChecker : Checker<"Lock">,
+  HelpText<"Check for the correct usage of locking APIs.">,
+  Dependencies<[PthreadLockBase]>,
+  Documentation<HasDocumentation>;
+
 } // end fuchsia
 

diff  --git a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index ed0fc0f98c2c..d3470dad0a43 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -6,8 +6,12 @@
 //
 
//===----------------------------------------------------------------------===//
 //
-// This defines PthreadLockChecker, a simple lock -> unlock checker.
-// Also handles XNU locks, which behave similarly enough to share code.
+// This file defines:
+//  * PthreadLockChecker, a simple lock -> unlock checker.
+//    Which also checks for XNU locks, which behave similarly enough to share
+//    code.
+//  * FuchsiaLocksChecker, which is also rather similar.
+//  * C11LockChecker which also closely follows Pthread semantics.
 //
 
//===----------------------------------------------------------------------===//
 
@@ -15,8 +19,8 @@
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
 
 using namespace clang;
 using namespace ento;
@@ -46,9 +50,7 @@ struct LockState {
     return LockState(UnlockedAndPossiblyDestroyed);
   }
 
-  bool operator==(const LockState &X) const {
-    return K == X.K;
-  }
+  bool operator==(const LockState &X) const { return K == X.K; }
 
   bool isLocked() const { return K == Locked; }
   bool isUnlocked() const { return K == Unlocked; }
@@ -60,98 +62,149 @@ struct LockState {
     return K == UnlockedAndPossiblyDestroyed;
   }
 
-  void Profile(llvm::FoldingSetNodeID &ID) const {
-    ID.AddInteger(K);
-  }
+  void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
 };
 
-class PthreadLockChecker
-    : public Checker<check::PostCall, check::DeadSymbols,
-                     check::RegionChanges> {
-  BugType BT_doublelock{this, "Double locking", "Lock checker"},
-          BT_doubleunlock{this, "Double unlocking", "Lock checker"},
-          BT_destroylock{this, "Use destroyed lock", "Lock checker"},
-          BT_initlock{this, "Init invalid lock", "Lock checker"},
-          BT_lor{this, "Lock order reversal", "Lock checker"};
-
-  enum LockingSemantics {
-    NotApplicable = 0,
-    PthreadSemantics,
-    XNUSemantics
+class PthreadLockChecker : public Checker<check::PostCall, check::DeadSymbols,
+                                          check::RegionChanges> {
+public:
+  enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics };
+  enum CheckerKind {
+    CK_PthreadLockChecker,
+    CK_FuchsiaLockChecker,
+    CK_C11LockChecker,
+    CK_NumCheckKinds
   };
+  DefaultBool ChecksEnabled[CK_NumCheckKinds];
+  CheckerNameRef CheckNames[CK_NumCheckKinds];
 
+private:
   typedef void (PthreadLockChecker::*FnCheck)(const CallEvent &Call,
-                                              CheckerContext &C) const;
-  CallDescriptionMap<FnCheck> Callbacks = {
-    // Init.
-    {{"pthread_mutex_init",        2}, &PthreadLockChecker::InitAnyLock},
-    // TODO: pthread_rwlock_init(2 arguments).
-    // TODO: lck_mtx_init(3 arguments).
-    // TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex.
-    // TODO: lck_rw_init(3 arguments).
-    // TODO: lck_rw_alloc_init(2 arguments) => returns the mutex.
-
-    // Acquire.
-    {{"pthread_mutex_lock",        1}, 
&PthreadLockChecker::AcquirePthreadLock},
-    {{"pthread_rwlock_rdlock",     1}, 
&PthreadLockChecker::AcquirePthreadLock},
-    {{"pthread_rwlock_wrlock",     1}, 
&PthreadLockChecker::AcquirePthreadLock},
-    {{"lck_mtx_lock",              1}, &PthreadLockChecker::AcquireXNULock},
-    {{"lck_rw_lock_exclusive",     1}, &PthreadLockChecker::AcquireXNULock},
-    {{"lck_rw_lock_shared",        1}, &PthreadLockChecker::AcquireXNULock},
-
-    // Try.
-    {{"pthread_mutex_trylock",     1}, &PthreadLockChecker::TryPthreadLock},
-    {{"pthread_rwlock_tryrdlock",  1}, &PthreadLockChecker::TryPthreadLock},
-    {{"pthread_rwlock_trywrlock",  1}, &PthreadLockChecker::TryPthreadLock},
-    {{"lck_mtx_try_lock",          1}, &PthreadLockChecker::TryXNULock},
-    {{"lck_rw_try_lock_exclusive", 1}, &PthreadLockChecker::TryXNULock},
-    {{"lck_rw_try_lock_shared",    1}, &PthreadLockChecker::TryXNULock},
-
-    // Release.
-    {{"pthread_mutex_unlock",      1}, &PthreadLockChecker::ReleaseAnyLock},
-    {{"pthread_rwlock_unlock",     1}, &PthreadLockChecker::ReleaseAnyLock},
-    {{"lck_mtx_unlock",            1}, &PthreadLockChecker::ReleaseAnyLock},
-    {{"lck_rw_unlock_exclusive",   1}, &PthreadLockChecker::ReleaseAnyLock},
-    {{"lck_rw_unlock_shared",      1}, &PthreadLockChecker::ReleaseAnyLock},
-    {{"lck_rw_done",               1}, &PthreadLockChecker::ReleaseAnyLock},
-
-    // Destroy.
-    {{"pthread_mutex_destroy",     1}, 
&PthreadLockChecker::DestroyPthreadLock},
-    {{"lck_mtx_destroy",           2}, &PthreadLockChecker::DestroyXNULock},
-    // TODO: pthread_rwlock_destroy(1 argument).
-    // TODO: lck_rw_destroy(2 arguments).
+                                              CheckerContext &C,
+                                              CheckerKind checkkind) const;
+  CallDescriptionMap<FnCheck> PThreadCallbacks = {
+      // Init.
+      {{"pthread_mutex_init", 2}, &PthreadLockChecker::InitAnyLock},
+      // TODO: pthread_rwlock_init(2 arguments).
+      // TODO: lck_mtx_init(3 arguments).
+      // TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex.
+      // TODO: lck_rw_init(3 arguments).
+      // TODO: lck_rw_alloc_init(2 arguments) => returns the mutex.
+
+      // Acquire.
+      {{"pthread_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+      {{"pthread_rwlock_rdlock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+      {{"pthread_rwlock_wrlock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+      {{"lck_mtx_lock", 1}, &PthreadLockChecker::AcquireXNULock},
+      {{"lck_rw_lock_exclusive", 1}, &PthreadLockChecker::AcquireXNULock},
+      {{"lck_rw_lock_shared", 1}, &PthreadLockChecker::AcquireXNULock},
+
+      // Try.
+      {{"pthread_mutex_trylock", 1}, &PthreadLockChecker::TryPthreadLock},
+      {{"pthread_rwlock_tryrdlock", 1}, &PthreadLockChecker::TryPthreadLock},
+      {{"pthread_rwlock_trywrlock", 1}, &PthreadLockChecker::TryPthreadLock},
+      {{"lck_mtx_try_lock", 1}, &PthreadLockChecker::TryXNULock},
+      {{"lck_rw_try_lock_exclusive", 1}, &PthreadLockChecker::TryXNULock},
+      {{"lck_rw_try_lock_shared", 1}, &PthreadLockChecker::TryXNULock},
+
+      // Release.
+      {{"pthread_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+      {{"pthread_rwlock_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+      {{"lck_mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+      {{"lck_rw_unlock_exclusive", 1}, &PthreadLockChecker::ReleaseAnyLock},
+      {{"lck_rw_unlock_shared", 1}, &PthreadLockChecker::ReleaseAnyLock},
+      {{"lck_rw_done", 1}, &PthreadLockChecker::ReleaseAnyLock},
+
+      // Destroy.
+      {{"pthread_mutex_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock},
+      {{"lck_mtx_destroy", 2}, &PthreadLockChecker::DestroyXNULock},
+      // TODO: pthread_rwlock_destroy(1 argument).
+      // TODO: lck_rw_destroy(2 arguments).
+  };
+
+  CallDescriptionMap<FnCheck> FuchsiaCallbacks = {
+      // Init.
+      {{"spin_lock_init", 1}, &PthreadLockChecker::InitAnyLock},
+
+      // Acquire.
+      {{"spin_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+      {{"spin_lock_save", 3}, &PthreadLockChecker::AcquirePthreadLock},
+      {{"sync_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+      {{"sync_mutex_lock_with_waiter", 1},
+       &PthreadLockChecker::AcquirePthreadLock},
+
+      // Try.
+      {{"spin_trylock", 1}, &PthreadLockChecker::TryFuchsiaLock},
+      {{"sync_mutex_trylock", 1}, &PthreadLockChecker::TryFuchsiaLock},
+      {{"sync_mutex_timedlock", 2}, &PthreadLockChecker::TryFuchsiaLock},
+
+      // Release.
+      {{"spin_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+      {{"spin_unlock_restore", 3}, &PthreadLockChecker::ReleaseAnyLock},
+      {{"sync_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+  };
+
+  CallDescriptionMap<FnCheck> C11Callbacks = {
+      // Init.
+      {{"mtx_init", 2}, &PthreadLockChecker::InitAnyLock},
+
+      // Acquire.
+      {{"mtx_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+
+      // Try.
+      {{"mtx_trylock", 1}, &PthreadLockChecker::TryC11Lock},
+      {{"mtx_timedlock", 2}, &PthreadLockChecker::TryC11Lock},
+
+      // Release.
+      {{"mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+
+      // Destroy
+      {{"mtx_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock},
   };
 
   ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state,
                                                 const MemRegion *lockR,
                                                 const SymbolRef *sym) const;
   void reportUseDestroyedBug(const CallEvent &Call, CheckerContext &C,
-                             unsigned ArgNo) const;
+                             unsigned ArgNo, CheckerKind checkKind) const;
 
   // Init.
-  void InitAnyLock(const CallEvent &Call, CheckerContext &C) const;
+  void InitAnyLock(const CallEvent &Call, CheckerContext &C,
+                   CheckerKind checkkind) const;
   void InitLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
-                   SVal Lock) const;
+                   SVal Lock, CheckerKind checkkind) const;
 
   // Lock, Try-lock.
-  void AcquirePthreadLock(const CallEvent &Call, CheckerContext &C) const;
-  void AcquireXNULock(const CallEvent &Call, CheckerContext &C) const;
-  void TryPthreadLock(const CallEvent &Call, CheckerContext &C) const;
-  void TryXNULock(const CallEvent &Call, CheckerContext &C) const;
+  void AcquirePthreadLock(const CallEvent &Call, CheckerContext &C,
+                          CheckerKind checkkind) const;
+  void AcquireXNULock(const CallEvent &Call, CheckerContext &C,
+                      CheckerKind checkkind) const;
+  void TryPthreadLock(const CallEvent &Call, CheckerContext &C,
+                      CheckerKind checkkind) const;
+  void TryXNULock(const CallEvent &Call, CheckerContext &C,
+                  CheckerKind checkkind) const;
+  void TryFuchsiaLock(const CallEvent &Call, CheckerContext &C,
+                      CheckerKind checkkind) const;
+  void TryC11Lock(const CallEvent &Call, CheckerContext &C,
+                  CheckerKind checkkind) const;
   void AcquireLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
-                      SVal lock, bool isTryLock,
-                      enum LockingSemantics semantics) const;
+                      SVal lock, bool isTryLock, LockingSemantics semantics,
+                      CheckerKind checkkind) const;
 
   // Release.
-  void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C) const;
+  void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C,
+                      CheckerKind checkkind) const;
   void ReleaseLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
-                      SVal lock) const;
+                      SVal lock, CheckerKind checkkind) const;
 
   // Destroy.
-  void DestroyPthreadLock(const CallEvent &Call, CheckerContext &C) const;
-  void DestroyXNULock(const CallEvent &Call, CheckerContext &C) const;
+  void DestroyPthreadLock(const CallEvent &Call, CheckerContext &C,
+                          CheckerKind checkkind) const;
+  void DestroyXNULock(const CallEvent &Call, CheckerContext &C,
+                      CheckerKind checkkind) const;
   void DestroyLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
-                      SVal Lock, enum LockingSemantics semantics) const;
+                      SVal Lock, LockingSemantics semantics,
+                      CheckerKind checkkind) const;
 
 public:
   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
@@ -161,8 +214,30 @@ class PthreadLockChecker
                      ArrayRef<const MemRegion *> ExplicitRegions,
                      ArrayRef<const MemRegion *> Regions,
                      const LocationContext *LCtx, const CallEvent *Call) const;
-  void printState(raw_ostream &Out, ProgramStateRef State,
-                  const char *NL, const char *Sep) const override;
+  void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
+                  const char *Sep) const override;
+
+private:
+  mutable std::unique_ptr<BugType> BT_doublelock[CK_NumCheckKinds];
+  mutable std::unique_ptr<BugType> BT_doubleunlock[CK_NumCheckKinds];
+  mutable std::unique_ptr<BugType> BT_destroylock[CK_NumCheckKinds];
+  mutable std::unique_ptr<BugType> BT_initlock[CK_NumCheckKinds];
+  mutable std::unique_ptr<BugType> BT_lor[CK_NumCheckKinds];
+
+  void initBugType(CheckerKind checkKind) const {
+    if (BT_doublelock[checkKind])
+      return;
+    BT_doublelock[checkKind].reset(
+        new BugType{CheckNames[checkKind], "Double locking", "Lock checker"});
+    BT_doubleunlock[checkKind].reset(
+        new BugType{CheckNames[checkKind], "Double unlocking", "Lock 
checker"});
+    BT_destroylock[checkKind].reset(new BugType{
+        CheckNames[checkKind], "Use destroyed lock", "Lock checker"});
+    BT_initlock[checkKind].reset(new BugType{
+        CheckNames[checkKind], "Init invalid lock", "Lock checker"});
+    BT_lor[checkKind].reset(new BugType{CheckNames[checkKind],
+                                        "Lock order reversal", "Lock 
checker"});
+  }
 };
 } // end anonymous namespace
 
@@ -184,11 +259,14 @@ void PthreadLockChecker::checkPostCall(const CallEvent 
&Call,
   if (!Call.isGlobalCFunction())
     return;
 
-  if (const FnCheck *Callback = Callbacks.lookup(Call))
-    (this->**Callback)(Call, C);
+  if (const FnCheck *Callback = PThreadCallbacks.lookup(Call))
+    (this->**Callback)(Call, C, CK_PthreadLockChecker);
+  else if (const FnCheck *Callback = FuchsiaCallbacks.lookup(Call))
+    (this->**Callback)(Call, C, CK_FuchsiaLockChecker);
+  else if (const FnCheck *Callback = C11Callbacks.lookup(Call))
+    (this->**Callback)(Call, C, CK_C11LockChecker);
 }
 
-
 // When a lock is destroyed, in some semantics(like PthreadSemantics) we are 
not
 // sure if the destroy call has succeeded or failed, and the lock enters one of
 // the 'possibly destroyed' state. There is a short time frame for the
@@ -248,7 +326,7 @@ void PthreadLockChecker::printState(raw_ostream &Out, 
ProgramStateRef State,
   LockSetTy LS = State->get<LockSet>();
   if (!LS.isEmpty()) {
     Out << Sep << "Mutex lock order:" << NL;
-    for (auto I: LS) {
+    for (auto I : LS) {
       I->dumpToStream(Out);
       Out << NL;
     }
@@ -258,29 +336,52 @@ void PthreadLockChecker::printState(raw_ostream &Out, 
ProgramStateRef State,
 }
 
 void PthreadLockChecker::AcquirePthreadLock(const CallEvent &Call,
-                                            CheckerContext &C) const {
-  AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, PthreadSemantics);
+                                            CheckerContext &C,
+                                            CheckerKind checkKind) const {
+  AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, PthreadSemantics,
+                 checkKind);
 }
 
 void PthreadLockChecker::AcquireXNULock(const CallEvent &Call,
-                                           CheckerContext &C) const {
-  AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, XNUSemantics);
+                                        CheckerContext &C,
+                                        CheckerKind checkKind) const {
+  AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, XNUSemantics,
+                 checkKind);
 }
 
 void PthreadLockChecker::TryPthreadLock(const CallEvent &Call,
-                                        CheckerContext &C) const {
-  AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics);
+                                        CheckerContext &C,
+                                        CheckerKind checkKind) const {
+  AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
+                 checkKind);
+}
+
+void PthreadLockChecker::TryXNULock(const CallEvent &Call, CheckerContext &C,
+                                    CheckerKind checkKind) const {
+  AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
+                 checkKind);
+}
+
+void PthreadLockChecker::TryFuchsiaLock(const CallEvent &Call,
+                                        CheckerContext &C,
+                                        CheckerKind checkKind) const {
+  AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
+                 checkKind);
 }
 
-void PthreadLockChecker::TryXNULock(const CallEvent &Call,
-                                        CheckerContext &C) const {
-  AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics);
+void PthreadLockChecker::TryC11Lock(const CallEvent &Call, CheckerContext &C,
+                                    CheckerKind checkKind) const {
+  AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
+                 checkKind);
 }
 
 void PthreadLockChecker::AcquireLockAux(const CallEvent &Call,
                                         CheckerContext &C, unsigned ArgNo,
                                         SVal lock, bool isTryLock,
-                                        enum LockingSemantics semantics) const 
{
+                                        enum LockingSemantics semantics,
+                                        CheckerKind checkKind) const {
+  if (!ChecksEnabled[checkKind])
+    return;
 
   const MemRegion *lockR = lock.getAsRegion();
   if (!lockR)
@@ -296,13 +397,14 @@ void PthreadLockChecker::AcquireLockAux(const CallEvent 
&Call,
       ExplodedNode *N = C.generateErrorNode();
       if (!N)
         return;
+      initBugType(checkKind);
       auto report = std::make_unique<PathSensitiveBugReport>(
-          BT_doublelock, "This lock has already been acquired", N);
+          *BT_doublelock[checkKind], "This lock has already been acquired", N);
       report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
       C.emitReport(std::move(report));
       return;
     } else if (LState->isDestroyed()) {
-      reportUseDestroyedBug(Call, C, ArgNo);
+      reportUseDestroyedBug(Call, C, ArgNo, checkKind);
       return;
     }
   }
@@ -352,13 +454,17 @@ void PthreadLockChecker::AcquireLockAux(const CallEvent 
&Call,
 }
 
 void PthreadLockChecker::ReleaseAnyLock(const CallEvent &Call,
-                                        CheckerContext &C) const {
-  ReleaseLockAux(Call, C, 0, Call.getArgSVal(0));
+                                        CheckerContext &C,
+                                        CheckerKind checkKind) const {
+  ReleaseLockAux(Call, C, 0, Call.getArgSVal(0), checkKind);
 }
 
 void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call,
                                         CheckerContext &C, unsigned ArgNo,
-                                        SVal lock) const {
+                                        SVal lock,
+                                        CheckerKind checkKind) const {
+  if (!ChecksEnabled[checkKind])
+    return;
 
   const MemRegion *lockR = lock.getAsRegion();
   if (!lockR)
@@ -374,13 +480,15 @@ void PthreadLockChecker::ReleaseLockAux(const CallEvent 
&Call,
       ExplodedNode *N = C.generateErrorNode();
       if (!N)
         return;
+      initBugType(checkKind);
       auto Report = std::make_unique<PathSensitiveBugReport>(
-          BT_doubleunlock, "This lock has already been unlocked", N);
+          *BT_doubleunlock[checkKind], "This lock has already been unlocked",
+          N);
       Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
       C.emitReport(std::move(Report));
       return;
     } else if (LState->isDestroyed()) {
-      reportUseDestroyedBug(Call, C, ArgNo);
+      reportUseDestroyedBug(Call, C, ArgNo, checkKind);
       return;
     }
   }
@@ -393,9 +501,12 @@ void PthreadLockChecker::ReleaseLockAux(const CallEvent 
&Call,
       ExplodedNode *N = C.generateErrorNode();
       if (!N)
         return;
+      initBugType(checkKind);
       auto report = std::make_unique<PathSensitiveBugReport>(
-          BT_lor, "This was not the most recently acquired lock. Possible "
-                  "lock order reversal", N);
+          *BT_lor[checkKind],
+          "This was not the most recently acquired lock. Possible "
+          "lock order reversal",
+          N);
       report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
       C.emitReport(std::move(report));
       return;
@@ -409,19 +520,24 @@ void PthreadLockChecker::ReleaseLockAux(const CallEvent 
&Call,
 }
 
 void PthreadLockChecker::DestroyPthreadLock(const CallEvent &Call,
-                                            CheckerContext &C) const {
-  DestroyLockAux(Call, C, 0, Call.getArgSVal(0), PthreadSemantics);
+                                            CheckerContext &C,
+                                            CheckerKind checkKind) const {
+  DestroyLockAux(Call, C, 0, Call.getArgSVal(0), PthreadSemantics, checkKind);
 }
 
 void PthreadLockChecker::DestroyXNULock(const CallEvent &Call,
-                                            CheckerContext &C) const {
-  DestroyLockAux(Call, C, 0, Call.getArgSVal(0), XNUSemantics);
+                                        CheckerContext &C,
+                                        CheckerKind checkKind) const {
+  DestroyLockAux(Call, C, 0, Call.getArgSVal(0), XNUSemantics, checkKind);
 }
 
 void PthreadLockChecker::DestroyLockAux(const CallEvent &Call,
                                         CheckerContext &C, unsigned ArgNo,
                                         SVal Lock,
-                                        enum LockingSemantics semantics) const 
{
+                                        enum LockingSemantics semantics,
+                                        CheckerKind checkKind) const {
+  if (!ChecksEnabled[checkKind])
+    return;
 
   const MemRegion *LockR = Lock.getAsRegion();
   if (!LockR)
@@ -472,19 +588,23 @@ void PthreadLockChecker::DestroyLockAux(const CallEvent 
&Call,
   ExplodedNode *N = C.generateErrorNode();
   if (!N)
     return;
-  auto Report =
-      std::make_unique<PathSensitiveBugReport>(BT_destroylock, Message, N);
+  initBugType(checkKind);
+  auto Report = std::make_unique<PathSensitiveBugReport>(
+      *BT_destroylock[checkKind], Message, N);
   Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
   C.emitReport(std::move(Report));
 }
 
-void PthreadLockChecker::InitAnyLock(const CallEvent &Call,
-                                     CheckerContext &C) const {
-  InitLockAux(Call, C, 0, Call.getArgSVal(0));
+void PthreadLockChecker::InitAnyLock(const CallEvent &Call, CheckerContext &C,
+                                     CheckerKind checkKind) const {
+  InitLockAux(Call, C, 0, Call.getArgSVal(0), checkKind);
 }
 
 void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C,
-                                     unsigned ArgNo, SVal Lock) const {
+                                     unsigned ArgNo, SVal Lock,
+                                     CheckerKind checkKind) const {
+  if (!ChecksEnabled[checkKind])
+    return;
 
   const MemRegion *LockR = Lock.getAsRegion();
   if (!LockR)
@@ -514,20 +634,23 @@ void PthreadLockChecker::InitLockAux(const CallEvent 
&Call, CheckerContext &C,
   ExplodedNode *N = C.generateErrorNode();
   if (!N)
     return;
-  auto Report =
-      std::make_unique<PathSensitiveBugReport>(BT_initlock, Message, N);
+  initBugType(checkKind);
+  auto Report = std::make_unique<PathSensitiveBugReport>(
+      *BT_initlock[checkKind], Message, N);
   Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
   C.emitReport(std::move(Report));
 }
 
 void PthreadLockChecker::reportUseDestroyedBug(const CallEvent &Call,
                                                CheckerContext &C,
-                                               unsigned ArgNo) const {
+                                               unsigned ArgNo,
+                                               CheckerKind checkKind) const {
   ExplodedNode *N = C.generateErrorNode();
   if (!N)
     return;
+  initBugType(checkKind);
   auto Report = std::make_unique<PathSensitiveBugReport>(
-      BT_destroylock, "This lock has already been destroyed", N);
+      *BT_destroylock[checkKind], "This lock has already been destroyed", N);
   Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
   C.emitReport(std::move(Report));
 }
@@ -566,8 +689,9 @@ ProgramStateRef PthreadLockChecker::checkRegionChanges(
   bool IsLibraryFunction = false;
   if (Call && Call->isGlobalCFunction()) {
     // Avoid invalidating mutex state when a known supported function is 
called.
-    if (Callbacks.lookup(*Call))
-        return State;
+    if (PThreadCallbacks.lookup(*Call) || FuchsiaCallbacks.lookup(*Call) ||
+        C11Callbacks.lookup(*Call))
+      return State;
 
     if (Call->isInSystemHeader())
       IsLibraryFunction = true;
@@ -593,10 +717,22 @@ ProgramStateRef PthreadLockChecker::checkRegionChanges(
   return State;
 }
 
-void ento::registerPthreadLockChecker(CheckerManager &mgr) {
+void ento::registerPthreadLockBase(CheckerManager &mgr) {
   mgr.registerChecker<PthreadLockChecker>();
 }
 
-bool ento::shouldRegisterPthreadLockChecker(const LangOptions &LO) {
-  return true;
-}
+bool ento::shouldRegisterPthreadLockBase(const LangOptions &LO) { return true; 
}
+
+#define REGISTER_CHECKER(name)                                                 
\
+  void ento::register##name(CheckerManager &mgr) {                             
\
+    PthreadLockChecker *checker = mgr.getChecker<PthreadLockChecker>();        
\
+    checker->ChecksEnabled[PthreadLockChecker::CK_##name] = true;              
\
+    checker->CheckNames[PthreadLockChecker::CK_##name] =                       
\
+        mgr.getCurrentCheckerName();                                           
\
+  }                                                                            
\
+                                                                               
\
+  bool ento::shouldRegister##name(const LangOptions &LO) { return true; }
+
+REGISTER_CHECKER(PthreadLockChecker)
+REGISTER_CHECKER(FuchsiaLockChecker)
+REGISTER_CHECKER(C11LockChecker)

diff  --git a/clang/test/Analysis/c11lock.c b/clang/test/Analysis/c11lock.c
new file mode 100644
index 000000000000..78e62982fea1
--- /dev/null
+++ b/clang/test/Analysis/c11lock.c
@@ -0,0 +1,90 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.C11Lock -verify %s
+
+typedef int mtx_t;
+struct timespec;
+
+enum {
+  // FIXME: The value if this enum is implementation defined. While all the
+  // implementations I am aware of using 0, the right solution would be to
+  // look this value up in the AST (and disable the check if it is not found).
+  thrd_success = 0,
+  thrd_error = 2
+};
+
+int mtx_init(mtx_t *mutex, int type);
+int mtx_lock(mtx_t *mutex);
+int mtx_timedlock(mtx_t *mutex,
+                  const struct timespec *time_point);
+int mtx_trylock(mtx_t *mutex);
+int mtx_unlock(mtx_t *mutex);
+int mtx_destroy(mtx_t *mutex);
+
+mtx_t mtx1;
+mtx_t mtx2;
+
+void bad1(void)
+{
+  mtx_lock(&mtx1);     // no-warning
+  mtx_lock(&mtx1);     // expected-warning{{This lock has already been 
acquired}}
+}
+
+void bad2(void) {
+  mtx_t mtx;
+  mtx_init(&mtx, 0);
+  mtx_lock(&mtx);
+} // TODO: Warn for missing unlock?
+
+void bad3(void) {
+  mtx_t mtx;
+  mtx_init(&mtx, 0);
+} // TODO: Warn for missing destroy?
+
+void bad4(void) {
+  mtx_t mtx;
+  mtx_init(&mtx, 0);
+  mtx_lock(&mtx);
+  mtx_unlock(&mtx);
+} // TODO: warn for missing destroy?
+
+void bad5(void) {
+  mtx_lock(&mtx1);
+  mtx_unlock(&mtx1);
+  mtx_unlock(&mtx1); // expected-warning {{This lock has already been 
unlocked}}
+}
+
+void bad6() {
+  mtx_init(&mtx1, 0);
+  if (mtx_trylock(&mtx1) != thrd_success)
+    mtx_unlock(&mtx1); // expected-warning {{This lock has already been 
unlocked}}
+}
+
+void bad7(void) {
+  mtx_lock(&mtx1);
+  mtx_lock(&mtx2);
+  mtx_unlock(&mtx1); // expected-warning {{This was not the most recently 
acquired lock. Possible lock order reversal}}
+  mtx_unlock(&mtx2);
+}
+
+void good() {
+  mtx_t mtx;
+  mtx_init(&mtx, 0);
+  mtx_lock(&mtx);
+  mtx_unlock(&mtx);
+  mtx_destroy(&mtx);
+}
+
+void good2() {
+  mtx_t mtx;
+  mtx_init(&mtx, 0);
+  if (mtx_trylock(&mtx) == thrd_success)
+    mtx_unlock(&mtx);
+  mtx_destroy(&mtx);
+}
+
+void good3() {
+  mtx_t mtx;
+  mtx_init(&mtx, 0);
+  if (mtx_timedlock(&mtx, 0) == thrd_success)
+    mtx_unlock(&mtx);
+  mtx_destroy(&mtx);
+}

diff  --git a/clang/test/Analysis/fuchsia_lock.c 
b/clang/test/Analysis/fuchsia_lock.c
new file mode 100644
index 000000000000..9f2a7ec09fa3
--- /dev/null
+++ b/clang/test/Analysis/fuchsia_lock.c
@@ -0,0 +1,104 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=fuchsia.Lock -verify %s
+
+typedef int spin_lock_t;
+typedef int zx_status_t;
+typedef int zx_time_t;
+
+void spin_lock(spin_lock_t *lock);
+int spin_trylock(spin_lock_t *lock);
+void spin_unlock(spin_lock_t *lock);
+void spin_lock_init(spin_lock_t *lock);
+
+void spin_lock_save(spin_lock_t *lock, void *statep,
+                    int flags);
+void spin_unlock_restore(spin_lock_t *lock, void *old_state,
+                         int flags);
+
+spin_lock_t mtx1;
+spin_lock_t mtx2;
+
+void bad1(void)
+{
+       spin_lock(&mtx1);       // no-warning
+       spin_lock(&mtx1);       // expected-warning{{This lock has already been 
acquired}}
+}
+
+void bad2(void) {
+  spin_lock(&mtx1);
+  spin_unlock(&mtx1);
+  spin_unlock(&mtx1); // expected-warning {{This lock has already been 
unlocked}}
+}
+
+void bad3() {
+  spin_lock_init(&mtx1);
+  if (spin_trylock(&mtx1) != 0)
+    spin_unlock(&mtx1); // expected-warning {{This lock has already been 
unlocked}}
+}
+
+void bad4(void) {
+  spin_lock(&mtx1);
+  spin_lock(&mtx2);
+  spin_unlock(&mtx1); // expected-warning {{This was not the most recently 
acquired lock. Possible lock order reversal}}
+  spin_unlock(&mtx2);
+}
+
+void good() {
+  spin_lock_t mtx;
+  spin_lock_init(&mtx);
+  spin_lock_save(&mtx, 0, 0);
+  spin_unlock_restore(&mtx, 0, 0);
+}
+
+void good2() {
+  spin_lock_t mtx;
+  spin_lock_init(&mtx);
+  if (spin_trylock(&mtx) == 0)
+    spin_unlock(&mtx);
+}
+
+typedef int sync_mutex_t;
+void sync_mutex_lock(sync_mutex_t* mutex);
+void sync_mutex_lock_with_waiter(sync_mutex_t* mutex);
+zx_status_t sync_mutex_timedlock(sync_mutex_t* mutex, zx_time_t deadline);
+zx_status_t sync_mutex_trylock(sync_mutex_t* mutex);
+void sync_mutex_unlock(sync_mutex_t* mutex);
+
+sync_mutex_t smtx1;
+sync_mutex_t smtx2;
+
+void bad11(void)
+{
+       sync_mutex_lock(&smtx1);        // no-warning
+       sync_mutex_lock(&smtx1);        // expected-warning{{This lock has 
already been acquired}}
+}
+
+void bad12(void) {
+  sync_mutex_lock_with_waiter(&smtx1);
+  sync_mutex_unlock(&smtx1);
+  sync_mutex_unlock(&smtx1); // expected-warning {{This lock has already been 
unlocked}}
+}
+
+void bad13() {
+  sync_mutex_unlock(&smtx1);
+  if (sync_mutex_trylock(&smtx1) != 0)
+    sync_mutex_unlock(&smtx1); // expected-warning {{This lock has already 
been unlocked}}
+}
+
+void bad14(void) {
+  sync_mutex_lock(&smtx1);
+  sync_mutex_lock(&smtx2);
+  sync_mutex_unlock(&smtx1); // expected-warning {{This was not the most 
recently acquired lock. Possible lock order reversal}}
+  sync_mutex_unlock(&smtx2);
+}
+
+void good11() {
+  sync_mutex_t mtx;
+  if (sync_mutex_trylock(&mtx) == 0)
+    sync_mutex_unlock(&mtx);
+}
+
+void good12() {
+  sync_mutex_t mtx;
+  if (sync_mutex_timedlock(&mtx, 0) == 0)
+    sync_mutex_unlock(&mtx);
+}


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to