xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp (revision a7dea1671b87c07d2d266f836bfa8b58efc7c134)
10b57cec5SDimitry Andric //===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This defines PthreadLockChecker, a simple lock -> unlock checker.
100b57cec5SDimitry Andric // Also handles XNU locks, which behave similarly enough to share code.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
150b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
160b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
200b57cec5SDimitry Andric 
210b57cec5SDimitry Andric using namespace clang;
220b57cec5SDimitry Andric using namespace ento;
230b57cec5SDimitry Andric 
240b57cec5SDimitry Andric namespace {
250b57cec5SDimitry Andric 
260b57cec5SDimitry Andric struct LockState {
270b57cec5SDimitry Andric   enum Kind {
280b57cec5SDimitry Andric     Destroyed,
290b57cec5SDimitry Andric     Locked,
300b57cec5SDimitry Andric     Unlocked,
310b57cec5SDimitry Andric     UntouchedAndPossiblyDestroyed,
320b57cec5SDimitry Andric     UnlockedAndPossiblyDestroyed
330b57cec5SDimitry Andric   } K;
340b57cec5SDimitry Andric 
350b57cec5SDimitry Andric private:
360b57cec5SDimitry Andric   LockState(Kind K) : K(K) {}
370b57cec5SDimitry Andric 
380b57cec5SDimitry Andric public:
390b57cec5SDimitry Andric   static LockState getLocked() { return LockState(Locked); }
400b57cec5SDimitry Andric   static LockState getUnlocked() { return LockState(Unlocked); }
410b57cec5SDimitry Andric   static LockState getDestroyed() { return LockState(Destroyed); }
420b57cec5SDimitry Andric   static LockState getUntouchedAndPossiblyDestroyed() {
430b57cec5SDimitry Andric     return LockState(UntouchedAndPossiblyDestroyed);
440b57cec5SDimitry Andric   }
450b57cec5SDimitry Andric   static LockState getUnlockedAndPossiblyDestroyed() {
460b57cec5SDimitry Andric     return LockState(UnlockedAndPossiblyDestroyed);
470b57cec5SDimitry Andric   }
480b57cec5SDimitry Andric 
490b57cec5SDimitry Andric   bool operator==(const LockState &X) const {
500b57cec5SDimitry Andric     return K == X.K;
510b57cec5SDimitry Andric   }
520b57cec5SDimitry Andric 
530b57cec5SDimitry Andric   bool isLocked() const { return K == Locked; }
540b57cec5SDimitry Andric   bool isUnlocked() const { return K == Unlocked; }
550b57cec5SDimitry Andric   bool isDestroyed() const { return K == Destroyed; }
560b57cec5SDimitry Andric   bool isUntouchedAndPossiblyDestroyed() const {
570b57cec5SDimitry Andric     return K == UntouchedAndPossiblyDestroyed;
580b57cec5SDimitry Andric   }
590b57cec5SDimitry Andric   bool isUnlockedAndPossiblyDestroyed() const {
600b57cec5SDimitry Andric     return K == UnlockedAndPossiblyDestroyed;
610b57cec5SDimitry Andric   }
620b57cec5SDimitry Andric 
630b57cec5SDimitry Andric   void Profile(llvm::FoldingSetNodeID &ID) const {
640b57cec5SDimitry Andric     ID.AddInteger(K);
650b57cec5SDimitry Andric   }
660b57cec5SDimitry Andric };
670b57cec5SDimitry Andric 
680b57cec5SDimitry Andric class PthreadLockChecker
690b57cec5SDimitry Andric     : public Checker<check::PostStmt<CallExpr>, check::DeadSymbols> {
700b57cec5SDimitry Andric   mutable std::unique_ptr<BugType> BT_doublelock;
710b57cec5SDimitry Andric   mutable std::unique_ptr<BugType> BT_doubleunlock;
720b57cec5SDimitry Andric   mutable std::unique_ptr<BugType> BT_destroylock;
730b57cec5SDimitry Andric   mutable std::unique_ptr<BugType> BT_initlock;
740b57cec5SDimitry Andric   mutable std::unique_ptr<BugType> BT_lor;
750b57cec5SDimitry Andric   enum LockingSemantics {
760b57cec5SDimitry Andric     NotApplicable = 0,
770b57cec5SDimitry Andric     PthreadSemantics,
780b57cec5SDimitry Andric     XNUSemantics
790b57cec5SDimitry Andric   };
800b57cec5SDimitry Andric public:
810b57cec5SDimitry Andric   void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
820b57cec5SDimitry Andric   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
830b57cec5SDimitry Andric   void printState(raw_ostream &Out, ProgramStateRef State,
840b57cec5SDimitry Andric                   const char *NL, const char *Sep) const override;
850b57cec5SDimitry Andric 
860b57cec5SDimitry Andric   void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock,
870b57cec5SDimitry Andric                    bool isTryLock, enum LockingSemantics semantics) const;
880b57cec5SDimitry Andric 
890b57cec5SDimitry Andric   void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const;
900b57cec5SDimitry Andric   void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock,
910b57cec5SDimitry Andric                    enum LockingSemantics semantics) const;
920b57cec5SDimitry Andric   void InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
930b57cec5SDimitry Andric   void reportUseDestroyedBug(CheckerContext &C, const CallExpr *CE) const;
940b57cec5SDimitry Andric   ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state,
950b57cec5SDimitry Andric                                                 const MemRegion *lockR,
960b57cec5SDimitry Andric                                                 const SymbolRef *sym) const;
970b57cec5SDimitry Andric };
980b57cec5SDimitry Andric } // end anonymous namespace
990b57cec5SDimitry Andric 
1000b57cec5SDimitry Andric // A stack of locks for tracking lock-unlock order.
1010b57cec5SDimitry Andric REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *)
1020b57cec5SDimitry Andric 
1030b57cec5SDimitry Andric // An entry for tracking lock states.
1040b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState)
1050b57cec5SDimitry Andric 
1060b57cec5SDimitry Andric // Return values for unresolved calls to pthread_mutex_destroy().
1070b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(DestroyRetVal, const MemRegion *, SymbolRef)
1080b57cec5SDimitry Andric 
1090b57cec5SDimitry Andric void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
1100b57cec5SDimitry Andric                                        CheckerContext &C) const {
1110b57cec5SDimitry Andric   StringRef FName = C.getCalleeName(CE);
1120b57cec5SDimitry Andric   if (FName.empty())
1130b57cec5SDimitry Andric     return;
1140b57cec5SDimitry Andric 
1150b57cec5SDimitry Andric   if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
1160b57cec5SDimitry Andric     return;
1170b57cec5SDimitry Andric 
1180b57cec5SDimitry Andric   if (FName == "pthread_mutex_lock" ||
1190b57cec5SDimitry Andric       FName == "pthread_rwlock_rdlock" ||
1200b57cec5SDimitry Andric       FName == "pthread_rwlock_wrlock")
1210b57cec5SDimitry Andric     AcquireLock(C, CE, C.getSVal(CE->getArg(0)), false, PthreadSemantics);
1220b57cec5SDimitry Andric   else if (FName == "lck_mtx_lock" ||
1230b57cec5SDimitry Andric            FName == "lck_rw_lock_exclusive" ||
1240b57cec5SDimitry Andric            FName == "lck_rw_lock_shared")
1250b57cec5SDimitry Andric     AcquireLock(C, CE, C.getSVal(CE->getArg(0)), false, XNUSemantics);
1260b57cec5SDimitry Andric   else if (FName == "pthread_mutex_trylock" ||
1270b57cec5SDimitry Andric            FName == "pthread_rwlock_tryrdlock" ||
1280b57cec5SDimitry Andric            FName == "pthread_rwlock_trywrlock")
1290b57cec5SDimitry Andric     AcquireLock(C, CE, C.getSVal(CE->getArg(0)),
1300b57cec5SDimitry Andric                 true, PthreadSemantics);
1310b57cec5SDimitry Andric   else if (FName == "lck_mtx_try_lock" ||
1320b57cec5SDimitry Andric            FName == "lck_rw_try_lock_exclusive" ||
1330b57cec5SDimitry Andric            FName == "lck_rw_try_lock_shared")
1340b57cec5SDimitry Andric     AcquireLock(C, CE, C.getSVal(CE->getArg(0)), true, XNUSemantics);
1350b57cec5SDimitry Andric   else if (FName == "pthread_mutex_unlock" ||
1360b57cec5SDimitry Andric            FName == "pthread_rwlock_unlock" ||
1370b57cec5SDimitry Andric            FName == "lck_mtx_unlock" ||
1380b57cec5SDimitry Andric            FName == "lck_rw_done")
1390b57cec5SDimitry Andric     ReleaseLock(C, CE, C.getSVal(CE->getArg(0)));
1400b57cec5SDimitry Andric   else if (FName == "pthread_mutex_destroy")
1410b57cec5SDimitry Andric     DestroyLock(C, CE, C.getSVal(CE->getArg(0)), PthreadSemantics);
1420b57cec5SDimitry Andric   else if (FName == "lck_mtx_destroy")
1430b57cec5SDimitry Andric     DestroyLock(C, CE, C.getSVal(CE->getArg(0)), XNUSemantics);
1440b57cec5SDimitry Andric   else if (FName == "pthread_mutex_init")
1450b57cec5SDimitry Andric     InitLock(C, CE, C.getSVal(CE->getArg(0)));
1460b57cec5SDimitry Andric }
1470b57cec5SDimitry Andric 
1480b57cec5SDimitry Andric // When a lock is destroyed, in some semantics(like PthreadSemantics) we are not
1490b57cec5SDimitry Andric // sure if the destroy call has succeeded or failed, and the lock enters one of
1500b57cec5SDimitry Andric // the 'possibly destroyed' state. There is a short time frame for the
1510b57cec5SDimitry Andric // programmer to check the return value to see if the lock was successfully
1520b57cec5SDimitry Andric // destroyed. Before we model the next operation over that lock, we call this
1530b57cec5SDimitry Andric // function to see if the return value was checked by now and set the lock state
1540b57cec5SDimitry Andric // - either to destroyed state or back to its previous state.
1550b57cec5SDimitry Andric 
1560b57cec5SDimitry Andric // In PthreadSemantics, pthread_mutex_destroy() returns zero if the lock is
1570b57cec5SDimitry Andric // successfully destroyed and it returns a non-zero value otherwise.
1580b57cec5SDimitry Andric ProgramStateRef PthreadLockChecker::resolvePossiblyDestroyedMutex(
1590b57cec5SDimitry Andric     ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const {
1600b57cec5SDimitry Andric   const LockState *lstate = state->get<LockMap>(lockR);
1610b57cec5SDimitry Andric   // Existence in DestroyRetVal ensures existence in LockMap.
1620b57cec5SDimitry Andric   // Existence in Destroyed also ensures that the lock state for lockR is either
1630b57cec5SDimitry Andric   // UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed.
1640b57cec5SDimitry Andric   assert(lstate->isUntouchedAndPossiblyDestroyed() ||
1650b57cec5SDimitry Andric          lstate->isUnlockedAndPossiblyDestroyed());
1660b57cec5SDimitry Andric 
1670b57cec5SDimitry Andric   ConstraintManager &CMgr = state->getConstraintManager();
1680b57cec5SDimitry Andric   ConditionTruthVal retZero = CMgr.isNull(state, *sym);
1690b57cec5SDimitry Andric   if (retZero.isConstrainedFalse()) {
1700b57cec5SDimitry Andric     if (lstate->isUntouchedAndPossiblyDestroyed())
1710b57cec5SDimitry Andric       state = state->remove<LockMap>(lockR);
1720b57cec5SDimitry Andric     else if (lstate->isUnlockedAndPossiblyDestroyed())
1730b57cec5SDimitry Andric       state = state->set<LockMap>(lockR, LockState::getUnlocked());
1740b57cec5SDimitry Andric   } else
1750b57cec5SDimitry Andric     state = state->set<LockMap>(lockR, LockState::getDestroyed());
1760b57cec5SDimitry Andric 
1770b57cec5SDimitry Andric   // Removing the map entry (lockR, sym) from DestroyRetVal as the lock state is
1780b57cec5SDimitry Andric   // now resolved.
1790b57cec5SDimitry Andric   state = state->remove<DestroyRetVal>(lockR);
1800b57cec5SDimitry Andric   return state;
1810b57cec5SDimitry Andric }
1820b57cec5SDimitry Andric 
1830b57cec5SDimitry Andric void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State,
1840b57cec5SDimitry Andric                                     const char *NL, const char *Sep) const {
1850b57cec5SDimitry Andric   LockMapTy LM = State->get<LockMap>();
1860b57cec5SDimitry Andric   if (!LM.isEmpty()) {
1870b57cec5SDimitry Andric     Out << Sep << "Mutex states:" << NL;
1880b57cec5SDimitry Andric     for (auto I : LM) {
1890b57cec5SDimitry Andric       I.first->dumpToStream(Out);
1900b57cec5SDimitry Andric       if (I.second.isLocked())
1910b57cec5SDimitry Andric         Out << ": locked";
1920b57cec5SDimitry Andric       else if (I.second.isUnlocked())
1930b57cec5SDimitry Andric         Out << ": unlocked";
1940b57cec5SDimitry Andric       else if (I.second.isDestroyed())
1950b57cec5SDimitry Andric         Out << ": destroyed";
1960b57cec5SDimitry Andric       else if (I.second.isUntouchedAndPossiblyDestroyed())
1970b57cec5SDimitry Andric         Out << ": not tracked, possibly destroyed";
1980b57cec5SDimitry Andric       else if (I.second.isUnlockedAndPossiblyDestroyed())
1990b57cec5SDimitry Andric         Out << ": unlocked, possibly destroyed";
2000b57cec5SDimitry Andric       Out << NL;
2010b57cec5SDimitry Andric     }
2020b57cec5SDimitry Andric   }
2030b57cec5SDimitry Andric 
2040b57cec5SDimitry Andric   LockSetTy LS = State->get<LockSet>();
2050b57cec5SDimitry Andric   if (!LS.isEmpty()) {
2060b57cec5SDimitry Andric     Out << Sep << "Mutex lock order:" << NL;
2070b57cec5SDimitry Andric     for (auto I: LS) {
2080b57cec5SDimitry Andric       I->dumpToStream(Out);
2090b57cec5SDimitry Andric       Out << NL;
2100b57cec5SDimitry Andric     }
2110b57cec5SDimitry Andric   }
2120b57cec5SDimitry Andric 
2130b57cec5SDimitry Andric   // TODO: Dump destroyed mutex symbols?
2140b57cec5SDimitry Andric }
2150b57cec5SDimitry Andric 
2160b57cec5SDimitry Andric void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
2170b57cec5SDimitry Andric                                      SVal lock, bool isTryLock,
2180b57cec5SDimitry Andric                                      enum LockingSemantics semantics) const {
2190b57cec5SDimitry Andric 
2200b57cec5SDimitry Andric   const MemRegion *lockR = lock.getAsRegion();
2210b57cec5SDimitry Andric   if (!lockR)
2220b57cec5SDimitry Andric     return;
2230b57cec5SDimitry Andric 
2240b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
2250b57cec5SDimitry Andric   const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
2260b57cec5SDimitry Andric   if (sym)
2270b57cec5SDimitry Andric     state = resolvePossiblyDestroyedMutex(state, lockR, sym);
2280b57cec5SDimitry Andric 
2290b57cec5SDimitry Andric   SVal X = C.getSVal(CE);
2300b57cec5SDimitry Andric   if (X.isUnknownOrUndef())
2310b57cec5SDimitry Andric     return;
2320b57cec5SDimitry Andric 
2330b57cec5SDimitry Andric   DefinedSVal retVal = X.castAs<DefinedSVal>();
2340b57cec5SDimitry Andric 
2350b57cec5SDimitry Andric   if (const LockState *LState = state->get<LockMap>(lockR)) {
2360b57cec5SDimitry Andric     if (LState->isLocked()) {
2370b57cec5SDimitry Andric       if (!BT_doublelock)
2380b57cec5SDimitry Andric         BT_doublelock.reset(new BugType(this, "Double locking",
2390b57cec5SDimitry Andric                                         "Lock checker"));
2400b57cec5SDimitry Andric       ExplodedNode *N = C.generateErrorNode();
2410b57cec5SDimitry Andric       if (!N)
2420b57cec5SDimitry Andric         return;
243*a7dea167SDimitry Andric       auto report = std::make_unique<PathSensitiveBugReport>(
2440b57cec5SDimitry Andric           *BT_doublelock, "This lock has already been acquired", N);
2450b57cec5SDimitry Andric       report->addRange(CE->getArg(0)->getSourceRange());
2460b57cec5SDimitry Andric       C.emitReport(std::move(report));
2470b57cec5SDimitry Andric       return;
2480b57cec5SDimitry Andric     } else if (LState->isDestroyed()) {
2490b57cec5SDimitry Andric       reportUseDestroyedBug(C, CE);
2500b57cec5SDimitry Andric       return;
2510b57cec5SDimitry Andric     }
2520b57cec5SDimitry Andric   }
2530b57cec5SDimitry Andric 
2540b57cec5SDimitry Andric   ProgramStateRef lockSucc = state;
2550b57cec5SDimitry Andric   if (isTryLock) {
2560b57cec5SDimitry Andric     // Bifurcate the state, and allow a mode where the lock acquisition fails.
2570b57cec5SDimitry Andric     ProgramStateRef lockFail;
2580b57cec5SDimitry Andric     switch (semantics) {
2590b57cec5SDimitry Andric     case PthreadSemantics:
2600b57cec5SDimitry Andric       std::tie(lockFail, lockSucc) = state->assume(retVal);
2610b57cec5SDimitry Andric       break;
2620b57cec5SDimitry Andric     case XNUSemantics:
2630b57cec5SDimitry Andric       std::tie(lockSucc, lockFail) = state->assume(retVal);
2640b57cec5SDimitry Andric       break;
2650b57cec5SDimitry Andric     default:
2660b57cec5SDimitry Andric       llvm_unreachable("Unknown tryLock locking semantics");
2670b57cec5SDimitry Andric     }
2680b57cec5SDimitry Andric     assert(lockFail && lockSucc);
2690b57cec5SDimitry Andric     C.addTransition(lockFail);
2700b57cec5SDimitry Andric 
2710b57cec5SDimitry Andric   } else if (semantics == PthreadSemantics) {
2720b57cec5SDimitry Andric     // Assume that the return value was 0.
2730b57cec5SDimitry Andric     lockSucc = state->assume(retVal, false);
2740b57cec5SDimitry Andric     assert(lockSucc);
2750b57cec5SDimitry Andric 
2760b57cec5SDimitry Andric   } else {
2770b57cec5SDimitry Andric     // XNU locking semantics return void on non-try locks
2780b57cec5SDimitry Andric     assert((semantics == XNUSemantics) && "Unknown locking semantics");
2790b57cec5SDimitry Andric     lockSucc = state;
2800b57cec5SDimitry Andric   }
2810b57cec5SDimitry Andric 
2820b57cec5SDimitry Andric   // Record that the lock was acquired.
2830b57cec5SDimitry Andric   lockSucc = lockSucc->add<LockSet>(lockR);
2840b57cec5SDimitry Andric   lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked());
2850b57cec5SDimitry Andric   C.addTransition(lockSucc);
2860b57cec5SDimitry Andric }
2870b57cec5SDimitry Andric 
2880b57cec5SDimitry Andric void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
2890b57cec5SDimitry Andric                                      SVal lock) const {
2900b57cec5SDimitry Andric 
2910b57cec5SDimitry Andric   const MemRegion *lockR = lock.getAsRegion();
2920b57cec5SDimitry Andric   if (!lockR)
2930b57cec5SDimitry Andric     return;
2940b57cec5SDimitry Andric 
2950b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
2960b57cec5SDimitry Andric   const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
2970b57cec5SDimitry Andric   if (sym)
2980b57cec5SDimitry Andric     state = resolvePossiblyDestroyedMutex(state, lockR, sym);
2990b57cec5SDimitry Andric 
3000b57cec5SDimitry Andric   if (const LockState *LState = state->get<LockMap>(lockR)) {
3010b57cec5SDimitry Andric     if (LState->isUnlocked()) {
3020b57cec5SDimitry Andric       if (!BT_doubleunlock)
3030b57cec5SDimitry Andric         BT_doubleunlock.reset(new BugType(this, "Double unlocking",
3040b57cec5SDimitry Andric                                           "Lock checker"));
3050b57cec5SDimitry Andric       ExplodedNode *N = C.generateErrorNode();
3060b57cec5SDimitry Andric       if (!N)
3070b57cec5SDimitry Andric         return;
308*a7dea167SDimitry Andric       auto Report = std::make_unique<PathSensitiveBugReport>(
3090b57cec5SDimitry Andric           *BT_doubleunlock, "This lock has already been unlocked", N);
3100b57cec5SDimitry Andric       Report->addRange(CE->getArg(0)->getSourceRange());
3110b57cec5SDimitry Andric       C.emitReport(std::move(Report));
3120b57cec5SDimitry Andric       return;
3130b57cec5SDimitry Andric     } else if (LState->isDestroyed()) {
3140b57cec5SDimitry Andric       reportUseDestroyedBug(C, CE);
3150b57cec5SDimitry Andric       return;
3160b57cec5SDimitry Andric     }
3170b57cec5SDimitry Andric   }
3180b57cec5SDimitry Andric 
3190b57cec5SDimitry Andric   LockSetTy LS = state->get<LockSet>();
3200b57cec5SDimitry Andric 
3210b57cec5SDimitry Andric   // FIXME: Better analysis requires IPA for wrappers.
3220b57cec5SDimitry Andric 
3230b57cec5SDimitry Andric   if (!LS.isEmpty()) {
3240b57cec5SDimitry Andric     const MemRegion *firstLockR = LS.getHead();
3250b57cec5SDimitry Andric     if (firstLockR != lockR) {
3260b57cec5SDimitry Andric       if (!BT_lor)
3270b57cec5SDimitry Andric         BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker"));
3280b57cec5SDimitry Andric       ExplodedNode *N = C.generateErrorNode();
3290b57cec5SDimitry Andric       if (!N)
3300b57cec5SDimitry Andric         return;
331*a7dea167SDimitry Andric       auto report = std::make_unique<PathSensitiveBugReport>(
3320b57cec5SDimitry Andric           *BT_lor, "This was not the most recently acquired lock. Possible "
3330b57cec5SDimitry Andric                    "lock order reversal", N);
3340b57cec5SDimitry Andric       report->addRange(CE->getArg(0)->getSourceRange());
3350b57cec5SDimitry Andric       C.emitReport(std::move(report));
3360b57cec5SDimitry Andric       return;
3370b57cec5SDimitry Andric     }
3380b57cec5SDimitry Andric     // Record that the lock was released.
3390b57cec5SDimitry Andric     state = state->set<LockSet>(LS.getTail());
3400b57cec5SDimitry Andric   }
3410b57cec5SDimitry Andric 
3420b57cec5SDimitry Andric   state = state->set<LockMap>(lockR, LockState::getUnlocked());
3430b57cec5SDimitry Andric   C.addTransition(state);
3440b57cec5SDimitry Andric }
3450b57cec5SDimitry Andric 
3460b57cec5SDimitry Andric void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE,
3470b57cec5SDimitry Andric                                      SVal Lock,
3480b57cec5SDimitry Andric                                      enum LockingSemantics semantics) const {
3490b57cec5SDimitry Andric 
3500b57cec5SDimitry Andric   const MemRegion *LockR = Lock.getAsRegion();
3510b57cec5SDimitry Andric   if (!LockR)
3520b57cec5SDimitry Andric     return;
3530b57cec5SDimitry Andric 
3540b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
3550b57cec5SDimitry Andric 
3560b57cec5SDimitry Andric   const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
3570b57cec5SDimitry Andric   if (sym)
3580b57cec5SDimitry Andric     State = resolvePossiblyDestroyedMutex(State, LockR, sym);
3590b57cec5SDimitry Andric 
3600b57cec5SDimitry Andric   const LockState *LState = State->get<LockMap>(LockR);
3610b57cec5SDimitry Andric   // Checking the return value of the destroy method only in the case of
3620b57cec5SDimitry Andric   // PthreadSemantics
3630b57cec5SDimitry Andric   if (semantics == PthreadSemantics) {
3640b57cec5SDimitry Andric     if (!LState || LState->isUnlocked()) {
3650b57cec5SDimitry Andric       SymbolRef sym = C.getSVal(CE).getAsSymbol();
3660b57cec5SDimitry Andric       if (!sym) {
3670b57cec5SDimitry Andric         State = State->remove<LockMap>(LockR);
3680b57cec5SDimitry Andric         C.addTransition(State);
3690b57cec5SDimitry Andric         return;
3700b57cec5SDimitry Andric       }
3710b57cec5SDimitry Andric       State = State->set<DestroyRetVal>(LockR, sym);
3720b57cec5SDimitry Andric       if (LState && LState->isUnlocked())
3730b57cec5SDimitry Andric         State = State->set<LockMap>(
3740b57cec5SDimitry Andric             LockR, LockState::getUnlockedAndPossiblyDestroyed());
3750b57cec5SDimitry Andric       else
3760b57cec5SDimitry Andric         State = State->set<LockMap>(
3770b57cec5SDimitry Andric             LockR, LockState::getUntouchedAndPossiblyDestroyed());
3780b57cec5SDimitry Andric       C.addTransition(State);
3790b57cec5SDimitry Andric       return;
3800b57cec5SDimitry Andric     }
3810b57cec5SDimitry Andric   } else {
3820b57cec5SDimitry Andric     if (!LState || LState->isUnlocked()) {
3830b57cec5SDimitry Andric       State = State->set<LockMap>(LockR, LockState::getDestroyed());
3840b57cec5SDimitry Andric       C.addTransition(State);
3850b57cec5SDimitry Andric       return;
3860b57cec5SDimitry Andric     }
3870b57cec5SDimitry Andric   }
3880b57cec5SDimitry Andric   StringRef Message;
3890b57cec5SDimitry Andric 
3900b57cec5SDimitry Andric   if (LState->isLocked()) {
3910b57cec5SDimitry Andric     Message = "This lock is still locked";
3920b57cec5SDimitry Andric   } else {
3930b57cec5SDimitry Andric     Message = "This lock has already been destroyed";
3940b57cec5SDimitry Andric   }
3950b57cec5SDimitry Andric 
3960b57cec5SDimitry Andric   if (!BT_destroylock)
3970b57cec5SDimitry Andric     BT_destroylock.reset(new BugType(this, "Destroy invalid lock",
3980b57cec5SDimitry Andric                                      "Lock checker"));
3990b57cec5SDimitry Andric   ExplodedNode *N = C.generateErrorNode();
4000b57cec5SDimitry Andric   if (!N)
4010b57cec5SDimitry Andric     return;
402*a7dea167SDimitry Andric   auto Report =
403*a7dea167SDimitry Andric       std::make_unique<PathSensitiveBugReport>(*BT_destroylock, Message, N);
4040b57cec5SDimitry Andric   Report->addRange(CE->getArg(0)->getSourceRange());
4050b57cec5SDimitry Andric   C.emitReport(std::move(Report));
4060b57cec5SDimitry Andric }
4070b57cec5SDimitry Andric 
4080b57cec5SDimitry Andric void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE,
4090b57cec5SDimitry Andric                                   SVal Lock) const {
4100b57cec5SDimitry Andric 
4110b57cec5SDimitry Andric   const MemRegion *LockR = Lock.getAsRegion();
4120b57cec5SDimitry Andric   if (!LockR)
4130b57cec5SDimitry Andric     return;
4140b57cec5SDimitry Andric 
4150b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
4160b57cec5SDimitry Andric 
4170b57cec5SDimitry Andric   const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
4180b57cec5SDimitry Andric   if (sym)
4190b57cec5SDimitry Andric     State = resolvePossiblyDestroyedMutex(State, LockR, sym);
4200b57cec5SDimitry Andric 
4210b57cec5SDimitry Andric   const struct LockState *LState = State->get<LockMap>(LockR);
4220b57cec5SDimitry Andric   if (!LState || LState->isDestroyed()) {
4230b57cec5SDimitry Andric     State = State->set<LockMap>(LockR, LockState::getUnlocked());
4240b57cec5SDimitry Andric     C.addTransition(State);
4250b57cec5SDimitry Andric     return;
4260b57cec5SDimitry Andric   }
4270b57cec5SDimitry Andric 
4280b57cec5SDimitry Andric   StringRef Message;
4290b57cec5SDimitry Andric 
4300b57cec5SDimitry Andric   if (LState->isLocked()) {
4310b57cec5SDimitry Andric     Message = "This lock is still being held";
4320b57cec5SDimitry Andric   } else {
4330b57cec5SDimitry Andric     Message = "This lock has already been initialized";
4340b57cec5SDimitry Andric   }
4350b57cec5SDimitry Andric 
4360b57cec5SDimitry Andric   if (!BT_initlock)
4370b57cec5SDimitry Andric     BT_initlock.reset(new BugType(this, "Init invalid lock",
4380b57cec5SDimitry Andric                                   "Lock checker"));
4390b57cec5SDimitry Andric   ExplodedNode *N = C.generateErrorNode();
4400b57cec5SDimitry Andric   if (!N)
4410b57cec5SDimitry Andric     return;
442*a7dea167SDimitry Andric   auto Report =
443*a7dea167SDimitry Andric       std::make_unique<PathSensitiveBugReport>(*BT_initlock, Message, N);
4440b57cec5SDimitry Andric   Report->addRange(CE->getArg(0)->getSourceRange());
4450b57cec5SDimitry Andric   C.emitReport(std::move(Report));
4460b57cec5SDimitry Andric }
4470b57cec5SDimitry Andric 
4480b57cec5SDimitry Andric void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C,
4490b57cec5SDimitry Andric                                                const CallExpr *CE) const {
4500b57cec5SDimitry Andric   if (!BT_destroylock)
4510b57cec5SDimitry Andric     BT_destroylock.reset(new BugType(this, "Use destroyed lock",
4520b57cec5SDimitry Andric                                      "Lock checker"));
4530b57cec5SDimitry Andric   ExplodedNode *N = C.generateErrorNode();
4540b57cec5SDimitry Andric   if (!N)
4550b57cec5SDimitry Andric     return;
456*a7dea167SDimitry Andric   auto Report = std::make_unique<PathSensitiveBugReport>(
4570b57cec5SDimitry Andric       *BT_destroylock, "This lock has already been destroyed", N);
4580b57cec5SDimitry Andric   Report->addRange(CE->getArg(0)->getSourceRange());
4590b57cec5SDimitry Andric   C.emitReport(std::move(Report));
4600b57cec5SDimitry Andric }
4610b57cec5SDimitry Andric 
4620b57cec5SDimitry Andric void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper,
4630b57cec5SDimitry Andric                                           CheckerContext &C) const {
4640b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
4650b57cec5SDimitry Andric 
4660b57cec5SDimitry Andric   // TODO: Clean LockMap when a mutex region dies.
4670b57cec5SDimitry Andric 
4680b57cec5SDimitry Andric   DestroyRetValTy TrackedSymbols = State->get<DestroyRetVal>();
4690b57cec5SDimitry Andric   for (DestroyRetValTy::iterator I = TrackedSymbols.begin(),
4700b57cec5SDimitry Andric                                  E = TrackedSymbols.end();
4710b57cec5SDimitry Andric        I != E; ++I) {
4720b57cec5SDimitry Andric     const SymbolRef Sym = I->second;
4730b57cec5SDimitry Andric     const MemRegion *lockR = I->first;
4740b57cec5SDimitry Andric     bool IsSymDead = SymReaper.isDead(Sym);
4750b57cec5SDimitry Andric     // Remove the dead symbol from the return value symbols map.
4760b57cec5SDimitry Andric     if (IsSymDead)
4770b57cec5SDimitry Andric       State = resolvePossiblyDestroyedMutex(State, lockR, &Sym);
4780b57cec5SDimitry Andric   }
4790b57cec5SDimitry Andric   C.addTransition(State);
4800b57cec5SDimitry Andric }
4810b57cec5SDimitry Andric 
4820b57cec5SDimitry Andric void ento::registerPthreadLockChecker(CheckerManager &mgr) {
4830b57cec5SDimitry Andric   mgr.registerChecker<PthreadLockChecker>();
4840b57cec5SDimitry Andric }
4850b57cec5SDimitry Andric 
4860b57cec5SDimitry Andric bool ento::shouldRegisterPthreadLockChecker(const LangOptions &LO) {
4870b57cec5SDimitry Andric   return true;
4880b57cec5SDimitry Andric }
489