Hi,

Here are three patches for the pthread lock checker. The first detects
if a lock is unlocked twice. The second patch will make the checker warn
if a lock is destroyed twice or destroyed while being locked. The third
patch will warn if we initialize an already initialized mutex.

The first patch will use a set to track if a lock is already unlocked.
The set is changed in patch two to a map to have better knowledge about
the state of the lock.

The third patch does not track lck_mtx_init. The reason for that is that
I haven't found a good description on if is allowed to reinit a
destroyed lock, what happens if you call lck_mtx_init twice etc.

Any comments?

Cheers,
Daniel
>From cfb78e3e79e3e5a65945e7935a1ff2b64c38c721 Mon Sep 17 00:00:00 2001
From: Daniel Fahlgren <dan...@fahlgren.se>
Date: Sun, 30 Mar 2014 17:47:29 +0200
Subject: [PATCH 1/3] Add support to detect double unlock

We now detect if a lock previously is unlocked when unlocking it. If the
lock isn't previously seen we assume the unlock is ok.
---
 lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp |   53 +++++++----
 test/Analysis/pthreadlock.c                        |   96 ++++++++++++++++++++
 2 files changed, 132 insertions(+), 17 deletions(-)

diff --git a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index 5afa2d1..5616e4d 100644
--- a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -26,6 +26,7 @@ using namespace ento;
 namespace {
 class PthreadLockChecker : public Checker< check::PostStmt<CallExpr> > {
   mutable std::unique_ptr<BugType> BT_doublelock;
+  mutable std::unique_ptr<BugType> BT_doubleunlock;
   mutable std::unique_ptr<BugType> BT_lor;
   enum LockingSemantics {
     NotApplicable = 0,
@@ -45,6 +46,7 @@ public:
 // GDM Entry for tracking lock state.
 REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *)
 
+REGISTER_SET_WITH_PROGRAMSTATE(UnlockSet, const MemRegion *)
 
 void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
                                        CheckerContext &C) const {
@@ -144,6 +146,7 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
   
   // Record that the lock was acquired.  
   lockSucc = lockSucc->add<LockSet>(lockR);
+  lockSucc = lockSucc->remove<UnlockSet>(lockR);
   C.addTransition(lockSucc);
 }
 
@@ -155,32 +158,48 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
     return;
   
   ProgramStateRef state = C.getState();
-  LockSetTy LS = state->get<LockSet>();
 
-  // FIXME: Better analysis requires IPA for wrappers.
-  // FIXME: check for double unlocks
-  if (LS.isEmpty())
-    return;
-  
-  const MemRegion *firstLockR = LS.getHead();
-  if (firstLockR != lockR) {
-    if (!BT_lor)
-      BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker"));
+  if (state->contains<UnlockSet>(lockR)) {
+    if (!BT_doubleunlock)
+      BT_doubleunlock.reset(new BugType(this, "Double unlocking",
+                                              "Lock checker"));
     ExplodedNode *N = C.generateSink();
     if (!N)
       return;
-    BugReport *report = new BugReport(*BT_lor,
-                                               "This was not the most "
-                                               "recently acquired lock. "
-                                               "Possible lock order "
-                                               "reversal", N);
-    report->addRange(CE->getArg(0)->getSourceRange());
-    C.emitReport(report);
+    BugReport *Report = new BugReport(*BT_doubleunlock,
+                                                      "This lock has already "
+                                                      "been unlocked", N);
+    Report->addRange(CE->getArg(0)->getSourceRange());
+    C.emitReport(Report);
     return;
   }
 
+  LockSetTy LS = state->get<LockSet>();
+
+  // FIXME: Better analysis requires IPA for wrappers.
+
+  if (!LS.isEmpty()) {
+    const MemRegion *firstLockR = LS.getHead();
+    if (firstLockR != lockR) {
+      if (!BT_lor)
+        BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker"));
+      ExplodedNode *N = C.generateSink();
+      if (!N)
+        return;
+      BugReport *report = new BugReport(*BT_lor,
+                                                 "This was not the most "
+                                                 "recently acquired lock. "
+                                                 "Possible lock order "
+                                                 "reversal", N);
+      report->addRange(CE->getArg(0)->getSourceRange());
+      C.emitReport(report);
+      return;
+    }
+  }
+
   // Record that the lock was released. 
   state = state->set<LockSet>(LS.getTail());
+  state = state->add<UnlockSet>(lockR);
   C.addTransition(state);
 }
 
diff --git a/test/Analysis/pthreadlock.c b/test/Analysis/pthreadlock.c
index b904774..bc7ef66 100644
--- a/test/Analysis/pthreadlock.c
+++ b/test/Analysis/pthreadlock.c
@@ -69,6 +69,31 @@ ok7(void)
 }
 
 void
