diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2014-11-24 09:15:30 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2014-11-24 09:15:30 +0000 |
commit | 9f4dbff6669c8037f3b036bcf580d14f1a4f12a5 (patch) | |
tree | 47df2c12b57214af6c31e47404b005675b8b7ffc /lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp | |
parent | f73d5f23a889b93d89ddef61ac0995df40286bb8 (diff) | |
download | src-9f4dbff6669c8037f3b036bcf580d14f1a4f12a5.tar.gz src-9f4dbff6669c8037f3b036bcf580d14f1a4f12a5.zip |
Vendor import of clang RELEASE_350/final tag r216957 (effectively, 3.5.0 release):vendor/clang/clang-release_350-r216957
Notes
Notes:
svn path=/vendor/clang/dist/; revision=274958
svn path=/vendor/clang/clang-release_350-r216957/; revision=274959; tag=vendor/clang/clang-release_350-r216957
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp | 223 |
1 files changed, 186 insertions, 37 deletions
diff --git a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp index ffb8cf20207b..1ede3a2a5126 100644 --- a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -24,9 +24,37 @@ using namespace clang; using namespace ento; namespace { + +struct LockState { + enum Kind { Destroyed, Locked, Unlocked } K; + +private: + LockState(Kind K) : K(K) {} + +public: + 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 OwningPtr<BugType> BT_doublelock; - mutable OwningPtr<BugType> BT_lor; + 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, PthreadSemantics, @@ -39,12 +67,16 @@ 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 InitLock(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_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState) void PthreadLockChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { @@ -54,7 +86,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" || @@ -69,7 +101,7 @@ void PthreadLockChecker::checkPostStmt(const CallExpr *CE, false, XNUSemantics); else if (FName == "pthread_mutex_trylock" || FName == "pthread_rwlock_tryrdlock" || - FName == "pthread_rwlock_tryrwlock") + FName == "pthread_rwlock_trywrlock") AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), true, PthreadSemantics); else if (FName == "lck_mtx_try_lock" || @@ -82,6 +114,11 @@ 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)); + else if (FName == "pthread_mutex_init") + InitLock(C, CE, state->getSVal(CE->getArg(0), LCtx)); } void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, @@ -100,18 +137,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("Double locking", "Lock checker")); - ExplodedNode *N = C.generateSink(); - if (!N) + if (const 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; @@ -120,10 +163,10 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, ProgramStateRef lockFail; switch (semantics) { case PthreadSemantics: - llvm::tie(lockFail, lockSucc) = state->assume(retVal); + std::tie(lockFail, lockSucc) = state->assume(retVal); break; case XNUSemantics: - llvm::tie(lockSucc, lockFail) = state->assume(retVal); + std::tie(lockSucc, lockFail) = state->assume(retVal); break; default: llvm_unreachable("Unknown tryLock locking semantics"); @@ -144,6 +187,7 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, // Record that the lock was acquired. lockSucc = lockSucc->add<LockSet>(lockR); + lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked()); C.addTransition(lockSucc); } @@ -155,35 +199,140 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, return; ProgramStateRef state = C.getState(); + + if (const 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; + } else if (LState->isDestroyed()) { + reportUseDestroyedBug(C, CE); + return; + } + } + 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("Lock order reversal", "Lock checker")); - ExplodedNode *N = C.generateSink(); - if (!N) + + 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; - 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()); } - // Record that the lock was released. - state = state->set<LockSet>(LS.getTail()); + 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 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::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) + 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>(); |