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