+ok8(void)
+{
+	pthread_mutex_lock(&mtx1);	// no-warning
+	pthread_mutex_lock(&mtx2);	// no-warning
+	pthread_mutex_unlock(&mtx2);	// no-warning
+	pthread_mutex_unlock(&mtx1);	// no-warning
+}
+
+void
+ok9(void)
+{
+	pthread_mutex_unlock(&mtx1);		// no-warning
+	if (pthread_mutex_trylock(&mtx1) == 0)	// no-warning
+		pthread_mutex_unlock(&mtx1);	// no-warning
+}
+
+void
+ok10(void)
+{
+	if (pthread_mutex_trylock(&mtx1) != 0)	// no-warning
+		pthread_mutex_lock(&mtx1);	// no-warning
+	pthread_mutex_unlock(&mtx1);		// no-warning
+}
+
+void
 bad1(void)
 {
 	pthread_mutex_lock(&mtx1);	// no-warning
@@ -135,3 +160,74 @@ bad8(void)
 	lck_mtx_lock(&lck2);		// no-warning
 	lck_mtx_unlock(&lck1);		// expected-warning{{This was not the most recently acquired lock}}
 }
+
+void
+bad9(void)
+{
+	lck_mtx_unlock(&lck1);		// no-warning
+	lck_mtx_unlock(&lck1);		// expected-warning{{This lock has already been unlocked}}
+}
+
+void
+bad10(void)
+{
+	lck_mtx_lock(&lck1);		// no-warning
+	lck_mtx_unlock(&lck1);		// no-warning
+	lck_mtx_unlock(&lck1);		// expected-warning{{This lock has already been unlocked}}
+}
+
+static void
+bad11_sub(pthread_mutex_t *lock)
+{
+	lck_mtx_unlock(lock);		// expected-warning{{This lock has already been unlocked}}
+}
+
+void
+bad11(int i)
+{
+	lck_mtx_lock(&lck1);		// no-warning
+	lck_mtx_unlock(&lck1);		// no-warning
+	if (i < 5)
+		bad11_sub(&lck1);
+}
+
+void
+bad12(void)
+{
+	pthread_mutex_lock(&mtx1);	// no-warning
+	pthread_mutex_unlock(&mtx1);	// no-warning
+	pthread_mutex_lock(&mtx1);	// no-warning
+	pthread_mutex_unlock(&mtx1);	// no-warning
+	pthread_mutex_unlock(&mtx1);	// expected-warning{{This lock has already been unlocked}}
+}
+
+void
+bad13(void)
+{
+	pthread_mutex_lock(&mtx1);	// no-warning
+	pthread_mutex_unlock(&mtx1);	// no-warning
+	pthread_mutex_lock(&mtx2);	// no-warning
+	pthread_mutex_unlock(&mtx2);	// no-warning
+	pthread_mutex_unlock(&mtx1);	// expected-warning{{This lock has already been unlocked}}
+}
+
+void
+bad14(void)
+{
+	pthread_mutex_lock(&mtx1);	// no-warning
+	pthread_mutex_lock(&mtx2);	// no-warning
+	pthread_mutex_unlock(&mtx2);	// no-warning
+	pthread_mutex_unlock(&mtx1);	// no-warning
+	pthread_mutex_unlock(&mtx2);	// expected-warning{{This lock has already been unlocked}}
+}
+
+void
+bad15(void)
+{
+	pthread_mutex_lock(&mtx1);	// no-warning
+	pthread_mutex_lock(&mtx2);	// no-warning
+	pthread_mutex_unlock(&mtx2);	// no-warning
+	pthread_mutex_unlock(&mtx1);	// no-warning
+	pthread_mutex_lock(&mtx1);	// no-warning
+	pthread_mutex_unlock(&mtx2);	// expected-warning{{This lock has already been unlocked}}
+}
-- 
1.7.9.5

