ASDenysPetrov updated this revision to Diff 287997.
ASDenysPetrov edited the summary of this revision.
ASDenysPetrov added a comment.

Added support of:

- std::timed_mutex
- std::recursive_timed_mutex
- std::shared_mutex
- std::shared_timed_mutex


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

https://reviews.llvm.org/D85984

Files:
  clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
  clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
  clang/test/Analysis/Checkers/CPlusPlus11LockChecker.cpp

Index: clang/test/Analysis/Checkers/CPlusPlus11LockChecker.cpp
===================================================================
--- /dev/null
+++ clang/test/Analysis/Checkers/CPlusPlus11LockChecker.cpp
@@ -0,0 +1,621 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.cplusplus.CPlusPlus11Lock -verify %s
+
+namespace std {
+
+struct mutex {
+  void lock();
+  bool try_lock();
+  void unlock();
+};
+
+struct timed_mutex {
+  void lock();
+  bool try_lock();
+  void unlock();
+};
+
+struct recursive_mutex {
+  void lock();
+  bool try_lock();
+  void unlock();
+};
+
+struct recursive_timed_mutex {
+  void lock();
+  bool try_lock();
+  void unlock();
+};
+
+struct shared_mutex {
+  void lock();
+  bool try_lock();
+  void unlock();
+};
+
+struct shared_timed_mutex {
+  void lock();
+  bool try_lock();
+  void unlock();
+};
+
+template <typename T>
+struct lock_guard {
+  T &t;
+  lock_guard(T &m) : t(m) {
+    t.lock();
+  }
+  ~lock_guard() {
+    t.unlock();
+  }
+};
+} // namespace std
+
+std::mutex m1;
+std::mutex m2;
+
+// mutex ok
+
+void m_ok1() {
+  m1.lock(); // no-warning
+}
+
+void m_ok2() {
+  m1.unlock(); // no-warning
+}
+
+void m_ok3() {
+  m1.lock();   // no-warning
+  m1.unlock(); // no-warning
+}
+
+void m_ok4() {
+  m1.lock();   // no-warning
+  m1.unlock(); // no-warning
+  m1.lock();   // no-warning
+  m1.unlock(); // no-warning
+}
+
+void m_ok5() {
+  m1.lock();   // no-warning
+  m1.unlock(); // no-warning
+  m2.lock();   // no-warning
+  m2.unlock(); // no-warning
+}
+
+void m_ok6(void) {
+  m1.lock();   // no-warning
+  m2.lock();   // no-warning
+  m2.unlock(); // no-warning
+  m1.unlock(); // no-warning
+}
+
+void m_ok7(void) {
+  if (m1.try_lock()) // no-warning
+    m1.unlock();     // no-warning
+}
+
+void m_ok8(void) {
+  m1.unlock();       // no-warning
+  if (m1.try_lock()) // no-warning
+    m1.unlock();     // no-warning
+}
+
+void m_ok9(void) {
+  if (!m1.try_lock()) // no-warning
+    m1.lock();        // no-warning
+  m1.unlock();        // no-warning
+}
+
+void m_ok10() {
+  std::lock_guard<std::mutex> gl1(m1); // no-warning
+}
+
+// mutex bad
+
+void m_bad1() {
+  m1.lock(); // no-warning
+  m1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void m_bad2() {
+  m1.lock();   // no-warning
+  m1.unlock(); // no-warning
+  m1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void m_bad3() {
+  m1.lock();   // no-warning
+  m2.lock();   // no-warning
+  m1.unlock(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}}
+  m2.unlock(); // no-warning
+}
+
+void m_bad5() {
+  while (true)
+    m1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void m_bad6() {
+  while (true)
+    m1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void m_bad7() {
+  if (m1.try_lock()) // no-warning
+    m1.lock();       // expected-warning{{This lock has already been acquired}}
+}
+
+std::timed_mutex tm1;
+std::timed_mutex tm2;
+
+// timed_mutex ok
+
+void tm_ok1() {
+  tm1.lock(); // no-warning
+}
+
+void tm_ok2() {
+  tm1.unlock(); // no-warning
+}
+
+void tm_ok3() {
+  tm1.lock();   // no-warning
+  tm1.unlock(); // no-warning
+}
+
+void tm_ok4() {
+  tm1.lock();   // no-warning
+  tm1.unlock(); // no-warning
+  tm1.lock();   // no-warning
+  tm1.unlock(); // no-warning
+}
+
+void tm_ok5() {
+  tm1.lock();   // no-warning
+  tm1.unlock(); // no-warning
+  tm2.lock();   // no-warning
+  tm2.unlock(); // no-warning
+}
+
+void tm_ok6(void) {
+  tm1.lock();   // no-warning
+  tm2.lock();   // no-warning
+  tm2.unlock(); // no-warning
+  tm1.unlock(); // no-warning
+}
+
+void tm_ok7(void) {
+  if (tm1.try_lock()) // no-warning
+    tm1.unlock();     // no-warning
+}
+
+void tm_ok8(void) {
+  tm1.unlock();       // no-warning
+  if (tm1.try_lock()) // no-warning
+    tm1.unlock();     // no-warning
+}
+
+void tm_ok9(void) {
+  if (!tm1.try_lock()) // no-warning
+    tm1.lock();        // no-warning
+  tm1.unlock();        // no-warning
+}
+
+void tm_ok10() {
+  std::lock_guard<std::timed_mutex> gl(tm1); // no-warning
+}
+
+// timed_mutex bad
+
+void tm_bad1() {
+  tm1.lock(); // no-warning
+  tm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void tm_bad2() {
+  tm1.lock();   // no-warning
+  tm1.unlock(); // no-warning
+  tm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void tm_bad3() {
+  tm1.lock();   // no-warning
+  tm2.lock();   // no-warning
+  tm1.unlock(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}}
+  tm2.unlock(); // no-warning
+}
+
+void tm_bad5() {
+  while (true)
+    tm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void tm_bad6() {
+  while (true)
+    tm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void tm_bad7() {
+  if (tm1.try_lock()) // no-warning
+    tm1.lock();       // expected-warning{{This lock has already been acquired}}
+}
+
+std::recursive_mutex rm1;
+std::recursive_mutex rm2;
+
+// recursive_mutex ok
+
+void rm_ok1() {
+  rm1.lock(); // no-warning
+}
+
+void rm_ok2() {
+  rm1.unlock(); // no-warning
+}
+
+void rm_ok3() {
+  rm1.lock();   // no-warning
+  rm1.unlock(); // no-warning
+}
+
+void rm_ok4() {
+  rm1.lock();   // no-warning
+  rm1.unlock(); // no-warning
+  rm1.lock();   // no-warning
+  rm1.unlock(); // no-warning
+}
+
+void rm_ok5() {
+  rm1.lock();   // no-warning
+  rm1.unlock(); // no-warning
+  rm2.lock();   // no-warning
+  rm2.unlock(); // no-warning
+}
+
+void rm_ok6(void) {
+  rm1.lock();   // no-warning
+  rm2.lock();   // no-warning
+  rm2.unlock(); // no-warning
+  rm1.unlock(); // no-warning
+}
+
+void rm_ok7(void) {
+  if (rm1.try_lock()) // no-warning
+    rm1.unlock();     // no-warning
+}
+
+void rm_ok8(void) {
+  rm1.unlock();       // no-warning
+  if (rm1.try_lock()) // no-warning
+    rm1.unlock();     // no-warning
+}
+
+void rm_ok9(void) {
+  if (!rm1.try_lock()) // no-warning
+    rm1.lock();        // no-warning
+  rm1.unlock();        // no-warning
+}
+
+void rm_ok10() {
+  std::lock_guard<std::recursive_mutex> gl(rm1); // no-warning
+}
+
+// recursive_mutex bad
+
+void rm_bad1() {
+  rm1.lock(); // no-warning
+  rm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void rm_bad2() {
+  rm1.lock();   // no-warning
+  rm1.unlock(); // no-warning
+  rm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void rm_bad3() {
+  rm1.lock();   // no-warning
+  rm2.lock();   // no-warning
+  rm1.unlock(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}}
+  rm2.unlock(); // no-warning
+}
+
+void rm_bad5() {
+  while (true)
+    rm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void rm_bad6() {
+  while (true)
+    rm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void rm_bad7() {
+  if (rm1.try_lock()) // no-warning
+    rm1.lock();       // expected-warning{{This lock has already been acquired}}
+}
+
+std::recursive_timed_mutex rtm1;
+std::recursive_timed_mutex rtm2;
+
+// recursive_timed_mutex ok
+
+void rtm_ok1() {
+  rtm1.lock(); // no-warning
+}
+
+void rtm_ok2() {
+  rtm1.unlock(); // no-warning
+}
+
+void rtm_ok3() {
+  rtm1.lock();   // no-warning
+  rtm1.unlock(); // no-warning
+}
+
+void rtm_ok4() {
+  rtm1.lock();   // no-warning
+  rtm1.unlock(); // no-warning
+  rtm1.lock();   // no-warning
+  rtm1.unlock(); // no-warning
+}
+
+void rtm_ok5() {
+  rtm1.lock();   // no-warning
+  rtm1.unlock(); // no-warning
+  rtm2.lock();   // no-warning
+  rtm2.unlock(); // no-warning
+}
+
+void rtm_ok6(void) {
+  rtm1.lock();   // no-warning
+  rtm2.lock();   // no-warning
+  rtm2.unlock(); // no-warning
+  rtm1.unlock(); // no-warning
+}
+
+void rtm_ok7(void) {
+  if (rtm1.try_lock()) // no-warning
+    rtm1.unlock();     // no-warning
+}
+
+void rtm_ok8(void) {
+  rtm1.unlock();       // no-warning
+  if (rtm1.try_lock()) // no-warning
+    rtm1.unlock();     // no-warning
+}
+
+void rtm_ok9(void) {
+  if (!rtm1.try_lock()) // no-warning
+    rtm1.lock();        // no-warning
+  rtm1.unlock();        // no-warning
+}
+
+void rtm_ok10() {
+  std::lock_guard<std::recursive_timed_mutex> gl(rtm1); // no-warning
+}
+
+// recursive_timed_mutex bad
+
+void rtm_bad1() {
+  rtm1.lock(); // no-warning
+  rtm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void rtm_bad2() {
+  rtm1.lock();   // no-warning
+  rtm1.unlock(); // no-warning
+  rtm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void rtm_bad3() {
+  rtm1.lock();   // no-warning
+  rtm2.lock();   // no-warning
+  rtm1.unlock(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}}
+  rtm2.unlock(); // no-warning
+}
+
+void rtm_bad5() {
+  while (true)
+    rtm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void rtm_bad6() {
+  while (true)
+    rtm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void rtm_bad7() {
+  if (rtm1.try_lock()) // no-warning
+    rtm1.lock();       // expected-warning{{This lock has already been acquired}}
+}
+
+std::shared_mutex sm1;
+std::shared_mutex sm2;
+
+// shared_mutex ok
+
+void sm_ok1() {
+  sm1.lock(); // no-warning
+}
+
+void sm_ok2() {
+  sm1.unlock(); // no-warning
+}
+
+void sm_ok3() {
+  sm1.lock();   // no-warning
+  sm1.unlock(); // no-warning
+}
+
+void sm_ok4() {
+  sm1.lock();   // no-warning
+  sm1.unlock(); // no-warning
+  sm1.lock();   // no-warning
+  sm1.unlock(); // no-warning
+}
+
+void sm_ok5() {
+  sm1.lock();   // no-warning
+  sm1.unlock(); // no-warning
+  sm2.lock();   // no-warning
+  sm2.unlock(); // no-warning
+}
+
+void sm_ok6(void) {
+  sm1.lock();   // no-warning
+  sm2.lock();   // no-warning
+  sm2.unlock(); // no-warning
+  sm1.unlock(); // no-warning
+}
+
+void sm_ok7(void) {
+  if (sm1.try_lock()) // no-warning
+    sm1.unlock();     // no-warning
+}
+
+void sm_ok8(void) {
+  sm1.unlock();       // no-warning
+  if (sm1.try_lock()) // no-warning
+    sm1.unlock();     // no-warning
+}
+
+void sm_ok9(void) {
+  if (!sm1.try_lock()) // no-warning
+    sm1.lock();        // no-warning
+  sm1.unlock();        // no-warning
+}
+
+void sm_ok10() {
+  std::lock_guard<std::shared_mutex> gl(sm1); // no-warning
+}
+
+// shared_mutex bad
+
+void sm_bad1() {
+  sm1.lock(); // no-warning
+  sm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void sm_bad2() {
+  sm1.lock();   // no-warning
+  sm1.unlock(); // no-warning
+  sm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void sm_bad3() {
+  sm1.lock();   // no-warning
+  sm2.lock();   // no-warning
+  sm1.unlock(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}}
+  sm2.unlock(); // no-warning
+}
+
+void sm_bad5() {
+  while (true)
+    sm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void sm_bad6() {
+  while (true)
+    sm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void sm_bad7() {
+  if (sm1.try_lock()) // no-warning
+    sm1.lock();       // expected-warning{{This lock has already been acquired}}
+}
+
+std::shared_timed_mutex stm1;
+std::shared_timed_mutex stm2;
+
+// shared_timed_mutex ok
+
+void stm_ok1() {
+  stm1.lock(); // no-warning
+}
+
+void stm_ok2() {
+  stm1.unlock(); // no-warning
+}
+
+void stm_ok3() {
+  stm1.lock();   // no-warning
+  stm1.unlock(); // no-warning
+}
+
+void stm_ok4() {
+  stm1.lock();   // no-warning
+  stm1.unlock(); // no-warning
+  stm1.lock();   // no-warning
+  stm1.unlock(); // no-warning
+}
+
+void stm_ok5() {
+  stm1.lock();   // no-warning
+  stm1.unlock(); // no-warning
+  stm2.lock();   // no-warning
+  stm2.unlock(); // no-warning
+}
+
+void stm_ok6(void) {
+  stm1.lock();   // no-warning
+  stm2.lock();   // no-warning
+  stm2.unlock(); // no-warning
+  stm1.unlock(); // no-warning
+}
+
+void stm_ok7(void) {
+  if (stm1.try_lock()) // no-warning
+    stm1.unlock();     // no-warning
+}
+
+void stm_ok8(void) {
+  stm1.unlock();       // no-warning
+  if (stm1.try_lock()) // no-warning
+    stm1.unlock();     // no-warning
+}
+
+void stm_ok9(void) {
+  if (!stm1.try_lock()) // no-warning
+    stm1.lock();        // no-warning
+  stm1.unlock();        // no-warning
+}
+
+void stm_ok10() {
+  std::lock_guard<std::shared_timed_mutex> gl(stm1); // no-warning
+}
+
+// shared_timed_mutex bad
+
+void stm_bad1() {
+  stm1.lock(); // no-warning
+  stm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void stm_bad2() {
+  stm1.lock();   // no-warning
+  stm1.unlock(); // no-warning
+  stm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void stm_bad3() {
+  stm1.lock();   // no-warning
+  stm2.lock();   // no-warning
+  stm1.unlock(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}}
+  stm2.unlock(); // no-warning
+}
+
+void stm_bad5() {
+  while (true)
+    stm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void stm_bad6() {
+  while (true)
+    stm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void stm_bad7() {
+  if (stm1.try_lock()) // no-warning
+    stm1.lock();       // expected-warning{{This lock has already been acquired}}
+}
Index: clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -70,11 +70,17 @@
 class PthreadLockChecker : public Checker<check::PostCall, check::DeadSymbols,
                                           check::RegionChanges> {
 public:
-  enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics };
+  enum LockingSemantics {
+    NotApplicable = 0,
+    PthreadSemantics,
+    XNUSemantics,
+    CPlusPlusSemantics,
+  };
   enum CheckerKind {
     CK_PthreadLockChecker,
     CK_FuchsiaLockChecker,
     CK_C11LockChecker,
+    CK_CPlusPlus11LockChecker,
     CK_NumCheckKinds
   };
   DefaultBool ChecksEnabled[CK_NumCheckKinds];
@@ -83,7 +89,7 @@
 private:
   typedef void (PthreadLockChecker::*FnCheck)(const CallEvent &Call,
                                               CheckerContext &C,
-                                              CheckerKind checkkind) const;
+                                              CheckerKind CheckKind) const;
   CallDescriptionMap<FnCheck> PThreadCallbacks = {
       // Init.
       {{"pthread_mutex_init", 2}, &PthreadLockChecker::InitAnyLock},
@@ -164,49 +170,101 @@
       {{"mtx_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock},
   };
 
+  CallDescriptionMap<FnCheck> CPlusPlus11Callbacks = {
+
+      // Acquire.
+      {{{"std", "mutex", "lock"}, 0, 0},
+       &PthreadLockChecker::AcquireCPlusPlus11Lock},
+      {{{"std", "timed_mutex", "lock"}, 0, 0},
+       &PthreadLockChecker::AcquireCPlusPlus11Lock},
+      {{{"std", "recursive_mutex", "lock"}, 0, 0},
+       &PthreadLockChecker::AcquireCPlusPlus11Lock},
+      {{{"std", "recursive_timed_mutex", "lock"}, 0, 0},
+       &PthreadLockChecker::AcquireCPlusPlus11Lock},
+      {{{"std", "shared_mutex", "lock"}, 0, 0},
+       &PthreadLockChecker::AcquireCPlusPlus11Lock},
+      {{{"std", "shared_timed_mutex", "lock"}, 0, 0},
+       &PthreadLockChecker::AcquireCPlusPlus11Lock},
+
+      // Try.
+      {{{"std", "mutex", "try_lock"}, 0, 0},
+       &PthreadLockChecker::TryCPlusPlus11Lock},
+      {{{"std", "timed_mutex", "try_lock"}, 0, 0},
+       &PthreadLockChecker::TryCPlusPlus11Lock},
+      {{{"std", "recursive_mutex", "try_lock"}, 0, 0},
+       &PthreadLockChecker::TryCPlusPlus11Lock},
+      {{{"std", "recursive_timed_mutex", "try_lock"}, 0, 0},
+       &PthreadLockChecker::TryCPlusPlus11Lock},
+      {{{"std", "shared_mutex", "try_lock"}, 0, 0},
+       &PthreadLockChecker::TryCPlusPlus11Lock},
+      {{{"std", "shared_timed_mutex", "try_lock"}, 0, 0},
+       &PthreadLockChecker::TryCPlusPlus11Lock},
+
+      // Release.
+      {{{"std", "mutex", "unlock"}, 0, 0},
+       &PthreadLockChecker::ReleaseCPlusPlus11Lock},
+      {{{"std", "timed_mutex", "unlock"}, 0, 0},
+       &PthreadLockChecker::ReleaseCPlusPlus11Lock},
+      {{{"std", "recursive_mutex", "unlock"}, 0, 0},
+       &PthreadLockChecker::ReleaseCPlusPlus11Lock},
+      {{{"std", "recursive_timed_mutex", "unlock"}, 0, 0},
+       &PthreadLockChecker::ReleaseCPlusPlus11Lock},
+      {{{"std", "shared_mutex", "unlock"}, 0, 0},
+       &PthreadLockChecker::ReleaseCPlusPlus11Lock},
+      {{{"std", "shared_timed_mutex", "unlock"}, 0, 0},
+       &PthreadLockChecker::ReleaseCPlusPlus11Lock},
+  };
+
   ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state,
                                                 const MemRegion *lockR,
                                                 const SymbolRef *sym) const;
   void reportUseDestroyedBug(const CallEvent &Call, CheckerContext &C,
-                             unsigned ArgNo, CheckerKind checkKind) const;
+                             const Expr *MtxExpr, CheckerKind CheckKind) const;
 
   // Init.
   void InitAnyLock(const CallEvent &Call, CheckerContext &C,
-                   CheckerKind checkkind) const;
+                   CheckerKind CheckKind) const;
   void InitLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
-                   SVal Lock, CheckerKind checkkind) const;
+                   SVal Lock, CheckerKind CheckKind) const;
 
   // Lock, Try-lock.
   void AcquirePthreadLock(const CallEvent &Call, CheckerContext &C,
-                          CheckerKind checkkind) const;
+                          CheckerKind CheckKind) const;
   void AcquireXNULock(const CallEvent &Call, CheckerContext &C,
-                      CheckerKind checkkind) const;
+                      CheckerKind CheckKind) const;
+  void AcquireCPlusPlus11Lock(const CallEvent &Call, CheckerContext &C,
+                              CheckerKind CheckKind) const;
   void TryPthreadLock(const CallEvent &Call, CheckerContext &C,
-                      CheckerKind checkkind) const;
+                      CheckerKind CheckKind) const;
   void TryXNULock(const CallEvent &Call, CheckerContext &C,
-                  CheckerKind checkkind) const;
+                  CheckerKind CheckKind) const;
   void TryFuchsiaLock(const CallEvent &Call, CheckerContext &C,
-                      CheckerKind checkkind) const;
+                      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, LockingSemantics semantics,
-                      CheckerKind checkkind) const;
+                  CheckerKind CheckKind) const;
+  void TryCPlusPlus11Lock(const CallEvent &Call, CheckerContext &C,
+                          CheckerKind CheckKind) const;
+  void AcquireLockAux(const CallEvent &Call, CheckerContext &C,
+                      const Expr *MtxExpr, SVal MtxVal, bool IsTryLock,
+                      LockingSemantics semantics, CheckerKind CheckKind) const;
 
   // Release.
   void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C,
-                      CheckerKind checkkind) const;
-  void ReleaseLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
-                      SVal lock, CheckerKind checkkind) const;
+                      CheckerKind CheckKind) const;
+  void ReleaseCPlusPlus11Lock(const CallEvent &Call, CheckerContext &C,
+                              CheckerKind CheckKind) const;
+  void ReleaseLockAux(const CallEvent &Call, CheckerContext &C,
+                      const Expr *MtxExpr, SVal MtxVal,
+                      LockingSemantics semantics, CheckerKind CheckKind) const;
 
   // Destroy.
   void DestroyPthreadLock(const CallEvent &Call, CheckerContext &C,
-                          CheckerKind checkkind) const;
+                          CheckerKind CheckKind) const;
   void DestroyXNULock(const CallEvent &Call, CheckerContext &C,
-                      CheckerKind checkkind) const;
+                      CheckerKind CheckKind) const;
   void DestroyLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
                       SVal Lock, LockingSemantics semantics,
-                      CheckerKind checkkind) const;
+                      CheckerKind CheckKind) const;
 
 public:
   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
@@ -226,18 +284,18 @@
   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])
