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