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