1*0b57cec5SDimitry Andric //===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // This defines PthreadLockChecker, a simple lock -> unlock checker. 10*0b57cec5SDimitry Andric // Also handles XNU locks, which behave similarly enough to share code. 11*0b57cec5SDimitry Andric // 12*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 13*0b57cec5SDimitry Andric 14*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 15*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 16*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 17*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 18*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 19*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 20*0b57cec5SDimitry Andric 21*0b57cec5SDimitry Andric using namespace clang; 22*0b57cec5SDimitry Andric using namespace ento; 23*0b57cec5SDimitry Andric 24*0b57cec5SDimitry Andric namespace { 25*0b57cec5SDimitry Andric 26*0b57cec5SDimitry Andric struct LockState { 27*0b57cec5SDimitry Andric enum Kind { 28*0b57cec5SDimitry Andric Destroyed, 29*0b57cec5SDimitry Andric Locked, 30*0b57cec5SDimitry Andric Unlocked, 31*0b57cec5SDimitry Andric UntouchedAndPossiblyDestroyed, 32*0b57cec5SDimitry Andric UnlockedAndPossiblyDestroyed 33*0b57cec5SDimitry Andric } K; 34*0b57cec5SDimitry Andric 35*0b57cec5SDimitry Andric private: 36*0b57cec5SDimitry Andric LockState(Kind K) : K(K) {} 37*0b57cec5SDimitry Andric 38*0b57cec5SDimitry Andric public: 39*0b57cec5SDimitry Andric static LockState getLocked() { return LockState(Locked); } 40*0b57cec5SDimitry Andric static LockState getUnlocked() { return LockState(Unlocked); } 41*0b57cec5SDimitry Andric static LockState getDestroyed() { return LockState(Destroyed); } 42*0b57cec5SDimitry Andric static LockState getUntouchedAndPossiblyDestroyed() { 43*0b57cec5SDimitry Andric return LockState(UntouchedAndPossiblyDestroyed); 44*0b57cec5SDimitry Andric } 45*0b57cec5SDimitry Andric static LockState getUnlockedAndPossiblyDestroyed() { 46*0b57cec5SDimitry Andric return LockState(UnlockedAndPossiblyDestroyed); 47*0b57cec5SDimitry Andric } 48*0b57cec5SDimitry Andric 49*0b57cec5SDimitry Andric bool operator==(const LockState &X) const { 50*0b57cec5SDimitry Andric return K == X.K; 51*0b57cec5SDimitry Andric } 52*0b57cec5SDimitry Andric 53*0b57cec5SDimitry Andric bool isLocked() const { return K == Locked; } 54*0b57cec5SDimitry Andric bool isUnlocked() const { return K == Unlocked; } 55*0b57cec5SDimitry Andric bool isDestroyed() const { return K == Destroyed; } 56*0b57cec5SDimitry Andric bool isUntouchedAndPossiblyDestroyed() const { 57*0b57cec5SDimitry Andric return K == UntouchedAndPossiblyDestroyed; 58*0b57cec5SDimitry Andric } 59*0b57cec5SDimitry Andric bool isUnlockedAndPossiblyDestroyed() const { 60*0b57cec5SDimitry Andric return K == UnlockedAndPossiblyDestroyed; 61*0b57cec5SDimitry Andric } 62*0b57cec5SDimitry Andric 63*0b57cec5SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const { 64*0b57cec5SDimitry Andric ID.AddInteger(K); 65*0b57cec5SDimitry Andric } 66*0b57cec5SDimitry Andric }; 67*0b57cec5SDimitry Andric 68*0b57cec5SDimitry Andric class PthreadLockChecker 69*0b57cec5SDimitry Andric : public Checker<check::PostStmt<CallExpr>, check::DeadSymbols> { 70*0b57cec5SDimitry Andric mutable std::unique_ptr<BugType> BT_doublelock; 71*0b57cec5SDimitry Andric mutable std::unique_ptr<BugType> BT_doubleunlock; 72*0b57cec5SDimitry Andric mutable std::unique_ptr<BugType> BT_destroylock; 73*0b57cec5SDimitry Andric mutable std::unique_ptr<BugType> BT_initlock; 74*0b57cec5SDimitry Andric mutable std::unique_ptr<BugType> BT_lor; 75*0b57cec5SDimitry Andric enum LockingSemantics { 76*0b57cec5SDimitry Andric NotApplicable = 0, 77*0b57cec5SDimitry Andric PthreadSemantics, 78*0b57cec5SDimitry Andric XNUSemantics 79*0b57cec5SDimitry Andric }; 80*0b57cec5SDimitry Andric public: 81*0b57cec5SDimitry Andric void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; 82*0b57cec5SDimitry Andric void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 83*0b57cec5SDimitry Andric void printState(raw_ostream &Out, ProgramStateRef State, 84*0b57cec5SDimitry Andric const char *NL, const char *Sep) const override; 85*0b57cec5SDimitry Andric 86*0b57cec5SDimitry Andric void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock, 87*0b57cec5SDimitry Andric bool isTryLock, enum LockingSemantics semantics) const; 88*0b57cec5SDimitry Andric 89*0b57cec5SDimitry Andric void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const; 90*0b57cec5SDimitry Andric void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock, 91*0b57cec5SDimitry Andric enum LockingSemantics semantics) const; 92*0b57cec5SDimitry Andric void InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const; 93*0b57cec5SDimitry Andric void reportUseDestroyedBug(CheckerContext &C, const CallExpr *CE) const; 94*0b57cec5SDimitry Andric ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state, 95*0b57cec5SDimitry Andric const MemRegion *lockR, 96*0b57cec5SDimitry Andric const SymbolRef *sym) const; 97*0b57cec5SDimitry Andric }; 98*0b57cec5SDimitry Andric } // end anonymous namespace 99*0b57cec5SDimitry Andric 100*0b57cec5SDimitry Andric // A stack of locks for tracking lock-unlock order. 101*0b57cec5SDimitry Andric REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *) 102*0b57cec5SDimitry Andric 103*0b57cec5SDimitry Andric // An entry for tracking lock states. 104*0b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState) 105*0b57cec5SDimitry Andric 106*0b57cec5SDimitry Andric // Return values for unresolved calls to pthread_mutex_destroy(). 107*0b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(DestroyRetVal, const MemRegion *, SymbolRef) 108*0b57cec5SDimitry Andric 109*0b57cec5SDimitry Andric void PthreadLockChecker::checkPostStmt(const CallExpr *CE, 110*0b57cec5SDimitry Andric CheckerContext &C) const { 111*0b57cec5SDimitry Andric StringRef FName = C.getCalleeName(CE); 112*0b57cec5SDimitry Andric if (FName.empty()) 113*0b57cec5SDimitry Andric return; 114*0b57cec5SDimitry Andric 115*0b57cec5SDimitry Andric if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2) 116*0b57cec5SDimitry Andric return; 117*0b57cec5SDimitry Andric 118*0b57cec5SDimitry Andric if (FName == "pthread_mutex_lock" || 119*0b57cec5SDimitry Andric FName == "pthread_rwlock_rdlock" || 120*0b57cec5SDimitry Andric FName == "pthread_rwlock_wrlock") 121*0b57cec5SDimitry Andric AcquireLock(C, CE, C.getSVal(CE->getArg(0)), false, PthreadSemantics); 122*0b57cec5SDimitry Andric else if (FName == "lck_mtx_lock" || 123*0b57cec5SDimitry Andric FName == "lck_rw_lock_exclusive" || 124*0b57cec5SDimitry Andric FName == "lck_rw_lock_shared") 125*0b57cec5SDimitry Andric AcquireLock(C, CE, C.getSVal(CE->getArg(0)), false, XNUSemantics); 126*0b57cec5SDimitry Andric else if (FName == "pthread_mutex_trylock" || 127*0b57cec5SDimitry Andric FName == "pthread_rwlock_tryrdlock" || 128*0b57cec5SDimitry Andric FName == "pthread_rwlock_trywrlock") 129*0b57cec5SDimitry Andric AcquireLock(C, CE, C.getSVal(CE->getArg(0)), 130*0b57cec5SDimitry Andric true, PthreadSemantics); 131*0b57cec5SDimitry Andric else if (FName == "lck_mtx_try_lock" || 132*0b57cec5SDimitry Andric FName == "lck_rw_try_lock_exclusive" || 133*0b57cec5SDimitry Andric FName == "lck_rw_try_lock_shared") 134*0b57cec5SDimitry Andric AcquireLock(C, CE, C.getSVal(CE->getArg(0)), true, XNUSemantics); 135*0b57cec5SDimitry Andric else if (FName == "pthread_mutex_unlock" || 136*0b57cec5SDimitry Andric FName == "pthread_rwlock_unlock" || 137*0b57cec5SDimitry Andric FName == "lck_mtx_unlock" || 138*0b57cec5SDimitry Andric FName == "lck_rw_done") 139*0b57cec5SDimitry Andric ReleaseLock(C, CE, C.getSVal(CE->getArg(0))); 140*0b57cec5SDimitry Andric else if (FName == "pthread_mutex_destroy") 141*0b57cec5SDimitry Andric DestroyLock(C, CE, C.getSVal(CE->getArg(0)), PthreadSemantics); 142*0b57cec5SDimitry Andric else if (FName == "lck_mtx_destroy") 143*0b57cec5SDimitry Andric DestroyLock(C, CE, C.getSVal(CE->getArg(0)), XNUSemantics); 144*0b57cec5SDimitry Andric else if (FName == "pthread_mutex_init") 145*0b57cec5SDimitry Andric InitLock(C, CE, C.getSVal(CE->getArg(0))); 146*0b57cec5SDimitry Andric } 147*0b57cec5SDimitry Andric 148*0b57cec5SDimitry Andric // When a lock is destroyed, in some semantics(like PthreadSemantics) we are not 149*0b57cec5SDimitry Andric // sure if the destroy call has succeeded or failed, and the lock enters one of 150*0b57cec5SDimitry Andric // the 'possibly destroyed' state. There is a short time frame for the 151*0b57cec5SDimitry Andric // programmer to check the return value to see if the lock was successfully 152*0b57cec5SDimitry Andric // destroyed. Before we model the next operation over that lock, we call this 153*0b57cec5SDimitry Andric // function to see if the return value was checked by now and set the lock state 154*0b57cec5SDimitry Andric // - either to destroyed state or back to its previous state. 155*0b57cec5SDimitry Andric 156*0b57cec5SDimitry Andric // In PthreadSemantics, pthread_mutex_destroy() returns zero if the lock is 157*0b57cec5SDimitry Andric // successfully destroyed and it returns a non-zero value otherwise. 158*0b57cec5SDimitry Andric ProgramStateRef PthreadLockChecker::resolvePossiblyDestroyedMutex( 159*0b57cec5SDimitry Andric ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const { 160*0b57cec5SDimitry Andric const LockState *lstate = state->get<LockMap>(lockR); 161*0b57cec5SDimitry Andric // Existence in DestroyRetVal ensures existence in LockMap. 162*0b57cec5SDimitry Andric // Existence in Destroyed also ensures that the lock state for lockR is either 163*0b57cec5SDimitry Andric // UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed. 164*0b57cec5SDimitry Andric assert(lstate->isUntouchedAndPossiblyDestroyed() || 165*0b57cec5SDimitry Andric lstate->isUnlockedAndPossiblyDestroyed()); 166*0b57cec5SDimitry Andric 167*0b57cec5SDimitry Andric ConstraintManager &CMgr = state->getConstraintManager(); 168*0b57cec5SDimitry Andric ConditionTruthVal retZero = CMgr.isNull(state, *sym); 169*0b57cec5SDimitry Andric if (retZero.isConstrainedFalse()) { 170*0b57cec5SDimitry Andric if (lstate->isUntouchedAndPossiblyDestroyed()) 171*0b57cec5SDimitry Andric state = state->remove<LockMap>(lockR); 172*0b57cec5SDimitry Andric else if (lstate->isUnlockedAndPossiblyDestroyed()) 173*0b57cec5SDimitry Andric state = state->set<LockMap>(lockR, LockState::getUnlocked()); 174*0b57cec5SDimitry Andric } else 175*0b57cec5SDimitry Andric state = state->set<LockMap>(lockR, LockState::getDestroyed()); 176*0b57cec5SDimitry Andric 177*0b57cec5SDimitry Andric // Removing the map entry (lockR, sym) from DestroyRetVal as the lock state is 178*0b57cec5SDimitry Andric // now resolved. 179*0b57cec5SDimitry Andric state = state->remove<DestroyRetVal>(lockR); 180*0b57cec5SDimitry Andric return state; 181*0b57cec5SDimitry Andric } 182*0b57cec5SDimitry Andric 183*0b57cec5SDimitry Andric void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State, 184*0b57cec5SDimitry Andric const char *NL, const char *Sep) const { 185*0b57cec5SDimitry Andric LockMapTy LM = State->get<LockMap>(); 186*0b57cec5SDimitry Andric if (!LM.isEmpty()) { 187*0b57cec5SDimitry Andric Out << Sep << "Mutex states:" << NL; 188*0b57cec5SDimitry Andric for (auto I : LM) { 189*0b57cec5SDimitry Andric I.first->dumpToStream(Out); 190*0b57cec5SDimitry Andric if (I.second.isLocked()) 191*0b57cec5SDimitry Andric Out << ": locked"; 192*0b57cec5SDimitry Andric else if (I.second.isUnlocked()) 193*0b57cec5SDimitry Andric Out << ": unlocked"; 194*0b57cec5SDimitry Andric else if (I.second.isDestroyed()) 195*0b57cec5SDimitry Andric Out << ": destroyed"; 196*0b57cec5SDimitry Andric else if (I.second.isUntouchedAndPossiblyDestroyed()) 197*0b57cec5SDimitry Andric Out << ": not tracked, possibly destroyed"; 198*0b57cec5SDimitry Andric else if (I.second.isUnlockedAndPossiblyDestroyed()) 199*0b57cec5SDimitry Andric Out << ": unlocked, possibly destroyed"; 200*0b57cec5SDimitry Andric Out << NL; 201*0b57cec5SDimitry Andric } 202*0b57cec5SDimitry Andric } 203*0b57cec5SDimitry Andric 204*0b57cec5SDimitry Andric LockSetTy LS = State->get<LockSet>(); 205*0b57cec5SDimitry Andric if (!LS.isEmpty()) { 206*0b57cec5SDimitry Andric Out << Sep << "Mutex lock order:" << NL; 207*0b57cec5SDimitry Andric for (auto I: LS) { 208*0b57cec5SDimitry Andric I->dumpToStream(Out); 209*0b57cec5SDimitry Andric Out << NL; 210*0b57cec5SDimitry Andric } 211*0b57cec5SDimitry Andric } 212*0b57cec5SDimitry Andric 213*0b57cec5SDimitry Andric // TODO: Dump destroyed mutex symbols? 214*0b57cec5SDimitry Andric } 215*0b57cec5SDimitry Andric 216*0b57cec5SDimitry Andric void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, 217*0b57cec5SDimitry Andric SVal lock, bool isTryLock, 218*0b57cec5SDimitry Andric enum LockingSemantics semantics) const { 219*0b57cec5SDimitry Andric 220*0b57cec5SDimitry Andric const MemRegion *lockR = lock.getAsRegion(); 221*0b57cec5SDimitry Andric if (!lockR) 222*0b57cec5SDimitry Andric return; 223*0b57cec5SDimitry Andric 224*0b57cec5SDimitry Andric ProgramStateRef state = C.getState(); 225*0b57cec5SDimitry Andric const SymbolRef *sym = state->get<DestroyRetVal>(lockR); 226*0b57cec5SDimitry Andric if (sym) 227*0b57cec5SDimitry Andric state = resolvePossiblyDestroyedMutex(state, lockR, sym); 228*0b57cec5SDimitry Andric 229*0b57cec5SDimitry Andric SVal X = C.getSVal(CE); 230*0b57cec5SDimitry Andric if (X.isUnknownOrUndef()) 231*0b57cec5SDimitry Andric return; 232*0b57cec5SDimitry Andric 233*0b57cec5SDimitry Andric DefinedSVal retVal = X.castAs<DefinedSVal>(); 234*0b57cec5SDimitry Andric 235*0b57cec5SDimitry Andric if (const LockState *LState = state->get<LockMap>(lockR)) { 236*0b57cec5SDimitry Andric if (LState->isLocked()) { 237*0b57cec5SDimitry Andric if (!BT_doublelock) 238*0b57cec5SDimitry Andric BT_doublelock.reset(new BugType(this, "Double locking", 239*0b57cec5SDimitry Andric "Lock checker")); 240*0b57cec5SDimitry Andric ExplodedNode *N = C.generateErrorNode(); 241*0b57cec5SDimitry Andric if (!N) 242*0b57cec5SDimitry Andric return; 243*0b57cec5SDimitry Andric auto report = llvm::make_unique<BugReport>( 244*0b57cec5SDimitry Andric *BT_doublelock, "This lock has already been acquired", N); 245*0b57cec5SDimitry Andric report->addRange(CE->getArg(0)->getSourceRange()); 246*0b57cec5SDimitry Andric C.emitReport(std::move(report)); 247*0b57cec5SDimitry Andric return; 248*0b57cec5SDimitry Andric } else if (LState->isDestroyed()) { 249*0b57cec5SDimitry Andric reportUseDestroyedBug(C, CE); 250*0b57cec5SDimitry Andric return; 251*0b57cec5SDimitry Andric } 252*0b57cec5SDimitry Andric } 253*0b57cec5SDimitry Andric 254*0b57cec5SDimitry Andric ProgramStateRef lockSucc = state; 255*0b57cec5SDimitry Andric if (isTryLock) { 256*0b57cec5SDimitry Andric // Bifurcate the state, and allow a mode where the lock acquisition fails. 257*0b57cec5SDimitry Andric ProgramStateRef lockFail; 258*0b57cec5SDimitry Andric switch (semantics) { 259*0b57cec5SDimitry Andric case PthreadSemantics: 260*0b57cec5SDimitry Andric std::tie(lockFail, lockSucc) = state->assume(retVal); 261*0b57cec5SDimitry Andric break; 262*0b57cec5SDimitry Andric case XNUSemantics: 263*0b57cec5SDimitry Andric std::tie(lockSucc, lockFail) = state->assume(retVal); 264*0b57cec5SDimitry Andric break; 265*0b57cec5SDimitry Andric default: 266*0b57cec5SDimitry Andric llvm_unreachable("Unknown tryLock locking semantics"); 267*0b57cec5SDimitry Andric } 268*0b57cec5SDimitry Andric assert(lockFail && lockSucc); 269*0b57cec5SDimitry Andric C.addTransition(lockFail); 270*0b57cec5SDimitry Andric 271*0b57cec5SDimitry Andric } else if (semantics == PthreadSemantics) { 272*0b57cec5SDimitry Andric // Assume that the return value was 0. 273*0b57cec5SDimitry Andric lockSucc = state->assume(retVal, false); 274*0b57cec5SDimitry Andric assert(lockSucc); 275*0b57cec5SDimitry Andric 276*0b57cec5SDimitry Andric } else { 277*0b57cec5SDimitry Andric // XNU locking semantics return void on non-try locks 278*0b57cec5SDimitry Andric assert((semantics == XNUSemantics) && "Unknown locking semantics"); 279*0b57cec5SDimitry Andric lockSucc = state; 280*0b57cec5SDimitry Andric } 281*0b57cec5SDimitry Andric 282*0b57cec5SDimitry Andric // Record that the lock was acquired. 283*0b57cec5SDimitry Andric lockSucc = lockSucc->add<LockSet>(lockR); 284*0b57cec5SDimitry Andric lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked()); 285*0b57cec5SDimitry Andric C.addTransition(lockSucc); 286*0b57cec5SDimitry Andric } 287*0b57cec5SDimitry Andric 288*0b57cec5SDimitry Andric void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, 289*0b57cec5SDimitry Andric SVal lock) const { 290*0b57cec5SDimitry Andric 291*0b57cec5SDimitry Andric const MemRegion *lockR = lock.getAsRegion(); 292*0b57cec5SDimitry Andric if (!lockR) 293*0b57cec5SDimitry Andric return; 294*0b57cec5SDimitry Andric 295*0b57cec5SDimitry Andric ProgramStateRef state = C.getState(); 296*0b57cec5SDimitry Andric const SymbolRef *sym = state->get<DestroyRetVal>(lockR); 297*0b57cec5SDimitry Andric if (sym) 298*0b57cec5SDimitry Andric state = resolvePossiblyDestroyedMutex(state, lockR, sym); 299*0b57cec5SDimitry Andric 300*0b57cec5SDimitry Andric if (const LockState *LState = state->get<LockMap>(lockR)) { 301*0b57cec5SDimitry Andric if (LState->isUnlocked()) { 302*0b57cec5SDimitry Andric if (!BT_doubleunlock) 303*0b57cec5SDimitry Andric BT_doubleunlock.reset(new BugType(this, "Double unlocking", 304*0b57cec5SDimitry Andric "Lock checker")); 305*0b57cec5SDimitry Andric ExplodedNode *N = C.generateErrorNode(); 306*0b57cec5SDimitry Andric if (!N) 307*0b57cec5SDimitry Andric return; 308*0b57cec5SDimitry Andric auto Report = llvm::make_unique<BugReport>( 309*0b57cec5SDimitry Andric *BT_doubleunlock, "This lock has already been unlocked", N); 310*0b57cec5SDimitry Andric Report->addRange(CE->getArg(0)->getSourceRange()); 311*0b57cec5SDimitry Andric C.emitReport(std::move(Report)); 312*0b57cec5SDimitry Andric return; 313*0b57cec5SDimitry Andric } else if (LState->isDestroyed()) { 314*0b57cec5SDimitry Andric reportUseDestroyedBug(C, CE); 315*0b57cec5SDimitry Andric return; 316*0b57cec5SDimitry Andric } 317*0b57cec5SDimitry Andric } 318*0b57cec5SDimitry Andric 319*0b57cec5SDimitry Andric LockSetTy LS = state->get<LockSet>(); 320*0b57cec5SDimitry Andric 321*0b57cec5SDimitry Andric // FIXME: Better analysis requires IPA for wrappers. 322*0b57cec5SDimitry Andric 323*0b57cec5SDimitry Andric if (!LS.isEmpty()) { 324*0b57cec5SDimitry Andric const MemRegion *firstLockR = LS.getHead(); 325*0b57cec5SDimitry Andric if (firstLockR != lockR) { 326*0b57cec5SDimitry Andric if (!BT_lor) 327*0b57cec5SDimitry Andric BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker")); 328*0b57cec5SDimitry Andric ExplodedNode *N = C.generateErrorNode(); 329*0b57cec5SDimitry Andric if (!N) 330*0b57cec5SDimitry Andric return; 331*0b57cec5SDimitry Andric auto report = llvm::make_unique<BugReport>( 332*0b57cec5SDimitry Andric *BT_lor, "This was not the most recently acquired lock. Possible " 333*0b57cec5SDimitry Andric "lock order reversal", N); 334*0b57cec5SDimitry Andric report->addRange(CE->getArg(0)->getSourceRange()); 335*0b57cec5SDimitry Andric C.emitReport(std::move(report)); 336*0b57cec5SDimitry Andric return; 337*0b57cec5SDimitry Andric } 338*0b57cec5SDimitry Andric // Record that the lock was released. 339*0b57cec5SDimitry Andric state = state->set<LockSet>(LS.getTail()); 340*0b57cec5SDimitry Andric } 341*0b57cec5SDimitry Andric 342*0b57cec5SDimitry Andric state = state->set<LockMap>(lockR, LockState::getUnlocked()); 343*0b57cec5SDimitry Andric C.addTransition(state); 344*0b57cec5SDimitry Andric } 345*0b57cec5SDimitry Andric 346*0b57cec5SDimitry Andric void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE, 347*0b57cec5SDimitry Andric SVal Lock, 348*0b57cec5SDimitry Andric enum LockingSemantics semantics) const { 349*0b57cec5SDimitry Andric 350*0b57cec5SDimitry Andric const MemRegion *LockR = Lock.getAsRegion(); 351*0b57cec5SDimitry Andric if (!LockR) 352*0b57cec5SDimitry Andric return; 353*0b57cec5SDimitry Andric 354*0b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 355*0b57cec5SDimitry Andric 356*0b57cec5SDimitry Andric const SymbolRef *sym = State->get<DestroyRetVal>(LockR); 357*0b57cec5SDimitry Andric if (sym) 358*0b57cec5SDimitry Andric State = resolvePossiblyDestroyedMutex(State, LockR, sym); 359*0b57cec5SDimitry Andric 360*0b57cec5SDimitry Andric const LockState *LState = State->get<LockMap>(LockR); 361*0b57cec5SDimitry Andric // Checking the return value of the destroy method only in the case of 362*0b57cec5SDimitry Andric // PthreadSemantics 363*0b57cec5SDimitry Andric if (semantics == PthreadSemantics) { 364*0b57cec5SDimitry Andric if (!LState || LState->isUnlocked()) { 365*0b57cec5SDimitry Andric SymbolRef sym = C.getSVal(CE).getAsSymbol(); 366*0b57cec5SDimitry Andric if (!sym) { 367*0b57cec5SDimitry Andric State = State->remove<LockMap>(LockR); 368*0b57cec5SDimitry Andric C.addTransition(State); 369*0b57cec5SDimitry Andric return; 370*0b57cec5SDimitry Andric } 371*0b57cec5SDimitry Andric State = State->set<DestroyRetVal>(LockR, sym); 372*0b57cec5SDimitry Andric if (LState && LState->isUnlocked()) 373*0b57cec5SDimitry Andric State = State->set<LockMap>( 374*0b57cec5SDimitry Andric LockR, LockState::getUnlockedAndPossiblyDestroyed()); 375*0b57cec5SDimitry Andric else 376*0b57cec5SDimitry Andric State = State->set<LockMap>( 377*0b57cec5SDimitry Andric LockR, LockState::getUntouchedAndPossiblyDestroyed()); 378*0b57cec5SDimitry Andric C.addTransition(State); 379*0b57cec5SDimitry Andric return; 380*0b57cec5SDimitry Andric } 381*0b57cec5SDimitry Andric } else { 382*0b57cec5SDimitry Andric if (!LState || LState->isUnlocked()) { 383*0b57cec5SDimitry Andric State = State->set<LockMap>(LockR, LockState::getDestroyed()); 384*0b57cec5SDimitry Andric C.addTransition(State); 385*0b57cec5SDimitry Andric return; 386*0b57cec5SDimitry Andric } 387*0b57cec5SDimitry Andric } 388*0b57cec5SDimitry Andric StringRef Message; 389*0b57cec5SDimitry Andric 390*0b57cec5SDimitry Andric if (LState->isLocked()) { 391*0b57cec5SDimitry Andric Message = "This lock is still locked"; 392*0b57cec5SDimitry Andric } else { 393*0b57cec5SDimitry Andric Message = "This lock has already been destroyed"; 394*0b57cec5SDimitry Andric } 395*0b57cec5SDimitry Andric 396*0b57cec5SDimitry Andric if (!BT_destroylock) 397*0b57cec5SDimitry Andric BT_destroylock.reset(new BugType(this, "Destroy invalid lock", 398*0b57cec5SDimitry Andric "Lock checker")); 399*0b57cec5SDimitry Andric ExplodedNode *N = C.generateErrorNode(); 400*0b57cec5SDimitry Andric if (!N) 401*0b57cec5SDimitry Andric return; 402*0b57cec5SDimitry Andric auto Report = llvm::make_unique<BugReport>(*BT_destroylock, Message, N); 403*0b57cec5SDimitry Andric Report->addRange(CE->getArg(0)->getSourceRange()); 404*0b57cec5SDimitry Andric C.emitReport(std::move(Report)); 405*0b57cec5SDimitry Andric } 406*0b57cec5SDimitry Andric 407*0b57cec5SDimitry Andric void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE, 408*0b57cec5SDimitry Andric SVal Lock) const { 409*0b57cec5SDimitry Andric 410*0b57cec5SDimitry Andric const MemRegion *LockR = Lock.getAsRegion(); 411*0b57cec5SDimitry Andric if (!LockR) 412*0b57cec5SDimitry Andric return; 413*0b57cec5SDimitry Andric 414*0b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 415*0b57cec5SDimitry Andric 416*0b57cec5SDimitry Andric const SymbolRef *sym = State->get<DestroyRetVal>(LockR); 417*0b57cec5SDimitry Andric if (sym) 418*0b57cec5SDimitry Andric State = resolvePossiblyDestroyedMutex(State, LockR, sym); 419*0b57cec5SDimitry Andric 420*0b57cec5SDimitry Andric const struct LockState *LState = State->get<LockMap>(LockR); 421*0b57cec5SDimitry Andric if (!LState || LState->isDestroyed()) { 422*0b57cec5SDimitry Andric State = State->set<LockMap>(LockR, LockState::getUnlocked()); 423*0b57cec5SDimitry Andric C.addTransition(State); 424*0b57cec5SDimitry Andric return; 425*0b57cec5SDimitry Andric } 426*0b57cec5SDimitry Andric 427*0b57cec5SDimitry Andric StringRef Message; 428*0b57cec5SDimitry Andric 429*0b57cec5SDimitry Andric if (LState->isLocked()) { 430*0b57cec5SDimitry Andric Message = "This lock is still being held"; 431*0b57cec5SDimitry Andric } else { 432*0b57cec5SDimitry Andric Message = "This lock has already been initialized"; 433*0b57cec5SDimitry Andric } 434*0b57cec5SDimitry Andric 435*0b57cec5SDimitry Andric if (!BT_initlock) 436*0b57cec5SDimitry Andric BT_initlock.reset(new BugType(this, "Init invalid lock", 437*0b57cec5SDimitry Andric "Lock checker")); 438*0b57cec5SDimitry Andric ExplodedNode *N = C.generateErrorNode(); 439*0b57cec5SDimitry Andric if (!N) 440*0b57cec5SDimitry Andric return; 441*0b57cec5SDimitry Andric auto Report = llvm::make_unique<BugReport>(*BT_initlock, Message, N); 442*0b57cec5SDimitry Andric Report->addRange(CE->getArg(0)->getSourceRange()); 443*0b57cec5SDimitry Andric C.emitReport(std::move(Report)); 444*0b57cec5SDimitry Andric } 445*0b57cec5SDimitry Andric 446*0b57cec5SDimitry Andric void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C, 447*0b57cec5SDimitry Andric const CallExpr *CE) const { 448*0b57cec5SDimitry Andric if (!BT_destroylock) 449*0b57cec5SDimitry Andric BT_destroylock.reset(new BugType(this, "Use destroyed lock", 450*0b57cec5SDimitry Andric "Lock checker")); 451*0b57cec5SDimitry Andric ExplodedNode *N = C.generateErrorNode(); 452*0b57cec5SDimitry Andric if (!N) 453*0b57cec5SDimitry Andric return; 454*0b57cec5SDimitry Andric auto Report = llvm::make_unique<BugReport>( 455*0b57cec5SDimitry Andric *BT_destroylock, "This lock has already been destroyed", N); 456*0b57cec5SDimitry Andric Report->addRange(CE->getArg(0)->getSourceRange()); 457*0b57cec5SDimitry Andric C.emitReport(std::move(Report)); 458*0b57cec5SDimitry Andric } 459*0b57cec5SDimitry Andric 460*0b57cec5SDimitry Andric void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper, 461*0b57cec5SDimitry Andric CheckerContext &C) const { 462*0b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 463*0b57cec5SDimitry Andric 464*0b57cec5SDimitry Andric // TODO: Clean LockMap when a mutex region dies. 465*0b57cec5SDimitry Andric 466*0b57cec5SDimitry Andric DestroyRetValTy TrackedSymbols = State->get<DestroyRetVal>(); 467*0b57cec5SDimitry Andric for (DestroyRetValTy::iterator I = TrackedSymbols.begin(), 468*0b57cec5SDimitry Andric E = TrackedSymbols.end(); 469*0b57cec5SDimitry Andric I != E; ++I) { 470*0b57cec5SDimitry Andric const SymbolRef Sym = I->second; 471*0b57cec5SDimitry Andric const MemRegion *lockR = I->first; 472*0b57cec5SDimitry Andric bool IsSymDead = SymReaper.isDead(Sym); 473*0b57cec5SDimitry Andric // Remove the dead symbol from the return value symbols map. 474*0b57cec5SDimitry Andric if (IsSymDead) 475*0b57cec5SDimitry Andric State = resolvePossiblyDestroyedMutex(State, lockR, &Sym); 476*0b57cec5SDimitry Andric } 477*0b57cec5SDimitry Andric C.addTransition(State); 478*0b57cec5SDimitry Andric } 479*0b57cec5SDimitry Andric 480*0b57cec5SDimitry Andric void ento::registerPthreadLockChecker(CheckerManager &mgr) { 481*0b57cec5SDimitry Andric mgr.registerChecker<PthreadLockChecker>(); 482*0b57cec5SDimitry Andric } 483*0b57cec5SDimitry Andric 484*0b57cec5SDimitry Andric bool ento::shouldRegisterPthreadLockChecker(const LangOptions &LO) { 485*0b57cec5SDimitry Andric return true; 486*0b57cec5SDimitry Andric } 487