xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp (revision 1484d0f12addf0d8aa08831adb63b8f56b8bd0f6)
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   for (auto I : State->get<DestroyRetVal>()) {
540     // Once the return value symbol dies, no more checks can be performed
541     // against it. See if the return value was checked before this point.
542     // This would remove the symbol from the map as well.
543     if (SymReaper.isDead(I.second))
544       State = resolvePossiblyDestroyedMutex(State, I.first, &I.second);
545   }
546 
547   for (auto I : State->get<LockMap>()) {
548     // Stop tracking dead mutex regions as well.
549     if (!SymReaper.isLiveRegion(I.first))
550       State = State->remove<LockMap>(I.first);
551   }
552 
553   // TODO: We probably need to clean up the lock stack as well.
554   // It is tricky though: even if the mutex cannot be unlocked anymore,
555   // it can still participate in lock order reversal resolution.
556 
557   C.addTransition(State);
558 }
559 
560 ProgramStateRef PthreadLockChecker::checkRegionChanges(
561     ProgramStateRef State, const InvalidatedSymbols *Symbols,
562     ArrayRef<const MemRegion *> ExplicitRegions,
563     ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
564     const CallEvent *Call) const {
565 
566   bool IsLibraryFunction = false;
567   if (Call && Call->isGlobalCFunction()) {
568     // Avoid invalidating mutex state when a known supported function is called.
569     if (Callbacks.lookup(*Call))
570         return State;
571 
572     if (Call->isInSystemHeader())
573       IsLibraryFunction = true;
574   }
575 
576   for (auto R : Regions) {
577     // We assume that system library function wouldn't touch the mutex unless
578     // it takes the mutex explicitly as an argument.
579     // FIXME: This is a bit quadratic.
580     if (IsLibraryFunction &&
581         std::find(ExplicitRegions.begin(), ExplicitRegions.end(), R) ==
582             ExplicitRegions.end())
583       continue;
584 
585     State = State->remove<LockMap>(R);
586     State = State->remove<DestroyRetVal>(R);
587 
588     // TODO: We need to invalidate the lock stack as well. This is tricky
589     // to implement correctly and efficiently though, because the effects
590     // of mutex escapes on lock order may be fairly varied.
591   }
592 
593   return State;
594 }
595 
596 void ento::registerPthreadLockChecker(CheckerManager &mgr) {
597   mgr.registerChecker<PthreadLockChecker>();
598 }
599 
600 bool ento::shouldRegisterPthreadLockChecker(const LangOptions &LO) {
601   return true;
602 }
603