>From 9ec019896af2b66e0cd55390927283429085e9c5 Mon Sep 17 00:00:00 2001
From: Daniel Fahlgren <dan...@fahlgren.se>
Date: Sun, 30 Mar 2014 17:49:59 +0200
Subject: [PATCH 2/3] Warn if a destroyed lock is used

Add support to warn if a lock has already been destroyed when using it.
Also warn if a locked lock is destroyed. Previously unseen locks are
assumed to be ok to destroy.
---
 lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp |  145 ++++++++++++++++----
 test/Analysis/pthreadlock.c                        |  100 ++++++++++++++
 2 files changed, 216 insertions(+), 29 deletions(-)

diff --git a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index 5616e4d..52a85c6 100644
--- a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -24,9 +24,32 @@ using namespace clang;
 using namespace ento;
 
 namespace {
+
+struct LockState {
+  enum Kind { Destroyed, Locked, Unlocked } K;
+
+  LockState(Kind K) : K(K) {}
+  static LockState getLocked(void) { return LockState(Locked); }
+  static LockState getUnlocked(void) { return LockState(Unlocked); }
+  static LockState getDestroyed(void) { return LockState(Destroyed); }
+
+  bool operator==(const LockState &X) const {
+    return K == X.K;
+  }
+
+  bool isLocked() const { return K == Locked; }
+  bool isUnlocked() const { return K == Unlocked; }
+  bool isDestroyed() const { return K == Destroyed; }
+
+  void Profile(llvm::FoldingSetNodeID &ID) const {
+    ID.AddInteger(K);
+  }
+};
+
 class PthreadLockChecker : public Checker< check::PostStmt<CallExpr> > {
   mutable std::unique_ptr<BugType> BT_doublelock;
   mutable std::unique_ptr<BugType> BT_doubleunlock;
+  mutable std::unique_ptr<BugType> BT_destroylock;
   mutable std::unique_ptr<BugType> BT_lor;
   enum LockingSemantics {
     NotApplicable = 0,
@@ -40,13 +63,15 @@ public:
                    bool isTryLock, enum LockingSemantics semantics) const;
     
   void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const;
+  void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
+  void ReportUseDestroyedBug(CheckerContext &C, const CallExpr *CE) const;
 };
 } // end anonymous namespace
 
 // GDM Entry for tracking lock state.
 REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *)
 
-REGISTER_SET_WITH_PROGRAMSTATE(UnlockSet, const MemRegion *)
+REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState)
 
 void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
                                        CheckerContext &C) const {
@@ -56,7 +81,7 @@ void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
   if (FName.empty())
     return;
 
-  if (CE->getNumArgs() != 1)
+  if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
     return;
 
   if (FName == "pthread_mutex_lock" ||
@@ -84,6 +109,9 @@ void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
            FName == "lck_mtx_unlock" ||
            FName == "lck_rw_done")
     ReleaseLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
+  else if (FName == "pthread_mutex_destroy" ||
+           FName == "lck_mtx_destroy")
+    DestroyLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
 }
 
 void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
@@ -102,18 +130,24 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
   
   DefinedSVal retVal = X.castAs<DefinedSVal>();
 