+  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],
+    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"});
   }
 };
@@ -260,7 +318,7 @@
   // with exactly one identifier?
   // FIXME: Try to handle cases when the implementation was inlined rather
   // than just giving up.
-  if (!Call.isGlobalCFunction() || C.wasInlined)
+  if (C.wasInlined)
     return;
 
   if (const FnCheck *Callback = PThreadCallbacks.lookup(Call))
@@ -269,6 +327,8 @@
     (this->**Callback)(Call, C, CK_FuchsiaLockChecker);
   else if (const FnCheck *Callback = C11Callbacks.lookup(Call))
     (this->**Callback)(Call, C, CK_C11LockChecker);
+  else if (const FnCheck *Callback = CPlusPlus11Callbacks.lookup(Call))
+    (this->**Callback)(Call, C, CK_CPlusPlus11LockChecker);
 }
 
 // When a lock is destroyed, in some semantics(like PthreadSemantics) we are not
@@ -341,53 +401,73 @@
 
 void PthreadLockChecker::AcquirePthreadLock(const CallEvent &Call,
                                             CheckerContext &C,
-                                            CheckerKind checkKind) const {
-  AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, PthreadSemantics,
-                 checkKind);
+                                            CheckerKind CheckKind) const {
+  AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false,
+                 PthreadSemantics, CheckKind);
 }
 
 void PthreadLockChecker::AcquireXNULock(const CallEvent &Call,
                                         CheckerContext &C,
-                                        CheckerKind checkKind) const {
-  AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, XNUSemantics,
-                 checkKind);
+                                        CheckerKind CheckKind) const {
+  AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false,
+                 XNUSemantics, CheckKind);
+}
+
+void PthreadLockChecker::AcquireCPlusPlus11Lock(const CallEvent &Call,
+                                                CheckerContext &C,
+                                                CheckerKind CheckKind) const {
+  auto MemberCall = cast<CXXMemberCall>(&Call);
+  auto ThisExpr = MemberCall->getCXXThisExpr();
+  auto ThisVal = MemberCall->getCXXThisVal();
+  AcquireLockAux(Call, C, ThisExpr, ThisVal, false, CPlusPlusSemantics,
+                 CheckKind);
 }
 
 void PthreadLockChecker::TryPthreadLock(const CallEvent &Call,
                                         CheckerContext &C,
-                                        CheckerKind checkKind) const {
-  AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
-                 checkKind);
+                                        CheckerKind CheckKind) const {
+  AcquireLockAux(Call, C, Call.getArgExpr(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);
+                                    CheckerKind CheckKind) const {
+  AcquireLockAux(Call, C, Call.getArgExpr(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);
+                                        CheckerKind CheckKind) const {
+  AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
+                 PthreadSemantics, CheckKind);
 }
 
 void PthreadLockChecker::TryC11Lock(const CallEvent &Call, CheckerContext &C,
-                                    CheckerKind checkKind) const {
-  AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
-                 checkKind);
+                                    CheckerKind CheckKind) const {
+  AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
+                 PthreadSemantics, CheckKind);
+}
+
+void PthreadLockChecker::TryCPlusPlus11Lock(const CallEvent &Call,
+                                            CheckerContext &C,
+                                            CheckerKind CheckKind) const {
+  auto MemberCall = cast<CXXMemberCall>(&Call);
+  auto ThisExpr = MemberCall->getCXXThisExpr();
+  auto ThisVal = MemberCall->getCXXThisVal();
+  AcquireLockAux(Call, C, ThisExpr, ThisVal, true, CPlusPlusSemantics,
+                 CheckKind);
 }
 
 void PthreadLockChecker::AcquireLockAux(const CallEvent &Call,
-                                        CheckerContext &C, unsigned ArgNo,
-                                        SVal lock, bool isTryLock,
-                                        enum LockingSemantics semantics,
-                                        CheckerKind checkKind) const {
-  if (!ChecksEnabled[checkKind])
+                                        CheckerContext &C, const Expr *MtxExpr,
+                                        SVal MtxVal, bool IsTryLock,
+                                        LockingSemantics semantics,
+                                        CheckerKind CheckKind) const {
+  if (!ChecksEnabled[CheckKind])
     return;
 
-  const MemRegion *lockR = lock.getAsRegion();
+  const MemRegion *lockR = MtxVal.getAsRegion();
   if (!lockR)
     return;
 
@@ -398,23 +478,24 @@
 
   if (const LockState *LState = state->get<LockMap>(lockR)) {
     if (LState->isLocked()) {
+      // Ignore twice recursive lock
       ExplodedNode *N = C.generateErrorNode();
       if (!N)
         return;
-      initBugType(checkKind);
+      initBugType(CheckKind);
       auto report = std::make_unique<PathSensitiveBugReport>(
-          *BT_doublelock[checkKind], "This lock has already been acquired", N);
-      report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
+          *BT_doublelock[CheckKind], "This lock has already been acquired", N);
+      report->addRange(MtxExpr->getSourceRange());
       C.emitReport(std::move(report));
       return;
     } else if (LState->isDestroyed()) {
-      reportUseDestroyedBug(Call, C, ArgNo, checkKind);
+      reportUseDestroyedBug(Call, C, MtxExpr, CheckKind);
       return;
     }
   }
 
   ProgramStateRef lockSucc = state;
