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 defines PthreadLockChecker, a simple lock -> unlock checker. 10 // Also handles XNU locks, which behave similarly enough to share code. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 15 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 16 #include "clang/StaticAnalyzer/Core/Checker.h" 17 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 18 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 19 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 20 21 using namespace clang; 22 using namespace ento; 23 24 namespace { 25 26 struct LockState { 27 enum Kind { 28 Destroyed, 29 Locked, 30 Unlocked, 31 UntouchedAndPossiblyDestroyed, 32 UnlockedAndPossiblyDestroyed 33 } K; 34 35 private: 36 LockState(Kind K) : K(K) {} 37 38 public: 39 static LockState getLocked() { return LockState(Locked); } 40 static LockState getUnlocked() { return LockState(Unlocked); } 41 static LockState getDestroyed() { return LockState(Destroyed); } 42 static LockState getUntouchedAndPossiblyDestroyed() { 43 return LockState(UntouchedAndPossiblyDestroyed); 44 } 45 static LockState getUnlockedAndPossiblyDestroyed() { 46 return LockState(UnlockedAndPossiblyDestroyed); 47 } 48 49 bool operator==(const LockState &X) const { 50 return K == X.K; 51 } 52 53 bool isLocked() const { return K == Locked; } 54 bool isUnlocked() const { return K == Unlocked; } 55 bool isDestroyed() const { return K == Destroyed; } 56 bool isUntouchedAndPossiblyDestroyed() const { 57 return K == UntouchedAndPossiblyDestroyed; 58 } 59 bool isUnlockedAndPossiblyDestroyed() const { 60 return K == UnlockedAndPossiblyDestroyed; 61 } 62 63 void Profile(llvm::FoldingSetNodeID &ID) const { 64 ID.AddInteger(K); 65 } 66 }; 67 68 class PthreadLockChecker 69 : public Checker<check::PostCall, check::DeadSymbols, 70 check::RegionChanges> { 71 BugType BT_doublelock{this, "Double locking", "Lock checker"}, 72 BT_doubleunlock{this, "Double unlocking", "Lock checker"}, 73 BT_destroylock{this, "Use destroyed lock", "Lock checker"}, 74 BT_initlock{this, "Init invalid lock", "Lock checker"}, 75 BT_lor{this, "Lock order reversal", "Lock checker"}; 76 77 enum LockingSemantics { 78 NotApplicable = 0, 79 PthreadSemantics, 80 XNUSemantics 81 }; 82 83 typedef void (PthreadLockChecker::*FnCheck)(const CallEvent &Call, 84 CheckerContext &C) const; 85 CallDescriptionMap<FnCheck> Callbacks = { 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 ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state, 126 const MemRegion *lockR, 127 const SymbolRef *sym) const; 128 void reportUseDestroyedBug(const CallEvent &Call, CheckerContext &C, 129 unsigned ArgNo) const; 130 131 // Init. 132 void InitAnyLock(const CallEvent &Call, CheckerContext &C) const; 133 void InitLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo, 134 SVal Lock) const; 135 136 // Lock, Try-lock. 137 void AcquirePthreadLock(const CallEvent &Call, CheckerContext &C) const; 138 void AcquireXNULock(const CallEvent &Call, CheckerContext &C) const; 139 void TryPthreadLock(const CallEvent &Call, CheckerContext &C) const; 140 void TryXNULock(const CallEvent &Call, CheckerContext &C) const; 141 void AcquireLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo, 142 SVal lock, bool isTryLock, 143 enum LockingSemantics semantics) const; 144 145 // Release. 146 void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C) const; 147 void ReleaseLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo, 148 SVal lock) const; 149 150 // Destroy. 151 void DestroyPthreadLock(const CallEvent &Call, CheckerContext &C) const; 152 void DestroyXNULock(const CallEvent &Call, CheckerContext &C) const; 153 void DestroyLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo, 154 SVal Lock, enum LockingSemantics semantics) const; 155 156 public: 157 void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 158 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 159 ProgramStateRef 160 checkRegionChanges(ProgramStateRef State, const InvalidatedSymbols *Symbols, 161 ArrayRef<const MemRegion *> ExplicitRegions, 162 ArrayRef<const MemRegion *> Regions, 163 const LocationContext *LCtx, const CallEvent *Call) const; 164 void printState(raw_ostream &Out, ProgramStateRef State, 165 const char *NL, const char *Sep) const override; 166 }; 167 } // end anonymous namespace 168 169 // A stack of locks for tracking lock-unlock order. 170 REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *) 171 172 // An entry for tracking lock states. 173 REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState) 174 175 // Return values for unresolved calls to pthread_mutex_destroy(). 176 REGISTER_MAP_WITH_PROGRAMSTATE(DestroyRetVal, const MemRegion *, SymbolRef) 177 178 void PthreadLockChecker::checkPostCall(const CallEvent &Call, 179 CheckerContext &C) const { 180 // An additional umbrella check that all functions modeled by this checker 181 // are global C functions. 182 // TODO: Maybe make this the default behavior of CallDescription 183 // with exactly one identifier? 184 if (!Call.isGlobalCFunction()) 185 return; 186 187 if (const FnCheck *Callback = Callbacks.lookup(Call)) 188 (this->**Callback)(Call, C); 189 } 190 191 192 // When a lock is destroyed, in some semantics(like PthreadSemantics) we are not 193 // sure if the destroy call has succeeded or failed, and the lock enters one of 194 // the 'possibly destroyed' state. There is a short time frame for the 195 // programmer to check the return value to see if the lock was successfully 196 // destroyed. Before we model the next operation over that lock, we call this 197 // function to see if the return value was checked by now and set the lock state 198 // - either to destroyed state or back to its previous state. 199 200 // In PthreadSemantics, pthread_mutex_destroy() returns zero if the lock is 201 // successfully destroyed and it returns a non-zero value otherwise. 202 ProgramStateRef PthreadLockChecker::resolvePossiblyDestroyedMutex( 203 ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const { 204 const LockState *lstate = state->get<LockMap>(lockR); 205 // Existence in DestroyRetVal ensures existence in LockMap. 206 // Existence in Destroyed also ensures that the lock state for lockR is either 207 // UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed. 208 assert(lstate->isUntouchedAndPossiblyDestroyed() || 209 lstate->isUnlockedAndPossiblyDestroyed()); 210 211 ConstraintManager &CMgr = state->getConstraintManager(); 212 ConditionTruthVal retZero = CMgr.isNull(state, *sym); 213 if (retZero.isConstrainedFalse()) { 214 if (lstate->isUntouchedAndPossiblyDestroyed()) 215 state = state->remove<LockMap>(lockR); 216 else if (lstate->isUnlockedAndPossiblyDestroyed()) 217 state = state->set<LockMap>(lockR, LockState::getUnlocked()); 218 } else 219 state = state->set<LockMap>(lockR, LockState::getDestroyed()); 220 221 // Removing the map entry (lockR, sym) from DestroyRetVal as the lock state is 222 // now resolved. 223 state = state->remove<DestroyRetVal>(lockR); 224 return state; 225 } 226 227 void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State, 228 const char *NL, const char *Sep) const { 229 LockMapTy LM = State->get<LockMap>(); 230 if (!LM.isEmpty()) { 231 Out << Sep << "Mutex states:" << NL; 232 for (auto I : LM) { 233 I.first->dumpToStream(Out); 234 if (I.second.isLocked()) 235 Out << ": locked"; 236 else if (I.second.isUnlocked()) 237 Out << ": unlocked"; 238 else if (I.second.isDestroyed()) 239 Out << ": destroyed"; 240 else if (I.second.isUntouchedAndPossiblyDestroyed()) 241 Out << ": not tracked, possibly destroyed"; 242 else if (I.second.isUnlockedAndPossiblyDestroyed()) 243 Out << ": unlocked, possibly destroyed"; 244 Out << NL; 245 } 246 } 247 248 LockSetTy LS = State->get<LockSet>(); 249 if (!LS.isEmpty()) { 250 Out << Sep << "Mutex lock order:" << NL; 251 for (auto I: LS) { 252 I->dumpToStream(Out); 253 Out << NL; 254 } 255 } 256 257 // TODO: Dump destroyed mutex symbols? 258 } 259 260 void PthreadLockChecker::AcquirePthreadLock(const CallEvent &Call, 261 CheckerContext &C) const { 262 AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, PthreadSemantics); 263 } 264 265 void PthreadLockChecker::AcquireXNULock(const CallEvent &Call, 266 CheckerContext &C) const { 267 AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, XNUSemantics); 268 } 269 270 void PthreadLockChecker::TryPthreadLock(const CallEvent &Call, 271 CheckerContext &C) const { 272 AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics); 273 } 274 275 void PthreadLockChecker::TryXNULock(const CallEvent &Call, 276 CheckerContext &C) const { 277 AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics); 278 } 279 280 void PthreadLockChecker::AcquireLockAux(const CallEvent &Call, 281 CheckerContext &C, unsigned ArgNo, 282 SVal lock, bool isTryLock, 283 enum LockingSemantics semantics) const { 284 285 const MemRegion *lockR = lock.getAsRegion(); 286 if (!lockR) 287 return; 288 289 ProgramStateRef state = C.getState(); 290 const SymbolRef *sym = state->get<DestroyRetVal>(lockR); 291 if (sym) 292 state = resolvePossiblyDestroyedMutex(state, lockR, sym); 293 294 if (const LockState *LState = state->get<LockMap>(lockR)) { 295 if (LState->isLocked()) { 296 ExplodedNode *N = C.generateErrorNode(); 297 if (!N) 298 return; 299 auto report = std::make_unique<PathSensitiveBugReport>( 300 BT_doublelock, "This lock has already been acquired", N); 301 report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); 302 C.emitReport(std::move(report)); 303 return; 304 } else if (LState->isDestroyed()) { 305 reportUseDestroyedBug(Call, C, ArgNo); 306 return; 307 } 308 } 309 310 ProgramStateRef lockSucc = state; 311 if (isTryLock) { 312 // Bifurcate the state, and allow a mode where the lock acquisition fails. 313 SVal RetVal = Call.getReturnValue(); 314 if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) { 315 ProgramStateRef lockFail; 316 switch (semantics) { 317 case PthreadSemantics: 318 std::tie(lockFail, lockSucc) = state->assume(*DefinedRetVal); 319 break; 320 case XNUSemantics: 321 std::tie(lockSucc, lockFail) = state->assume(*DefinedRetVal); 322 break; 323 default: 324 llvm_unreachable("Unknown tryLock locking semantics"); 325 } 326 assert(lockFail && lockSucc); 327 C.addTransition(lockFail); 328 } 329 // We might want to handle the case when the mutex lock function was inlined 330 // and returned an Unknown or Undefined value. 331 } else if (semantics == PthreadSemantics) { 332 // Assume that the return value was 0. 333 SVal RetVal = Call.getReturnValue(); 334 if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) { 335 // FIXME: If the lock function was inlined and returned true, 336 // we need to behave sanely - at least generate sink. 337 lockSucc = state->assume(*DefinedRetVal, false); 338 assert(lockSucc); 339 } 340 // We might want to handle the case when the mutex lock function was inlined 341 // and returned an Unknown or Undefined value. 342 } else { 343 // XNU locking semantics return void on non-try locks 344 assert((semantics == XNUSemantics) && "Unknown locking semantics"); 345 lockSucc = state; 346 } 347 348 // Record that the lock was acquired. 349 lockSucc = lockSucc->add<LockSet>(lockR); 350 lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked()); 351 C.addTransition(lockSucc); 352 } 353 354 void PthreadLockChecker::ReleaseAnyLock(const CallEvent &Call, 355 CheckerContext &C) const { 356 ReleaseLockAux(Call, C, 0, Call.getArgSVal(0)); 357 } 358 359 void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call, 360 CheckerContext &C, unsigned ArgNo, 361 SVal lock) const { 362 363 const MemRegion *lockR = lock.getAsRegion(); 364 if (!lockR) 365 return; 366 367 ProgramStateRef state = C.getState(); 368 const SymbolRef *sym = state->get<DestroyRetVal>(lockR); 369 if (sym) 370 state = resolvePossiblyDestroyedMutex(state, lockR, sym); 371 372 if (const LockState *LState = state->get<LockMap>(lockR)) { 373 if (LState->isUnlocked()) { 374 ExplodedNode *N = C.generateErrorNode(); 375 if (!N) 376 return; 377 auto Report = std::make_unique<PathSensitiveBugReport>( 378 BT_doubleunlock, "This lock has already been unlocked", N); 379 Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); 380 C.emitReport(std::move(Report)); 381 return; 382 } else if (LState->isDestroyed()) { 383 reportUseDestroyedBug(Call, C, ArgNo); 384 return; 385 } 386 } 387 388 LockSetTy LS = state->get<LockSet>(); 389 390 if (!LS.isEmpty()) { 391 const MemRegion *firstLockR = LS.getHead(); 392 if (firstLockR != lockR) { 393 ExplodedNode *N = C.generateErrorNode(); 394 if (!N) 395 return; 396 auto report = std::make_unique<PathSensitiveBugReport>( 397 BT_lor, "This was not the most recently acquired lock. Possible " 398 "lock order reversal", N); 399 report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); 400 C.emitReport(std::move(report)); 401 return; 402 } 403 // Record that the lock was released. 404 state = state->set<LockSet>(LS.getTail()); 405 } 406 407 state = state->set<LockMap>(lockR, LockState::getUnlocked()); 408 C.addTransition(state); 409 } 410 411 void PthreadLockChecker::DestroyPthreadLock(const CallEvent &Call, 412 CheckerContext &C) const { 413 DestroyLockAux(Call, C, 0, Call.getArgSVal(0), PthreadSemantics); 414 } 415 416 void PthreadLockChecker::DestroyXNULock(const CallEvent &Call, 417 CheckerContext &C) const { 418 DestroyLockAux(Call, C, 0, Call.getArgSVal(0), XNUSemantics); 419 } 420 421 void PthreadLockChecker::DestroyLockAux(const CallEvent &Call, 422 CheckerContext &C, unsigned ArgNo, 423 SVal Lock, 424 enum LockingSemantics semantics) const { 425 426 const MemRegion *LockR = Lock.getAsRegion(); 427 if (!LockR) 428 return; 429 430 ProgramStateRef State = C.getState(); 431 432 const SymbolRef *sym = State->get<DestroyRetVal>(LockR); 433 if (sym) 434 State = resolvePossiblyDestroyedMutex(State, LockR, sym); 435 436 const LockState *LState = State->get<LockMap>(LockR); 437 // Checking the return value of the destroy method only in the case of 438 // PthreadSemantics 439 if (semantics == PthreadSemantics) { 440 if (!LState || LState->isUnlocked()) { 441 SymbolRef sym = Call.getReturnValue().getAsSymbol(); 442 if (!sym) { 443 State = State->remove<LockMap>(LockR); 444 C.addTransition(State); 445 return; 446 } 447 State = State->set<DestroyRetVal>(LockR, sym); 448 if (LState && LState->isUnlocked()) 449 State = State->set<LockMap>( 450 LockR, LockState::getUnlockedAndPossiblyDestroyed()); 451 else 452 State = State->set<LockMap>( 453 LockR, LockState::getUntouchedAndPossiblyDestroyed()); 454 C.addTransition(State); 455 return; 456 } 457 } else { 458 if (!LState || LState->isUnlocked()) { 459 State = State->set<LockMap>(LockR, LockState::getDestroyed()); 460 C.addTransition(State); 461 return; 462 } 463 } 464 StringRef Message; 465 466 if (LState->isLocked()) { 467 Message = "This lock is still locked"; 468 } else { 469 Message = "This lock has already been destroyed"; 470 } 471 472 ExplodedNode *N = C.generateErrorNode(); 473 if (!N) 474 return; 475 auto Report = 476 std::make_unique<PathSensitiveBugReport>(BT_destroylock, Message, N); 477 Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); 478 C.emitReport(std::move(Report)); 479 } 480 481 void PthreadLockChecker::InitAnyLock(const CallEvent &Call, 482 CheckerContext &C) const { 483 InitLockAux(Call, C, 0, Call.getArgSVal(0)); 484 } 485 486 void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C, 487 unsigned ArgNo, SVal Lock) const { 488 489 const MemRegion *LockR = Lock.getAsRegion(); 490 if (!LockR) 491 return; 492 493 ProgramStateRef State = C.getState(); 494 495 const SymbolRef *sym = State->get<DestroyRetVal>(LockR); 496 if (sym) 497 State = resolvePossiblyDestroyedMutex(State, LockR, sym); 498 499 const struct LockState *LState = State->get<LockMap>(LockR); 500 if (!LState || LState->isDestroyed()) { 501 State = State->set<LockMap>(LockR, LockState::getUnlocked()); 502 C.addTransition(State); 503 return; 504 } 505 506 StringRef Message; 507 508 if (LState->isLocked()) { 509 Message = "This lock is still being held"; 510 } else { 511 Message = "This lock has already been initialized"; 512 } 513 514 ExplodedNode *N = C.generateErrorNode(); 515 if (!N) 516 return; 517 auto Report = 518 std::make_unique<PathSensitiveBugReport>(BT_initlock, Message, N); 519 Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); 520 C.emitReport(std::move(Report)); 521 } 522 523 void PthreadLockChecker::reportUseDestroyedBug(const CallEvent &Call, 524 CheckerContext &C, 525 unsigned ArgNo) const { 526 ExplodedNode *N = C.generateErrorNode(); 527 if (!N) 528 return; 529 auto Report = std::make_unique<PathSensitiveBugReport>( 530 BT_destroylock, "This lock has already been destroyed", N); 531 Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); 532 C.emitReport(std::move(Report)); 533 } 534 535 void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper, 536 CheckerContext &C) const { 537 ProgramStateRef State = C.getState(); 538 539 // TODO: Clean LockMap when a mutex region dies. 540 541 DestroyRetValTy TrackedSymbols = State->get<DestroyRetVal>(); 542 for (DestroyRetValTy::iterator I = TrackedSymbols.begin(), 543 E = TrackedSymbols.end(); 544 I != E; ++I) { 545 const SymbolRef Sym = I->second; 546 const MemRegion *lockR = I->first; 547 bool IsSymDead = SymReaper.isDead(Sym); 548 // Remove the dead symbol from the return value symbols map. 549 if (IsSymDead) 550 State = resolvePossiblyDestroyedMutex(State, lockR, &Sym); 551 } 552 C.addTransition(State); 553 } 554 555 ProgramStateRef PthreadLockChecker::checkRegionChanges( 556 ProgramStateRef State, const InvalidatedSymbols *Symbols, 557 ArrayRef<const MemRegion *> ExplicitRegions, 558 ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, 559 const CallEvent *Call) const { 560 561 bool IsLibraryFunction = false; 562 if (Call && Call->isGlobalCFunction()) { 563 // Avoid invalidating mutex state when a known supported function is called. 564 if (Callbacks.lookup(*Call)) 565 return State; 566 567 if (Call->isInSystemHeader()) 568 IsLibraryFunction = true; 569 } 570 571 for (auto R : Regions) { 572 // We assume that system library function wouldn't touch the mutex unless 573 // it takes the mutex explicitly as an argument. 574 // FIXME: This is a bit quadratic. 575 if (IsLibraryFunction && 576 std::find(ExplicitRegions.begin(), ExplicitRegions.end(), R) == 577 ExplicitRegions.end()) 578 continue; 579 580 State = State->remove<LockMap>(R); 581 State = State->remove<DestroyRetVal>(R); 582 583 // TODO: We need to invalidate the lock stack as well. This is tricky 584 // to implement correctly and efficiently though, because the effects 585 // of mutex escapes on lock order may be fairly varied. 586 } 587 588 return State; 589 } 590 591 void ento::registerPthreadLockChecker(CheckerManager &mgr) { 592 mgr.registerChecker<PthreadLockChecker>(); 593 } 594 595 bool ento::shouldRegisterPthreadLockChecker(const LangOptions &LO) { 596 return true; 597 } 598