-  if (state->contains<LockSet>(lockR)) {
-    if (!BT_doublelock)
-      BT_doublelock.reset(new BugType(this, "Double locking", "Lock checker"));
-    ExplodedNode *N = C.generateSink();
-    if (!N)
+  if (const struct LockState *LState = state->get<LockMap>(lockR)) {
+    if (LState->isLocked()) {
+      if (!BT_doublelock)
+        BT_doublelock.reset(new BugType(this, "Double locking",
+                                              "Lock checker"));
+      ExplodedNode *N = C.generateSink();
+      if (!N)
+        return;
+      BugReport *report = new BugReport(*BT_doublelock,
+                                                        "This lock has already "
+                                                        "been acquired", N);
+      report->addRange(CE->getArg(0)->getSourceRange());
+      C.emitReport(report);
       return;
-    BugReport *report = new BugReport(*BT_doublelock,
-                                                      "This lock has already "
-                                                      "been acquired", N);
-    report->addRange(CE->getArg(0)->getSourceRange());
-    C.emitReport(report);
-    return;
+    } else if (LState->isDestroyed()) {
+      ReportUseDestroyedBug(C, CE);
+      return;
+    }
   }
 
   ProgramStateRef lockSucc = state;
@@ -146,7 +180,7 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
   
   // Record that the lock was acquired.  
   lockSucc = lockSucc->add<LockSet>(lockR);
-  lockSucc = lockSucc->remove<UnlockSet>(lockR);
+  lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked());
   C.addTransition(lockSucc);
 }
 
@@ -159,19 +193,24 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
   
   ProgramStateRef state = C.getState();
 
-  if (state->contains<UnlockSet>(lockR)) {
-    if (!BT_doubleunlock)
-      BT_doubleunlock.reset(new BugType(this, "Double unlocking",
-                                              "Lock checker"));
-    ExplodedNode *N = C.generateSink();
-    if (!N)
+  if (const struct LockState *LState = state->get<LockMap>(lockR)) {
+    if (LState->isUnlocked()) {
+      if (!BT_doubleunlock)
+        BT_doubleunlock.reset(new BugType(this, "Double unlocking",
+                                                "Lock checker"));
+      ExplodedNode *N = C.generateSink();
+      if (!N)
+        return;
+      BugReport *Report = new BugReport(*BT_doubleunlock,
+                                                        "This lock has already "
+                                                        "been unlocked", N);
+      Report->addRange(CE->getArg(0)->getSourceRange());
+      C.emitReport(Report);
       return;
-    BugReport *Report = new BugReport(*BT_doubleunlock,
-                                                      "This lock has already "
-                                                      "been unlocked", N);
-    Report->addRange(CE->getArg(0)->getSourceRange());
-    C.emitReport(Report);
-    return;
+    } else if (LState->isDestroyed()) {
+      ReportUseDestroyedBug(C, CE);
+      return;
+    }
   }
 
   LockSetTy LS = state->get<LockSet>();
@@ -195,14 +234,62 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
       C.emitReport(report);
       return;
     }
+    // Record that the lock was released. 
+    state = state->set<LockSet>(LS.getTail());
   }
 
-  // Record that the lock was released. 
-  state = state->set<LockSet>(LS.getTail());
-  state = state->add<UnlockSet>(lockR);
+  state = state->set<LockMap>(lockR, LockState::getUnlocked());
   C.addTransition(state);
 }
 
+void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE,
+                                     SVal Lock) const {
+
+  const MemRegion *LockR = Lock.getAsRegion();
+  if (!LockR)
+    return;
+
+  ProgramStateRef State = C.getState();
+
+  const struct LockState *LState = State->get<LockMap>(LockR);
+  if (!LState || LState->isUnlocked()) {
+    State = State->set<LockMap>(LockR, LockState::getDestroyed());
+    C.addTransition(State);
+    return;
+  }
+
+  StringRef Message;
+
+  if (LState->isLocked()) {
+    Message = "This lock is still locked";
+  } else {
+    Message = "This lock has already been destroyed";
+  }
+
+  if (!BT_destroylock)
+    BT_destroylock.reset(new BugType(this, "Destroy invalid lock", "Lock checker"));
+  ExplodedNode *N = C.generateSink();
+  if (!N)
+    return;
+  BugReport *Report = new BugReport(*BT_destroylock, Message, N);
+  Report->addRange(CE->getArg(0)->getSourceRange());
+  C.emitReport(Report);
+}
+
+void PthreadLockChecker::ReportUseDestroyedBug(CheckerContext &C,
+                                               const CallExpr *CE) const {
+  if (!BT_destroylock)
+    BT_destroylock.reset(new BugType(this, "Use destroyed lock",
+                                           "Lock checker"));
+  ExplodedNode *N = C.generateSink();
+  if (!N)
+    return;
+  BugReport *Report = new BugReport(*BT_destroylock, "This lock has already "
+                                                     "been destroyed", N);
+  Report->addRange(CE->getArg(0)->getSourceRange());
+  C.emitReport(Report);
+}
+
 void ento::registerPthreadLockChecker(CheckerManager &mgr) {
   mgr.registerChecker<PthreadLockChecker>();
 }