-  if (isTryLock) {
+  if (IsTryLock) {
     // Bifurcate the state, and allow a mode where the lock acquisition fails.
     SVal RetVal = Call.getReturnValue();
     if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
@@ -423,6 +504,7 @@
       case PthreadSemantics:
         std::tie(lockFail, lockSucc) = state->assume(*DefinedRetVal);
         break;
+      case CPlusPlusSemantics:
       case XNUSemantics:
         std::tie(lockSucc, lockFail) = state->assume(*DefinedRetVal);
         break;
@@ -446,8 +528,9 @@
     // We might want to handle the case when the mutex lock function was inlined
     // and returned an Unknown or Undefined value.
   } else {
-    // XNU locking semantics return void on non-try locks
-    assert((semantics == XNUSemantics) && "Unknown locking semantics");
+    // XNU and C++ locking semantics return void on non-try locks
+    assert((semantics == CPlusPlusSemantics || semantics == XNUSemantics) &&
+           "Unknown locking semantics");
     lockSucc = state;
   }
 
@@ -459,18 +542,28 @@
 
 void PthreadLockChecker::ReleaseAnyLock(const CallEvent &Call,
                                         CheckerContext &C,
-                                        CheckerKind checkKind) const {
-  ReleaseLockAux(Call, C, 0, Call.getArgSVal(0), checkKind);
+                                        CheckerKind CheckKind) const {
+  ReleaseLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), NotApplicable,
+                 CheckKind);
+}
+
+void PthreadLockChecker::ReleaseCPlusPlus11Lock(const CallEvent &Call,
+                                                CheckerContext &C,
+                                                CheckerKind CheckKind) const {
+  auto MemberCall = cast<CXXMemberCall>(&Call);
+  auto ThisExpr = MemberCall->getCXXThisExpr();
+  auto ThisVal = MemberCall->getCXXThisVal();
+  ReleaseLockAux(Call, C, ThisExpr, ThisVal, CPlusPlusSemantics, CheckKind);
 }
 
 void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call,
