1 //===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines: 10 // * PthreadLockChecker, a simple lock -> unlock checker. 11 // Which also checks for XNU locks, which behave similarly enough to share 12 // code. 13 // * FuchsiaLocksChecker, which is also rather similar. 14 // * C11LockChecker which also closely follows Pthread semantics. 15 // 16 //===----------------------------------------------------------------------===// 17 18 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 19 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 20 #include "clang/StaticAnalyzer/Core/Checker.h" 21 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 22 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 23 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 24 25 using namespace clang; 26 using namespace ento; 27 28 namespace { 29 30 struct LockState { 31 enum Kind { 32 Destroyed, 33 Locked, 34 Unlocked, 35 UntouchedAndPossiblyDestroyed, 36 UnlockedAndPossiblyDestroyed 37 } K; 38 39 private: 40 LockState(Kind K) : K(K) {} 41 42 public: 43 static LockState getLocked() { return LockState(Locked); } 44 static LockState getUnlocked() { return LockState(Unlocked); } 45 static LockState getDestroyed() { return LockState(Destroyed); } 46 static LockState getUntouchedAndPossiblyDestroyed() { 47 return LockState(UntouchedAndPossiblyDestroyed); 48 } 49 static LockState getUnlockedAndPossiblyDestroyed() { 50 return LockState(UnlockedAndPossiblyDestroyed); 51 } 52 53 bool operator==(const LockState &X) const { return K == X.K; } 54 55 bool isLocked() const { return K == Locked; } 56 bool isUnlocked() const { return K == Unlocked; } 57 bool isDestroyed() const { return K == Destroyed; } 58 bool isUntouchedAndPossiblyDestroyed() const { 59 return K == UntouchedAndPossiblyDestroyed; 60 } 61 bool isUnlockedAndPossiblyDestroyed() const { 62 return K == UnlockedAndPossiblyDestroyed; 63 } 64 65 void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } 66 }; 67 68 class PthreadLockChecker : public Checker<check::PostCall, check::DeadSymbols, 69 check::RegionChanges> { 70 public: 71 enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics }; 72 enum CheckerKind { 73 CK_PthreadLockChecker, 74 CK_FuchsiaLockChecker, 75 CK_C11LockChecker, 76 CK_NumCheckKinds 77 }; 78 DefaultBool ChecksEnabled[CK_NumCheckKinds]; 79 CheckerNameRef CheckNames[CK_NumCheckKinds]; 80 81 private: 82 typedef void (PthreadLockChecker::*FnCheck)(const CallEvent &Call, 83 CheckerContext &C, 84 CheckerKind checkkind) const; 85 CallDescriptionMap<FnCheck> PThreadCallbacks = { 86 // Init. 87 {{"pthread_mutex_init", 2}, &PthreadLockChecker::InitAnyLock}, 88 // TODO: pthread_rwlock_init(2 arguments). 89 // TODO: lck_mtx_init(3 arguments). 90 // TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex. 91 // TODO: lck_rw_init(3 arguments). 92 // TODO: lck_rw_alloc_init(2 arguments) => returns the mutex. 93 94 // Acquire. 95 {{"pthread_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock}, 96 {{"pthread_rwlock_rdlock", 1}, &PthreadLockChecker::AcquirePthreadLock}, 97 {{"pthread_rwlock_wrlock", 1}, &PthreadLockChecker::AcquirePthreadLock}, 98 {{"lck_mtx_lock", 1}, &PthreadLockChecker::AcquireXNULock}, 99 {{"lck_rw_lock_exclusive", 1}, &PthreadLockChecker::AcquireXNULock}, 100 {{"lck_rw_lock_shared", 1}, &PthreadLockChecker::AcquireXNULock}, 101 102 // Try. 103 {{"pthread_mutex_trylock", 1}, &PthreadLockChecker::TryPthreadLock}, 104 {{"pthread_rwlock_tryrdlock", 1}, &PthreadLockChecker::TryPthreadLock}, 105 {{"pthread_rwlock_trywrlock", 1}, &PthreadLockChecker::TryPthreadLock}, 106 {{"lck_mtx_try_lock", 1}, &PthreadLockChecker::TryXNULock}, 107 {{"lck_rw_try_lock_exclusive", 1}, &PthreadLockChecker::TryXNULock}, 108 {{"lck_rw_try_lock_shared", 1}, &PthreadLockChecker::TryXNULock}, 109 110 // Release. 111 {{"pthread_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, 112 {{"pthread_rwlock_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, 113 {{"lck_mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, 114 {{"lck_rw_unlock_exclusive", 1}, &PthreadLockChecker::ReleaseAnyLock}, 115 {{"lck_rw_unlock_shared", 1}, &PthreadLockChecker::ReleaseAnyLock}, 116 {{"lck_rw_done", 1}, &PthreadLockChecker::ReleaseAnyLock}, 117 118 // Destroy. 119 {{"pthread_mutex_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock}, 120 {{"lck_mtx_destroy", 2}, &PthreadLockChecker::DestroyXNULock}, 121 // TODO: pthread_rwlock_destroy(1 argument). 122 // TODO: lck_rw_destroy(2 arguments). 123 }; 124 125 CallDescriptionMap<FnCheck> FuchsiaCallbacks = { 126 // Init. 127 {{"spin_lock_init", 1}, &PthreadLockChecker::InitAnyLock}, 128 129 // Acquire. 130 {{"spin_lock", 1}, &PthreadLockChecker::AcquirePthreadLock}, 131 {{"spin_lock_save", 3}, &PthreadLockChecker::AcquirePthreadLock}, 132 {{"sync_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock}, 133 {{"sync_mutex_lock_with_waiter", 1}, 134 &PthreadLockChecker::AcquirePthreadLock}, 135 136 // Try. 137 {{"spin_trylock", 1}, &PthreadLockChecker::TryFuchsiaLock}, 138 {{"sync_mutex_trylock", 1}, &PthreadLockChecker::TryFuchsiaLock}, 139 {{"sync_mutex_timedlock", 2}, &PthreadLockChecker::TryFuchsiaLock}, 140 141 // Release. 142 {{"spin_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, 143 {{"spin_unlock_restore", 3}, &PthreadLockChecker::ReleaseAnyLock}, 144 {{"sync_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, 145 }; 146 147 CallDescriptionMap<FnCheck> C11Callbacks = { 148 // Init. 149 {{"mtx_init", 2}, &PthreadLockChecker::InitAnyLock}, 150 151 // Acquire. 152 {{"mtx_lock", 1}, &PthreadLockChecker::AcquirePthreadLock}, 153 154 // Try. 155 {{"mtx_trylock", 1}, &PthreadLockChecker::TryC11Lock}, 156 {{"mtx_timedlock", 2}, &PthreadLockChecker::TryC11Lock}, 157 158 // Release. 159 {{"mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, 160 161 // Destroy 162 {{"mtx_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock}, 163 }; 164 165 ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state, 166 const MemRegion *lockR, 167 const SymbolRef *sym) const; 168 void reportUseDestroyedBug(const CallEvent &Call, CheckerContext &C, 169 unsigned ArgNo, CheckerKind checkKind) const; 170 171 // Init. 172 void InitAnyLock(const CallEvent &Call, CheckerContext &C, 173 CheckerKind checkkind) const; 174 void InitLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo, 175 SVal Lock, CheckerKind checkkind) const; 176 177 // Lock, Try-lock. 178 void AcquirePthreadLock(const CallEvent &Call, CheckerContext &C, 179 CheckerKind checkkind) const; 180 void AcquireXNULock(const CallEvent &Call, CheckerContext &C, 181 CheckerKind checkkind) const; 182 void TryPthreadLock(const CallEvent &Call, CheckerContext &C, 183 CheckerKind checkkind) const; 184 void TryXNULock(const CallEvent &Call, CheckerContext &C, 185 CheckerKind checkkind) const; 186 void TryFuchsiaLock(const CallEvent &Call, CheckerContext &C, 187 CheckerKind checkkind) const; 188 void TryC11Lock(const CallEvent &Call, CheckerContext &C, 189 CheckerKind checkkind) const; 190 void AcquireLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo, 191 SVal lock, bool isTryLock, LockingSemantics semantics, 192 CheckerKind checkkind) const; 193 194 // Release. 195 void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C, 196 CheckerKind checkkind) const; 197 void ReleaseLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo, 198 SVal lock, CheckerKind checkkind) const; 199 200 // Destroy. 201 void DestroyPthreadLock(const CallEvent &Call, CheckerContext &C, 202 CheckerKind checkkind) const; 203 void DestroyXNULock(const CallEvent &Call, CheckerContext &C, 204 CheckerKind checkkind) const; 205 void DestroyLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo, 206 SVal Lock, LockingSemantics semantics, 207 CheckerKind checkkind) const; 208 209 public: 210 void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 211 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 212 ProgramStateRef 213 checkRegionChanges(ProgramStateRef State, const InvalidatedSymbols *Symbols, 214 ArrayRef<const MemRegion *> ExplicitRegions, 215 ArrayRef<const MemRegion *> Regions, 216 const LocationContext *LCtx, const CallEvent *Call) const; 217 void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, 218 const char *Sep) const override; 219 220 private: 221 mutable std::unique_ptr<BugType> BT_doublelock[CK_NumCheckKinds]; 222 mutable std::unique_ptr<BugType> BT_doubleunlock[CK_NumCheckKinds]; 223 mutable std::unique_ptr<BugType> BT_destroylock[CK_NumCheckKinds]; 224 mutable std::unique_ptr<BugType> BT_initlock[CK_NumCheckKinds]; 225 mutable std::unique_ptr<BugType> BT_lor[CK_NumCheckKinds]; 226 227 void initBugType(CheckerKind checkKind) const { 228 if (BT_doublelock[checkKind]) 229 return; 230 BT_doublelock[checkKind].reset( 231 new BugType{CheckNames[checkKind], "Double locking", "Lock checker"}); 232 BT_doubleunlock[checkKind].reset( 233 new BugType{CheckNames[checkKind], "Double unlocking", "Lock checker"}); 234 BT_destroylock[checkKind].reset(new BugType{ 235 CheckNames[checkKind], "Use destroyed lock", "Lock checker"}); 236 BT_initlock[checkKind].reset(new BugType{ 237 CheckNames[checkKind], "Init invalid lock", "Lock checker"}); 238 BT_lor[checkKind].reset(new BugType{CheckNames[checkKind], 239 "Lock order reversal", "Lock checker"}); 240 } 241 }; 242 } // end anonymous namespace 243 244 // A stack of locks for tracking lock-unlock order. 245 REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *) 246 247 // An entry for tracking lock states. 248 REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState) 249 250 // Return values for unresolved calls to pthread_mutex_destroy(). 251 REGISTER_MAP_WITH_PROGRAMSTATE(DestroyRetVal, const MemRegion *, SymbolRef) 252 253 void PthreadLockChecker::checkPostCall(const CallEvent &Call, 254 CheckerContext &C) const { 255 // An additional umbrella check that all functions modeled by this checker 256 // are global C functions. 257 // TODO: Maybe make this the default behavior of CallDescription 258 // with exactly one identifier? 259 if (!Call.isGlobalCFunction()) 260 return; 261 262 if (const FnCheck *Callback = PThreadCallbacks.lookup(Call)) 263 (this->**Callback)(Call, C, CK_PthreadLockChecker); 264 else if (const FnCheck *Callback = FuchsiaCallbacks.lookup(Call)) 265 (this->**Callback)(Call, C, CK_FuchsiaLockChecker); 266 else if (const FnCheck *Callback = C11Callbacks.lookup(Call)) 267 (this->**Callback)(Call, C, CK_C11LockChecker); 268 } 269 270 // When a lock is destroyed, in some semantics(like PthreadSemantics) we are not 271 // sure if the destroy call has succeeded or failed, and the lock enters one of 272 // the 'possibly destroyed' state. There is a short time frame for the 273 // programmer to check the return value to see if the lock was successfully 274 // destroyed. Before we model the next operation over that lock, we call this 275 // function to see if the return value was checked by now and set the lock state 276 // - either to destroyed state or back to its previous state. 277 278 // In PthreadSemantics, pthread_mutex_destroy() returns zero if the lock is 279 // successfully destroyed and it returns a non-zero value otherwise. 280 ProgramStateRef PthreadLockChecker::resolvePossiblyDestroyedMutex( 281 ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const { 282 const LockState *lstate = state->get<LockMap>(lockR); 283 // Existence in DestroyRetVal ensures existence in LockMap. 284 // Existence in Destroyed also ensures that the lock state for lockR is either 285 // UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed. 286 assert(lstate->isUntouchedAndPossiblyDestroyed() || 287 lstate->isUnlockedAndPossiblyDestroyed()); 288 289 ConstraintManager &CMgr = state->getConstraintManager(); 290 ConditionTruthVal retZero = CMgr.isNull(state, *sym); 291 if (retZero.isConstrainedFalse()) { 292 if (lstate->isUntouchedAndPossiblyDestroyed()) 293 state = state->remove<LockMap>(lockR); 294 else if (lstate->isUnlockedAndPossiblyDestroyed()) 295 state = state->set<LockMap>(lockR, LockState::getUnlocked()); 296 } else 297 state = state->set<LockMap>(lockR, LockState::getDestroyed()); 298 299 // Removing the map entry (lockR, sym) from DestroyRetVal as the lock state is 300 // now resolved. 301 state = state->remove<DestroyRetVal>(lockR); 302 return state; 303 } 304 305 void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State, 306 const char *NL, const char *Sep) const { 307 LockMapTy LM = State->get<LockMap>(); 308 if (!LM.isEmpty()) { 309 Out << Sep << "Mutex states:" << NL; 310 for (auto I : LM) { 311 I.first->dumpToStream(Out); 312 if (I.second.isLocked()) 313 Out << ": locked"; 314 else if (I.second.isUnlocked()) 315 Out << ": unlocked"; 316 else if (I.second.isDestroyed()) 317 Out << ": destroyed"; 318 else if (I.second.isUntouchedAndPossiblyDestroyed()) 319 Out << ": not tracked, possibly destroyed"; 320 else if (I.second.isUnlockedAndPossiblyDestroyed()) 321 Out << ": unlocked, possibly destroyed"; 322 Out << NL; 323 } 324 } 325 326 LockSetTy LS = State->get<LockSet>(); 327 if (!LS.isEmpty()) { 328 Out << Sep << "Mutex lock order:" << NL; 329 for (auto I : LS) { 330 I->dumpToStream(Out); 331 Out << NL; 332 } 333 } 334 335 // TODO: Dump destroyed mutex symbols? 336 } 337 338 void PthreadLockChecker::AcquirePthreadLock(const CallEvent &Call, 339 CheckerContext &C, 340 CheckerKind checkKind) const { 341 AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, PthreadSemantics, 342 checkKind); 343 } 344 345 void PthreadLockChecker::AcquireXNULock(const CallEvent &Call, 346 CheckerContext &C, 347 CheckerKind checkKind) const { 348 AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, XNUSemantics, 349 checkKind); 350 } 351 352 void PthreadLockChecker::TryPthreadLock(const CallEvent &Call, 353 CheckerContext &C, 354 CheckerKind checkKind) const { 355 AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics, 356 checkKind); 357 } 358 359 void PthreadLockChecker::TryXNULock(const CallEvent &Call, CheckerContext &C, 360 CheckerKind checkKind) const { 361 AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics, 362 checkKind); 363 } 364 365 void PthreadLockChecker::TryFuchsiaLock(const CallEvent &Call, 366 CheckerContext &C, 367 CheckerKind checkKind) const { 368 AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics, 369 checkKind); 370 } 371 372 void PthreadLockChecker::TryC11Lock(const CallEvent &Call, CheckerContext &C, 373 CheckerKind checkKind) const { 374 AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics, 375 checkKind); 376 } 377 378 void PthreadLockChecker::AcquireLockAux(const CallEvent &Call, 379 CheckerContext &C, unsigned ArgNo, 380 SVal lock, bool isTryLock, 381 enum LockingSemantics semantics, 382 CheckerKind checkKind) const { 383 if (!ChecksEnabled[checkKind]) 384 return; 385 386 const MemRegion *lockR = lock.getAsRegion(); 387 if (!lockR) 388 return; 389 390 ProgramStateRef state = C.getState(); 391 const SymbolRef *sym = state->get<DestroyRetVal>(lockR); 392 if (sym) 393 state = resolvePossiblyDestroyedMutex(state, lockR, sym); 394 395 if (const LockState *LState = state->get<LockMap>(lockR)) { 396 if (LState->isLocked()) { 397 ExplodedNode *N = C.generateErrorNode(); 398 if (!N) 399 return; 400 initBugType(checkKind); 401 auto report = std::make_unique<PathSensitiveBugReport>( 402 *BT_doublelock[checkKind], "This lock has already been acquired", N); 403 report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); 404 C.emitReport(std::move(report)); 405 return; 406 } else if (LState->isDestroyed()) { 407 reportUseDestroyedBug(Call, C, ArgNo, checkKind); 408 return; 409 } 410 } 411 412 ProgramStateRef lockSucc = state; 413 if (isTryLock) { 414 // Bifurcate the state, and allow a mode where the lock acquisition fails. 415 SVal RetVal = Call.getReturnValue(); 416 if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) { 417 ProgramStateRef lockFail; 418 switch (semantics) { 419 case PthreadSemantics: 420 std::tie(lockFail, lockSucc) = state->assume(*DefinedRetVal); 421 break; 422 case XNUSemantics: 423 std::tie(lockSucc, lockFail) = state->assume(*DefinedRetVal); 424 break; 425 default: 426 llvm_unreachable("Unknown tryLock locking semantics"); 427 } 428 assert(lockFail && lockSucc); 429 C.addTransition(lockFail); 430 } 431 // We might want to handle the case when the mutex lock function was inlined 432 // and returned an Unknown or Undefined value. 433 } else if (semantics == PthreadSemantics) { 434 // Assume that the return value was 0. 435 SVal RetVal = Call.getReturnValue(); 436 if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) { 437 // FIXME: If the lock function was inlined and returned true, 438 // we need to behave sanely - at least generate sink. 439 lockSucc = state->assume(*DefinedRetVal, false); 440 assert(lockSucc); 441 } 442 // We might want to handle the case when the mutex lock function was inlined 443 // and returned an Unknown or Undefined value. 444 } else { 445 // XNU locking semantics return void on non-try locks 446 assert((semantics == XNUSemantics) && "Unknown locking semantics"); 447 lockSucc = state; 448 } 449 450 // Record that the lock was acquired. 451 lockSucc = lockSucc->add<LockSet>(lockR); 452 lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked()); 453 C.addTransition(lockSucc); 454 } 455 456 void PthreadLockChecker::ReleaseAnyLock(const CallEvent &Call, 457 CheckerContext &C, 458 CheckerKind checkKind) const { 459 ReleaseLockAux(Call, C, 0, Call.getArgSVal(0), checkKind); 460 } 461 462 void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call, 463 CheckerContext &C, unsigned ArgNo, 464 SVal lock, 465 CheckerKind checkKind) const { 466 if (!ChecksEnabled[checkKind]) 467 return; 468 469 const MemRegion *lockR = lock.getAsRegion(); 470 if (!lockR) 471 return; 472 473 ProgramStateRef state = C.getState(); 474 const SymbolRef *sym = state->get<DestroyRetVal>(lockR); 475 if (sym) 476 state = resolvePossiblyDestroyedMutex(state, lockR, sym); 477 478 if (const LockState *LState = state->get<LockMap>(lockR)) { 479 if (LState->isUnlocked()) { 480 ExplodedNode *N = C.generateErrorNode(); 481 if (!N) 482 return; 483 initBugType(checkKind); 484 auto Report = std::make_unique<PathSensitiveBugReport>( 485 *BT_doubleunlock[checkKind], "This lock has already been unlocked", 486 N); 487 Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); 488 C.emitReport(std::move(Report)); 489 return; 490 } else if (LState->isDestroyed()) { 491 reportUseDestroyedBug(Call, C, ArgNo, checkKind); 492 return; 493 } 494 } 495 496 LockSetTy LS = state->get<LockSet>(); 497 498 if (!LS.isEmpty()) { 499 const MemRegion *firstLockR = LS.getHead(); 500 if (firstLockR != lockR) { 501 ExplodedNode *N = C.generateErrorNode(); 502 if (!N) 503 return; 504 initBugType(checkKind); 505 auto report = std::make_unique<PathSensitiveBugReport>( 506 *BT_lor[checkKind], 507 "This was not the most recently acquired lock. Possible " 508 "lock order reversal", 509 N); 510 report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); 511 C.emitReport(std::move(report)); 512 return; 513 } 514 // Record that the lock was released. 515 state = state->set<LockSet>(LS.getTail()); 516 } 517 518 state = state->set<LockMap>(lockR, LockState::getUnlocked()); 519 C.addTransition(state); 520 } 521 522 void PthreadLockChecker::DestroyPthreadLock(const CallEvent &Call, 523 CheckerContext &C, 524 CheckerKind checkKind) const { 525 DestroyLockAux(Call, C, 0, Call.getArgSVal(0), PthreadSemantics, checkKind); 526 } 527 528 void PthreadLockChecker::DestroyXNULock(const CallEvent &Call, 529 CheckerContext &C, 530 CheckerKind checkKind) const { 531 DestroyLockAux(Call, C, 0, Call.getArgSVal(0), XNUSemantics, checkKind); 532 } 533 534 void PthreadLockChecker::DestroyLockAux(const CallEvent &Call, 535 CheckerContext &C, unsigned ArgNo, 536 SVal Lock, 537 enum LockingSemantics semantics, 538 CheckerKind checkKind) const { 539 if (!ChecksEnabled[checkKind]) 540 return; 541 542 const MemRegion *LockR = Lock.getAsRegion(); 543 if (!LockR) 544 return; 545 546 ProgramStateRef State = C.getState(); 547 548 const SymbolRef *sym = State->get<DestroyRetVal>(LockR); 549 if (sym) 550 State = resolvePossiblyDestroyedMutex(State, LockR, sym); 551 552 const LockState *LState = State->get<LockMap>(LockR); 553 // Checking the return value of the destroy method only in the case of 554 // PthreadSemantics 555 if (semantics == PthreadSemantics) { 556 if (!LState || LState->isUnlocked()) { 557 SymbolRef sym = Call.getReturnValue().getAsSymbol(); 558 if (!sym) { 559 State = State->remove<LockMap>(LockR); 560 C.addTransition(State); 561 return; 562 } 563 State = State->set<DestroyRetVal>(LockR, sym); 564 if (LState && LState->isUnlocked()) 565 State = State->set<LockMap>( 566 LockR, LockState::getUnlockedAndPossiblyDestroyed()); 567 else 568 State = State->set<LockMap>( 569 LockR, LockState::getUntouchedAndPossiblyDestroyed()); 570 C.addTransition(State); 571 return; 572 } 573 } else { 574 if (!LState || LState->isUnlocked()) { 575 State = State->set<LockMap>(LockR, LockState::getDestroyed()); 576 C.addTransition(State); 577 return; 578 } 579 } 580 StringRef Message; 581 582 if (LState->isLocked()) { 583 Message = "This lock is still locked"; 584 } else { 585 Message = "This lock has already been destroyed"; 586 } 587 588 ExplodedNode *N = C.generateErrorNode(); 589 if (!N) 590 return; 591 initBugType(checkKind); 592 auto Report = std::make_unique<PathSensitiveBugReport>( 593 *BT_destroylock[checkKind], Message, N); 594 Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); 595 C.emitReport(std::move(Report)); 596 } 597 598 void PthreadLockChecker::InitAnyLock(const CallEvent &Call, CheckerContext &C, 599 CheckerKind checkKind) const { 600 InitLockAux(Call, C, 0, Call.getArgSVal(0), checkKind); 601 } 602 603 void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C, 604 unsigned ArgNo, SVal Lock, 605 CheckerKind checkKind) const { 606 if (!ChecksEnabled[checkKind]) 607 return; 608 609 const MemRegion *LockR = Lock.getAsRegion(); 610 if (!LockR) 611 return; 612 613 ProgramStateRef State = C.getState(); 614 615 const SymbolRef *sym = State->get<DestroyRetVal>(LockR); 616 if (sym) 617 State = resolvePossiblyDestroyedMutex(State, LockR, sym); 618 619 const struct LockState *LState = State->get<LockMap>(LockR); 620 if (!LState || LState->isDestroyed()) { 621 State = State->set<LockMap>(LockR, LockState::getUnlocked()); 622 C.addTransition(State); 623 return; 624 } 625 626 StringRef Message; 627 628 if (LState->isLocked()) { 629 Message = "This lock is still being held"; 630 } else { 631 Message = "This lock has already been initialized"; 632 } 633 634 ExplodedNode *N = C.generateErrorNode(); 635 if (!N) 636 return; 637 initBugType(checkKind); 638 auto Report = std::make_unique<PathSensitiveBugReport>( 639 *BT_initlock[checkKind], Message, N); 640 Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); 641 C.emitReport(std::move(Report)); 642 } 643 644 void PthreadLockChecker::reportUseDestroyedBug(const CallEvent &Call, 645 CheckerContext &C, 646 unsigned ArgNo, 647 CheckerKind checkKind) const { 648 ExplodedNode *N = C.generateErrorNode(); 649 if (!N) 650 return; 651 initBugType(checkKind); 652 auto Report = std::make_unique<PathSensitiveBugReport>( 653 *BT_destroylock[checkKind], "This lock has already been destroyed", N); 654 Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); 655 C.emitReport(std::move(Report)); 656 } 657 658 void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper, 659 CheckerContext &C) const { 660 ProgramStateRef State = C.getState(); 661 662 for (auto I : State->get<DestroyRetVal>()) { 663 // Once the return value symbol dies, no more checks can be performed 664 // against it. See if the return value was checked before this point. 665 // This would remove the symbol from the map as well. 666 if (SymReaper.isDead(I.second)) 667 State = resolvePossiblyDestroyedMutex(State, I.first, &I.second); 668 } 669 670 for (auto I : State->get<LockMap>()) { 671 // Stop tracking dead mutex regions as well. 672 if (!SymReaper.isLiveRegion(I.first)) 673 State = State->remove<LockMap>(I.first); 674 } 675 676 // TODO: We probably need to clean up the lock stack as well. 677 // It is tricky though: even if the mutex cannot be unlocked anymore, 678 // it can still participate in lock order reversal resolution. 679 680 C.addTransition(State); 681 } 682 683 ProgramStateRef PthreadLockChecker::checkRegionChanges( 684 ProgramStateRef State, const InvalidatedSymbols *Symbols, 685 ArrayRef<const MemRegion *> ExplicitRegions, 686 ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, 687 const CallEvent *Call) const { 688 689 bool IsLibraryFunction = false; 690 if (Call && Call->isGlobalCFunction()) { 691 // Avoid invalidating mutex state when a known supported function is called. 692 if (PThreadCallbacks.lookup(*Call) || FuchsiaCallbacks.lookup(*Call) || 693 C11Callbacks.lookup(*Call)) 694 return State; 695 696 if (Call->isInSystemHeader()) 697 IsLibraryFunction = true; 698 } 699 700 for (auto R : Regions) { 701 // We assume that system library function wouldn't touch the mutex unless 702 // it takes the mutex explicitly as an argument. 703 // FIXME: This is a bit quadratic. 704 if (IsLibraryFunction && 705 std::find(ExplicitRegions.begin(), ExplicitRegions.end(), R) == 706 ExplicitRegions.end()) 707 continue; 708 709 State = State->remove<LockMap>(R); 710 State = State->remove<DestroyRetVal>(R); 711 712 // TODO: We need to invalidate the lock stack as well. This is tricky 713 // to implement correctly and efficiently though, because the effects 714 // of mutex escapes on lock order may be fairly varied. 715 } 716 717 return State; 718 } 719 720 void ento::registerPthreadLockBase(CheckerManager &mgr) { 721 mgr.registerChecker<PthreadLockChecker>(); 722 } 723 724 bool ento::shouldRegisterPthreadLockBase(const LangOptions &LO) { return true; } 725 726 #define REGISTER_CHECKER(name) \ 727 void ento::register##name(CheckerManager &mgr) { \ 728 PthreadLockChecker *checker = mgr.getChecker<PthreadLockChecker>(); \ 729 checker->ChecksEnabled[PthreadLockChecker::CK_##name] = true; \ 730 checker->CheckNames[PthreadLockChecker::CK_##name] = \ 731 mgr.getCurrentCheckerName(); \ 732 } \ 733 \ 734 bool ento::shouldRegister##name(const LangOptions &LO) { return true; } 735 736 REGISTER_CHECKER(PthreadLockChecker) 737 REGISTER_CHECKER(FuchsiaLockChecker) 738 REGISTER_CHECKER(C11LockChecker) 739