diff --git a/test/Analysis/pthreadlock.c b/test/Analysis/pthreadlock.c
index bc7ef66..6a75a6e 100644
--- a/test/Analysis/pthreadlock.c
+++ b/test/Analysis/pthreadlock.c
@@ -6,17 +6,24 @@ typedef struct {
 	void	*foo;
 } pthread_mutex_t;
 
+typedef struct {
+	void	*foo;
+} lck_grp_t;
+
 typedef pthread_mutex_t lck_mtx_t;
 
 extern int pthread_mutex_lock(pthread_mutex_t *);
 extern int pthread_mutex_unlock(pthread_mutex_t *);
 extern int pthread_mutex_trylock(pthread_mutex_t *);
+extern int pthread_mutex_destroy(pthread_mutex_t *);
 extern int lck_mtx_lock(lck_mtx_t *);
 extern int lck_mtx_unlock(lck_mtx_t *);
 extern int lck_mtx_try_lock(lck_mtx_t *);
+extern void lck_mtx_destroy(lck_mtx_t *lck, lck_grp_t *grp);
 
 pthread_mutex_t mtx1, mtx2;
 lck_mtx_t lck1, lck2;
+lck_grp_t grp1;
 
 void
 ok1(void)
@@ -94,6 +101,43 @@ ok10(void)
 }
 
 void
+ok11(void)
+{
+	pthread_mutex_destroy(&mtx1);	// no-warning
+}
+
+void
+ok12(void)
+{
+	pthread_mutex_destroy(&mtx1);	// no-warning
+	pthread_mutex_destroy(&mtx2);	// no-warning
+}
+
+void
+ok13(void)
+{
+	pthread_mutex_unlock(&mtx1);	// no-warning
+	pthread_mutex_destroy(&mtx1);	// no-warning
+}
+
+void
+ok14(void)
+{
+	pthread_mutex_unlock(&mtx1);	// no-warning
+	pthread_mutex_destroy(&mtx1);	// no-warning
+	pthread_mutex_unlock(&mtx2);	// no-warning
+	pthread_mutex_destroy(&mtx2);	// no-warning
+}
+
+void
+ok15(void)
+{
+	pthread_mutex_lock(&mtx1);	// no-warning
+	pthread_mutex_unlock(&mtx1);	// no-warning
+	pthread_mutex_destroy(&mtx1);	// no-warning
+}
+
+void
 bad1(void)
 {
 	pthread_mutex_lock(&mtx1);	// no-warning
@@ -231,3 +275,59 @@ bad15(void)
 	pthread_mutex_lock(&mtx1);	// no-warning
 	pthread_mutex_unlock(&mtx2);	// expected-warning{{This lock has already been unlocked}}
 }