-                                        CheckerContext &C, unsigned ArgNo,
-                                        SVal lock,
-                                        CheckerKind checkKind) const {
-  if (!ChecksEnabled[checkKind])
+                                        CheckerContext &C, const Expr *MtxExpr,
+                                        SVal MtxVal, LockingSemantics semantics,
+                                        CheckerKind CheckKind) const {
+  if (!ChecksEnabled[CheckKind])
     return;
 
-  const MemRegion *lockR = lock.getAsRegion();
+  const MemRegion *lockR = MtxVal.getAsRegion();
   if (!lockR)
     return;
 
@@ -484,15 +577,15 @@
       ExplodedNode *N = C.generateErrorNode();
       if (!N)
         return;
-      initBugType(checkKind);
+      initBugType(CheckKind);
       auto Report = std::make_unique<PathSensitiveBugReport>(
-          *BT_doubleunlock[checkKind], "This lock has already been unlocked",
+          *BT_doubleunlock[CheckKind], "This lock has already been unlocked",
           N);
-      Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
+      Report->addRange(MtxExpr->getSourceRange());
       C.emitReport(std::move(Report));
       return;
     } else if (LState->isDestroyed()) {
-      reportUseDestroyedBug(Call, C, ArgNo, checkKind);
+      reportUseDestroyedBug(Call, C, MtxExpr, CheckKind);
       return;
     }
   }
@@ -505,13 +598,13 @@
       ExplodedNode *N = C.generateErrorNode();
       if (!N)
         return;
-      initBugType(checkKind);
+      initBugType(CheckKind);
       auto report = std::make_unique<PathSensitiveBugReport>(
-          *BT_lor[checkKind],
+          *BT_lor[CheckKind],
           "This was not the most recently acquired lock. Possible "
           "lock order reversal",
           N);
-      report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
+      report->addRange(MtxExpr->getSourceRange());
       C.emitReport(std::move(report));
       return;
     }
@@ -525,22 +618,21 @@
 
 void PthreadLockChecker::DestroyPthreadLock(const CallEvent &Call,
                                             CheckerContext &C,
-                                            CheckerKind checkKind) const {
-  DestroyLockAux(Call, C, 0, Call.getArgSVal(0), PthreadSemantics, checkKind);
+                                            CheckerKind CheckKind) const {
+  DestroyLockAux(Call, C, 0, Call.getArgSVal(0), PthreadSemantics, CheckKind);
 }
 
 void PthreadLockChecker::DestroyXNULock(const CallEvent &Call,
                                         CheckerContext &C,
-                                        CheckerKind checkKind) const {
-  DestroyLockAux(Call, C, 0, Call.getArgSVal(0), XNUSemantics, checkKind);
+                                        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,
-                                        CheckerKind checkKind) const {
-  if (!ChecksEnabled[checkKind])
+                                        SVal Lock, LockingSemantics semantics,
+                                        CheckerKind CheckKind) const {
+  if (!ChecksEnabled[CheckKind])
     return;
 
   const MemRegion *LockR = Lock.getAsRegion();
@@ -592,22 +684,22 @@
   ExplodedNode *N = C.generateErrorNode();
   if (!N)
     return;
-  initBugType(checkKind);
+  initBugType(CheckKind);
   auto Report = std::make_unique<PathSensitiveBugReport>(
-      *BT_destroylock[checkKind], Message, N);
+      *BT_destroylock[CheckKind], Message, N);
   Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
   C.emitReport(std::move(Report));
 }
 
 void PthreadLockChecker::InitAnyLock(const CallEvent &Call, CheckerContext &C,
-                                     CheckerKind checkKind) const {
-  InitLockAux(Call, C, 0, Call.getArgSVal(0), checkKind);
+                                     CheckerKind CheckKind) const {
+  InitLockAux(Call, C, 0, Call.getArgSVal(0), CheckKind);
 }
 
 void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C,
                                      unsigned ArgNo, SVal Lock,