+
+void
+bad16(void)
+{
+	pthread_mutex_destroy(&mtx1);	// no-warning
+	pthread_mutex_lock(&mtx1);	// expected-warning{{This lock has already been destroyed}}
+}
+
+void
+bad17(void)
+{
+	pthread_mutex_destroy(&mtx1);	// no-warning
+	pthread_mutex_unlock(&mtx1);	// expected-warning{{This lock has already been destroyed}}
+}
+
+void
+bad18(void)
+{
+	pthread_mutex_destroy(&mtx1);	// no-warning
+	pthread_mutex_destroy(&mtx1);	// expected-warning{{This lock has already been destroyed}}
+}
+
+void
+bad19(void)
+{
+	pthread_mutex_lock(&mtx1);	// no-warning
+	pthread_mutex_destroy(&mtx1);	// expected-warning{{This lock is still locked}}
+}
+
+void
+bad20(void)
+{
+	lck_mtx_destroy(&mtx1, &grp1);	// no-warning
+	lck_mtx_lock(&mtx1);		// expected-warning{{This lock has already been destroyed}}
+}
+
+void
+bad21(void)
+{
+	lck_mtx_destroy(&mtx1, &grp1);	// no-warning
+	lck_mtx_unlock(&mtx1);		// expected-warning{{This lock has already been destroyed}}
+}
+
+void
+bad22(void)
+{
+	lck_mtx_destroy(&mtx1, &grp1);	// no-warning
+	lck_mtx_destroy(&mtx1, &grp1);	// expected-warning{{This lock has already been destroyed}}
+}
+
+void
+bad23(void)
+{
+	lck_mtx_lock(&mtx1);		// no-warning
+	lck_mtx_destroy(&mtx1, &grp1);	// expected-warning{{This lock is still locked}}
+}
-- 
1.7.9.5

>From d1fd2c7d64c8951c9260bf93d676d4bdb6a994a3 Mon Sep 17 00:00:00 2001
From: Daniel Fahlgren <dan...@fahlgren.se>
Date: Sun, 30 Mar 2014 18:16:43 +0200
Subject: [PATCH 3/3] Track pthread_mutex_init

Emit a warning when we init an already initialized mutex. Also warn when
try to reinitialize a locked mutex.
---
 lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp |   39 ++++++++++++
 test/Analysis/pthreadlock.c                        |   67 ++++++++++++++++++++
 2 files changed, 106 insertions(+)

diff --git a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index 52a85c6..8c7aa20 100644
--- a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -50,6 +50,7 @@ class PthreadLockChecker : public Checker< check::PostStmt<CallExpr> > {
   mutable std::unique_ptr<BugType> BT_doublelock;
   mutable std::unique_ptr<BugType> BT_doubleunlock;
   mutable std::unique_ptr<BugType> BT_destroylock;
+  mutable std::unique_ptr<BugType> BT_initlock;
   mutable std::unique_ptr<BugType> BT_lor;
   enum LockingSemantics {
     NotApplicable = 0,
@@ -64,6 +65,7 @@ public:
     
   void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const;
   void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
+  void InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
   void ReportUseDestroyedBug(CheckerContext &C, const CallExpr *CE) const;
 };
 } // end anonymous namespace
@@ -112,6 +114,8 @@ void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
   else if (FName == "pthread_mutex_destroy" ||
            FName == "lck_mtx_destroy")
     DestroyLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
+  else if (FName == "pthread_mutex_init")
+    InitLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
 }
 
 void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
@@ -276,6 +280,41 @@ void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE,
   C.emitReport(Report);
 }
 
+void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE,
+                                  SVal Lock) const {
+
+  const MemRegion *LockR = Lock.getAsRegion();
+  if (!LockR)
+    return;
+
+  ProgramStateRef State = C.getState();
+
+  const struct LockState *LState = State->get<LockMap>(LockR);
+  if (!LState || LState->isDestroyed()) {
+    State = State->set<LockMap>(LockR, LockState::getUnlocked());
+    C.addTransition(State);
+    return;
+  }
+
+  StringRef Message;
+
+  if (LState->isLocked()) {
+    Message = "This lock is still being held";
+  } else {
+    Message = "This lock has already been initialized";
+  }
+
+  if (!BT_initlock)
+    BT_initlock.reset(new BugType(this, "Init invalid lock",
+                                        "Lock checker"));
+  ExplodedNode *N = C.generateSink();
+  if (!N)
+    return;
+  BugReport *Report = new BugReport(*BT_initlock, Message, N);
+  Report->addRange(CE->getArg(0)->getSourceRange());
+  C.emitReport(Report);
+}
+
 void PthreadLockChecker::ReportUseDestroyedBug(CheckerContext &C,
                                                const CallExpr *CE) const {
   if (!BT_destroylock)
diff --git a/test/Analysis/pthreadlock.c b/test/Analysis/pthreadlock.c
index 6a75a6e..2a59e0f 100644
--- a/test/Analysis/pthreadlock.c
+++ b/test/Analysis/pthreadlock.c
@@ -8,6 +8,10 @@ typedef struct {
 
 typedef struct {
 	void	*foo;
+} pthread_mutexattr_t;
+
+typedef struct {
+	void	*foo;
 } lck_grp_t;
 
 typedef pthread_mutex_t lck_mtx_t;
@@ -16,6 +20,7 @@ extern int pthread_mutex_lock(pthread_mutex_t *);
 extern int pthread_mutex_unlock(pthread_mutex_t *);
 extern int pthread_mutex_trylock(pthread_mutex_t *);
 extern int pthread_mutex_destroy(pthread_mutex_t *);
+extern int pthread_mutex_init(pthread_mutex_t  *mutex, const pthread_mutexattr_t *mutexattr);
 extern int lck_mtx_lock(lck_mtx_t *);
 extern int lck_mtx_unlock(lck_mtx_t *);
 extern int lck_mtx_try_lock(lck_mtx_t *);
@@ -25,6 +30,8 @@ pthread_mutex_t mtx1, mtx2;
 lck_mtx_t lck1, lck2;
 lck_grp_t grp1;
 
+#define NULL 0
+
 void
 ok1(void)
 {
@@ -138,6 +145,45 @@ ok15(void)
 }
 
 void
+ok16(void)
+{
+	pthread_mutex_init(&mtx1, NULL);	// no-warning
+}
+
+void
+ok17(void)
+{
+	pthread_mutex_init(&mtx1, NULL);	// no-warning
+	pthread_mutex_init(&mtx2, NULL);	// no-warning
+}
+
+void
+ok18(void)
+{
+	pthread_mutex_destroy(&mtx1);		// no-warning
+	pthread_mutex_init(&mtx1, NULL);	// no-warning
+}
+
+void
+ok19(void)
+{
+	pthread_mutex_destroy(&mtx1);		// no-warning
+	pthread_mutex_init(&mtx1, NULL);	// no-warning
+	pthread_mutex_destroy(&mtx2);		// no-warning
+	pthread_mutex_init(&mtx2, NULL);	// no-warning
+}
+
+void
+ok20(void)
+{
+	pthread_mutex_unlock(&mtx1);		// no-warning
+	pthread_mutex_destroy(&mtx1);		// no-warning
+	pthread_mutex_init(&mtx1, NULL);	// no-warning
+	pthread_mutex_destroy(&mtx1);		// no-warning
+	pthread_mutex_init(&mtx1, NULL);	// no-warning
+}
+
+void
 bad1(void)
 {
 	pthread_mutex_lock(&mtx1);	// no-warning
@@ -331,3 +377,24 @@ bad23(void)
 	lck_mtx_lock(&mtx1);		// no-warning
 	lck_mtx_destroy(&mtx1, &grp1);	// expected-warning{{This lock is still locked}}
 }
+
+void
+bad24(void)
+{
+	pthread_mutex_init(&mtx1, NULL);	// no-warning
+	pthread_mutex_init(&mtx1, NULL);	// expected-warning{{This lock has already been initialized}}
+}
+
+void
+bad25(void)
+{
+	pthread_mutex_lock(&mtx1);		// no-warning
+	pthread_mutex_init(&mtx1, NULL);	// expected-warning{{This lock is still being held}}
+}
+
+void
+bad26(void)
+{
+	pthread_mutex_unlock(&mtx1);		// no-warning
+	pthread_mutex_init(&mtx1, NULL);	// expected-warning{{This lock has already been initialized}}
+}
-- 
1.7.9.5

_______________________________________________
cfe-commits mailing list
cfe-commits@cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to