-                                     CheckerKind checkKind) const {
-  if (!ChecksEnabled[checkKind])
+                                     CheckerKind CheckKind) const {
+  if (!ChecksEnabled[CheckKind])
     return;
 
   const MemRegion *LockR = Lock.getAsRegion();
@@ -638,24 +730,24 @@
   ExplodedNode *N = C.generateErrorNode();
   if (!N)
     return;
-  initBugType(checkKind);
+  initBugType(CheckKind);
   auto Report = std::make_unique<PathSensitiveBugReport>(
-      *BT_initlock[checkKind], Message, N);
+      *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,
-                                               CheckerKind checkKind) const {
+                                               const Expr *MtxExpr,
+                                               CheckerKind CheckKind) const {
   ExplodedNode *N = C.generateErrorNode();
   if (!N)
     return;
-  initBugType(checkKind);
+  initBugType(CheckKind);
   auto Report = std::make_unique<PathSensitiveBugReport>(
-      *BT_destroylock[checkKind], "This lock has already been destroyed", N);
-  Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
+      *BT_destroylock[CheckKind], "This lock has already been destroyed", N);
+  Report->addRange(MtxExpr->getSourceRange());
   C.emitReport(std::move(Report));
 }
 
@@ -691,10 +783,10 @@
     const CallEvent *Call) const {
 
   bool IsLibraryFunction = false;
-  if (Call && Call->isGlobalCFunction()) {
+  if (Call) {
     // Avoid invalidating mutex state when a known supported function is called.
     if (PThreadCallbacks.lookup(*Call) || FuchsiaCallbacks.lookup(*Call) ||
-        C11Callbacks.lookup(*Call))
+        C11Callbacks.lookup(*Call) || CPlusPlus11Callbacks.lookup(*Call))
       return State;
 
     if (Call->isInSystemHeader())
@@ -740,3 +832,4 @@
 REGISTER_CHECKER(PthreadLockChecker)
 REGISTER_CHECKER(FuchsiaLockChecker)
 REGISTER_CHECKER(C11LockChecker)
+REGISTER_CHECKER(CPlusPlus11LockChecker)
Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
===================================================================
--- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -749,6 +749,11 @@
   Dependencies<[SmartPtrModeling]>,
   Documentation<HasAlphaDocumentation>;
 
+def CPlusPlus11LockChecker : Checker<"CPlusPlus11Lock">,
+  HelpText<"Simple C++11 lock -> unlock checker">,
+  Dependencies<[PthreadLockBase]>,
+  Documentation<HasAlphaDocumentation>;
+
 } // end: "alpha.cplusplus"
 
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to