xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
10b57cec5SDimitry Andric //===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
95ffd83dbSDimitry Andric // This file defines:
105ffd83dbSDimitry Andric //  * PthreadLockChecker, a simple lock -> unlock checker.
115ffd83dbSDimitry Andric //    Which also checks for XNU locks, which behave similarly enough to share
125ffd83dbSDimitry Andric //    code.
135ffd83dbSDimitry Andric //  * FuchsiaLocksChecker, which is also rather similar.
145ffd83dbSDimitry Andric //  * C11LockChecker which also closely follows Pthread semantics.
155ffd83dbSDimitry Andric //
165ffd83dbSDimitry Andric //  TODO: Path notes.
170b57cec5SDimitry Andric //
180b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
190b57cec5SDimitry Andric 
200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
230b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
24349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
255ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
260b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
270b57cec5SDimitry Andric 
280b57cec5SDimitry Andric using namespace clang;
290b57cec5SDimitry Andric using namespace ento;
300b57cec5SDimitry Andric 
310b57cec5SDimitry Andric namespace {
320b57cec5SDimitry Andric 
330b57cec5SDimitry Andric struct LockState {
340b57cec5SDimitry Andric   enum Kind {
350b57cec5SDimitry Andric     Destroyed,
360b57cec5SDimitry Andric     Locked,
370b57cec5SDimitry Andric     Unlocked,
380b57cec5SDimitry Andric     UntouchedAndPossiblyDestroyed,
390b57cec5SDimitry Andric     UnlockedAndPossiblyDestroyed
400b57cec5SDimitry Andric   } K;
410b57cec5SDimitry Andric 
420b57cec5SDimitry Andric private:
430b57cec5SDimitry Andric   LockState(Kind K) : K(K) {}
440b57cec5SDimitry Andric 
450b57cec5SDimitry Andric public:
460b57cec5SDimitry Andric   static LockState getLocked() { return LockState(Locked); }
470b57cec5SDimitry Andric   static LockState getUnlocked() { return LockState(Unlocked); }
480b57cec5SDimitry Andric   static LockState getDestroyed() { return LockState(Destroyed); }
490b57cec5SDimitry Andric   static LockState getUntouchedAndPossiblyDestroyed() {
500b57cec5SDimitry Andric     return LockState(UntouchedAndPossiblyDestroyed);
510b57cec5SDimitry Andric   }
520b57cec5SDimitry Andric   static LockState getUnlockedAndPossiblyDestroyed() {
530b57cec5SDimitry Andric     return LockState(UnlockedAndPossiblyDestroyed);
540b57cec5SDimitry Andric   }
550b57cec5SDimitry Andric 
565ffd83dbSDimitry Andric   bool operator==(const LockState &X) const { return K == X.K; }
570b57cec5SDimitry Andric 
580b57cec5SDimitry Andric   bool isLocked() const { return K == Locked; }
590b57cec5SDimitry Andric   bool isUnlocked() const { return K == Unlocked; }
600b57cec5SDimitry Andric   bool isDestroyed() const { return K == Destroyed; }
610b57cec5SDimitry Andric   bool isUntouchedAndPossiblyDestroyed() const {
620b57cec5SDimitry Andric     return K == UntouchedAndPossiblyDestroyed;
630b57cec5SDimitry Andric   }
640b57cec5SDimitry Andric   bool isUnlockedAndPossiblyDestroyed() const {
650b57cec5SDimitry Andric     return K == UnlockedAndPossiblyDestroyed;
660b57cec5SDimitry Andric   }
670b57cec5SDimitry Andric 
685ffd83dbSDimitry Andric   void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
690b57cec5SDimitry Andric };
700b57cec5SDimitry Andric 
715ffd83dbSDimitry Andric class PthreadLockChecker : public Checker<check::PostCall, check::DeadSymbols,
725ffd83dbSDimitry Andric                                           check::RegionChanges> {
730b57cec5SDimitry Andric public:
745ffd83dbSDimitry Andric   enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics };
755ffd83dbSDimitry Andric   enum CheckerKind {
765ffd83dbSDimitry Andric     CK_PthreadLockChecker,
775ffd83dbSDimitry Andric     CK_FuchsiaLockChecker,
785ffd83dbSDimitry Andric     CK_C11LockChecker,
795ffd83dbSDimitry Andric     CK_NumCheckKinds
805ffd83dbSDimitry Andric   };
8181ad6265SDimitry Andric   bool ChecksEnabled[CK_NumCheckKinds] = {false};
825ffd83dbSDimitry Andric   CheckerNameRef CheckNames[CK_NumCheckKinds];
830b57cec5SDimitry Andric 
845ffd83dbSDimitry Andric private:
855ffd83dbSDimitry Andric   typedef void (PthreadLockChecker::*FnCheck)(const CallEvent &Call,
865ffd83dbSDimitry Andric                                               CheckerContext &C,
87e8d8bef9SDimitry Andric                                               CheckerKind CheckKind) const;
885ffd83dbSDimitry Andric   CallDescriptionMap<FnCheck> PThreadCallbacks = {
895ffd83dbSDimitry Andric       // Init.
90*bdd1243dSDimitry Andric       {{{"pthread_mutex_init"}, 2}, &PthreadLockChecker::InitAnyLock},
915ffd83dbSDimitry Andric       // TODO: pthread_rwlock_init(2 arguments).
925ffd83dbSDimitry Andric       // TODO: lck_mtx_init(3 arguments).
935ffd83dbSDimitry Andric       // TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex.
945ffd83dbSDimitry Andric       // TODO: lck_rw_init(3 arguments).
955ffd83dbSDimitry Andric       // TODO: lck_rw_alloc_init(2 arguments) => returns the mutex.
960b57cec5SDimitry Andric 
975ffd83dbSDimitry Andric       // Acquire.
98*bdd1243dSDimitry Andric       {{{"pthread_mutex_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock},
99*bdd1243dSDimitry Andric       {{{"pthread_rwlock_rdlock"}, 1}, &PthreadLockChecker::AcquirePthreadLock},
100*bdd1243dSDimitry Andric       {{{"pthread_rwlock_wrlock"}, 1}, &PthreadLockChecker::AcquirePthreadLock},
101*bdd1243dSDimitry Andric       {{{"lck_mtx_lock"}, 1}, &PthreadLockChecker::AcquireXNULock},
102*bdd1243dSDimitry Andric       {{{"lck_rw_lock_exclusive"}, 1}, &PthreadLockChecker::AcquireXNULock},
103*bdd1243dSDimitry Andric       {{{"lck_rw_lock_shared"}, 1}, &PthreadLockChecker::AcquireXNULock},
1045ffd83dbSDimitry Andric 
1055ffd83dbSDimitry Andric       // Try.
106*bdd1243dSDimitry Andric       {{{"pthread_mutex_trylock"}, 1}, &PthreadLockChecker::TryPthreadLock},
107*bdd1243dSDimitry Andric       {{{"pthread_rwlock_tryrdlock"}, 1}, &PthreadLockChecker::TryPthreadLock},
108*bdd1243dSDimitry Andric       {{{"pthread_rwlock_trywrlock"}, 1}, &PthreadLockChecker::TryPthreadLock},
109*bdd1243dSDimitry Andric       {{{"lck_mtx_try_lock"}, 1}, &PthreadLockChecker::TryXNULock},
110*bdd1243dSDimitry Andric       {{{"lck_rw_try_lock_exclusive"}, 1}, &PthreadLockChecker::TryXNULock},
111*bdd1243dSDimitry Andric       {{{"lck_rw_try_lock_shared"}, 1}, &PthreadLockChecker::TryXNULock},
1125ffd83dbSDimitry Andric 
1135ffd83dbSDimitry Andric       // Release.
114*bdd1243dSDimitry Andric       {{{"pthread_mutex_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
115*bdd1243dSDimitry Andric       {{{"pthread_rwlock_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
116*bdd1243dSDimitry Andric       {{{"lck_mtx_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
117*bdd1243dSDimitry Andric       {{{"lck_rw_unlock_exclusive"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
118*bdd1243dSDimitry Andric       {{{"lck_rw_unlock_shared"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
119*bdd1243dSDimitry Andric       {{{"lck_rw_done"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
1205ffd83dbSDimitry Andric 
1215ffd83dbSDimitry Andric       // Destroy.
122*bdd1243dSDimitry Andric       {{{"pthread_mutex_destroy"}, 1}, &PthreadLockChecker::DestroyPthreadLock},
123*bdd1243dSDimitry Andric       {{{"lck_mtx_destroy"}, 2}, &PthreadLockChecker::DestroyXNULock},
1245ffd83dbSDimitry Andric       // TODO: pthread_rwlock_destroy(1 argument).
1255ffd83dbSDimitry Andric       // TODO: lck_rw_destroy(2 arguments).
1265ffd83dbSDimitry Andric   };
1275ffd83dbSDimitry Andric 
1285ffd83dbSDimitry Andric   CallDescriptionMap<FnCheck> FuchsiaCallbacks = {
1295ffd83dbSDimitry Andric       // Init.
130*bdd1243dSDimitry Andric       {{{"spin_lock_init"}, 1}, &PthreadLockChecker::InitAnyLock},
1315ffd83dbSDimitry Andric 
1325ffd83dbSDimitry Andric       // Acquire.
133*bdd1243dSDimitry Andric       {{{"spin_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock},
134*bdd1243dSDimitry Andric       {{{"spin_lock_save"}, 3}, &PthreadLockChecker::AcquirePthreadLock},
135*bdd1243dSDimitry Andric       {{{"sync_mutex_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock},
136*bdd1243dSDimitry Andric       {{{"sync_mutex_lock_with_waiter"}, 1},
1375ffd83dbSDimitry Andric        &PthreadLockChecker::AcquirePthreadLock},
1385ffd83dbSDimitry Andric 
1395ffd83dbSDimitry Andric       // Try.
140*bdd1243dSDimitry Andric       {{{"spin_trylock"}, 1}, &PthreadLockChecker::TryFuchsiaLock},
141*bdd1243dSDimitry Andric       {{{"sync_mutex_trylock"}, 1}, &PthreadLockChecker::TryFuchsiaLock},
142*bdd1243dSDimitry Andric       {{{"sync_mutex_timedlock"}, 2}, &PthreadLockChecker::TryFuchsiaLock},
1435ffd83dbSDimitry Andric 
1445ffd83dbSDimitry Andric       // Release.
145*bdd1243dSDimitry Andric       {{{"spin_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
146*bdd1243dSDimitry Andric       {{{"spin_unlock_restore"}, 3}, &PthreadLockChecker::ReleaseAnyLock},
147*bdd1243dSDimitry Andric       {{{"sync_mutex_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
1485ffd83dbSDimitry Andric   };
1495ffd83dbSDimitry Andric 
1505ffd83dbSDimitry Andric   CallDescriptionMap<FnCheck> C11Callbacks = {
1515ffd83dbSDimitry Andric       // Init.
152*bdd1243dSDimitry Andric       {{{"mtx_init"}, 2}, &PthreadLockChecker::InitAnyLock},
1535ffd83dbSDimitry Andric 
1545ffd83dbSDimitry Andric       // Acquire.
155*bdd1243dSDimitry Andric       {{{"mtx_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock},
1565ffd83dbSDimitry Andric 
1575ffd83dbSDimitry Andric       // Try.
158*bdd1243dSDimitry Andric       {{{"mtx_trylock"}, 1}, &PthreadLockChecker::TryC11Lock},
159*bdd1243dSDimitry Andric       {{{"mtx_timedlock"}, 2}, &PthreadLockChecker::TryC11Lock},
1605ffd83dbSDimitry Andric 
1615ffd83dbSDimitry Andric       // Release.
162*bdd1243dSDimitry Andric       {{{"mtx_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
1635ffd83dbSDimitry Andric 
1645ffd83dbSDimitry Andric       // Destroy
165*bdd1243dSDimitry Andric       {{{"mtx_destroy"}, 1}, &PthreadLockChecker::DestroyPthreadLock},
1665ffd83dbSDimitry Andric   };
1675ffd83dbSDimitry Andric 
1680b57cec5SDimitry Andric   ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state,
1690b57cec5SDimitry Andric                                                 const MemRegion *lockR,
1700b57cec5SDimitry Andric                                                 const SymbolRef *sym) const;
171e8d8bef9SDimitry Andric   void reportBug(CheckerContext &C, std::unique_ptr<BugType> BT[],
172e8d8bef9SDimitry Andric                  const Expr *MtxExpr, CheckerKind CheckKind,
173e8d8bef9SDimitry Andric                  StringRef Desc) const;
1745ffd83dbSDimitry Andric 
1755ffd83dbSDimitry Andric   // Init.
1765ffd83dbSDimitry Andric   void InitAnyLock(const CallEvent &Call, CheckerContext &C,
177e8d8bef9SDimitry Andric                    CheckerKind CheckKind) const;
178e8d8bef9SDimitry Andric   void InitLockAux(const CallEvent &Call, CheckerContext &C,
179e8d8bef9SDimitry Andric                    const Expr *MtxExpr, SVal MtxVal,
180e8d8bef9SDimitry Andric                    CheckerKind CheckKind) const;
1815ffd83dbSDimitry Andric 
1825ffd83dbSDimitry Andric   // Lock, Try-lock.
1835ffd83dbSDimitry Andric   void AcquirePthreadLock(const CallEvent &Call, CheckerContext &C,
184e8d8bef9SDimitry Andric                           CheckerKind CheckKind) const;
1855ffd83dbSDimitry Andric   void AcquireXNULock(const CallEvent &Call, CheckerContext &C,
186e8d8bef9SDimitry Andric                       CheckerKind CheckKind) const;
1875ffd83dbSDimitry Andric   void TryPthreadLock(const CallEvent &Call, CheckerContext &C,
188e8d8bef9SDimitry Andric                       CheckerKind CheckKind) const;
1895ffd83dbSDimitry Andric   void TryXNULock(const CallEvent &Call, CheckerContext &C,
190e8d8bef9SDimitry Andric                   CheckerKind CheckKind) const;
1915ffd83dbSDimitry Andric   void TryFuchsiaLock(const CallEvent &Call, CheckerContext &C,
192e8d8bef9SDimitry Andric                       CheckerKind CheckKind) const;
1935ffd83dbSDimitry Andric   void TryC11Lock(const CallEvent &Call, CheckerContext &C,
194e8d8bef9SDimitry Andric                   CheckerKind CheckKind) const;
195e8d8bef9SDimitry Andric   void AcquireLockAux(const CallEvent &Call, CheckerContext &C,
196e8d8bef9SDimitry Andric                       const Expr *MtxExpr, SVal MtxVal, bool IsTryLock,
197e8d8bef9SDimitry Andric                       LockingSemantics Semantics, CheckerKind CheckKind) const;
1985ffd83dbSDimitry Andric 
1995ffd83dbSDimitry Andric   // Release.
2005ffd83dbSDimitry Andric   void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C,
201e8d8bef9SDimitry Andric                       CheckerKind CheckKind) const;
202e8d8bef9SDimitry Andric   void ReleaseLockAux(const CallEvent &Call, CheckerContext &C,
203e8d8bef9SDimitry Andric                       const Expr *MtxExpr, SVal MtxVal,
204e8d8bef9SDimitry Andric                       CheckerKind CheckKind) const;
2055ffd83dbSDimitry Andric 
2065ffd83dbSDimitry Andric   // Destroy.
2075ffd83dbSDimitry Andric   void DestroyPthreadLock(const CallEvent &Call, CheckerContext &C,
208e8d8bef9SDimitry Andric                           CheckerKind CheckKind) const;
2095ffd83dbSDimitry Andric   void DestroyXNULock(const CallEvent &Call, CheckerContext &C,
210e8d8bef9SDimitry Andric                       CheckerKind CheckKind) const;
211e8d8bef9SDimitry Andric   void DestroyLockAux(const CallEvent &Call, CheckerContext &C,
212e8d8bef9SDimitry Andric                       const Expr *MtxExpr, SVal MtxVal,
213e8d8bef9SDimitry Andric                       LockingSemantics Semantics, CheckerKind CheckKind) const;
2145ffd83dbSDimitry Andric 
2155ffd83dbSDimitry Andric public:
2165ffd83dbSDimitry Andric   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
2175ffd83dbSDimitry Andric   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
2185ffd83dbSDimitry Andric   ProgramStateRef
2195ffd83dbSDimitry Andric   checkRegionChanges(ProgramStateRef State, const InvalidatedSymbols *Symbols,
2205ffd83dbSDimitry Andric                      ArrayRef<const MemRegion *> ExplicitRegions,
2215ffd83dbSDimitry Andric                      ArrayRef<const MemRegion *> Regions,
2225ffd83dbSDimitry Andric                      const LocationContext *LCtx, const CallEvent *Call) const;
2235ffd83dbSDimitry Andric   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
2245ffd83dbSDimitry Andric                   const char *Sep) const override;
2255ffd83dbSDimitry Andric 
2265ffd83dbSDimitry Andric private:
2275ffd83dbSDimitry Andric   mutable std::unique_ptr<BugType> BT_doublelock[CK_NumCheckKinds];
2285ffd83dbSDimitry Andric   mutable std::unique_ptr<BugType> BT_doubleunlock[CK_NumCheckKinds];
2295ffd83dbSDimitry Andric   mutable std::unique_ptr<BugType> BT_destroylock[CK_NumCheckKinds];
2305ffd83dbSDimitry Andric   mutable std::unique_ptr<BugType> BT_initlock[CK_NumCheckKinds];
2315ffd83dbSDimitry Andric   mutable std::unique_ptr<BugType> BT_lor[CK_NumCheckKinds];
2325ffd83dbSDimitry Andric 
233e8d8bef9SDimitry Andric   void initBugType(CheckerKind CheckKind) const {
234e8d8bef9SDimitry Andric     if (BT_doublelock[CheckKind])
2355ffd83dbSDimitry Andric       return;
236e8d8bef9SDimitry Andric     BT_doublelock[CheckKind].reset(
237e8d8bef9SDimitry Andric         new BugType{CheckNames[CheckKind], "Double locking", "Lock checker"});
238e8d8bef9SDimitry Andric     BT_doubleunlock[CheckKind].reset(
239e8d8bef9SDimitry Andric         new BugType{CheckNames[CheckKind], "Double unlocking", "Lock checker"});
240e8d8bef9SDimitry Andric     BT_destroylock[CheckKind].reset(new BugType{
241e8d8bef9SDimitry Andric         CheckNames[CheckKind], "Use destroyed lock", "Lock checker"});
242e8d8bef9SDimitry Andric     BT_initlock[CheckKind].reset(new BugType{
243e8d8bef9SDimitry Andric         CheckNames[CheckKind], "Init invalid lock", "Lock checker"});
244e8d8bef9SDimitry Andric     BT_lor[CheckKind].reset(new BugType{CheckNames[CheckKind],
2455ffd83dbSDimitry Andric                                         "Lock order reversal", "Lock checker"});
2465ffd83dbSDimitry Andric   }
2470b57cec5SDimitry Andric };
2480b57cec5SDimitry Andric } // end anonymous namespace
2490b57cec5SDimitry Andric 
2500b57cec5SDimitry Andric // A stack of locks for tracking lock-unlock order.
2510b57cec5SDimitry Andric REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *)
2520b57cec5SDimitry Andric 
2530b57cec5SDimitry Andric // An entry for tracking lock states.
2540b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState)
2550b57cec5SDimitry Andric 
2560b57cec5SDimitry Andric // Return values for unresolved calls to pthread_mutex_destroy().
2570b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(DestroyRetVal, const MemRegion *, SymbolRef)
2580b57cec5SDimitry Andric 
2595ffd83dbSDimitry Andric void PthreadLockChecker::checkPostCall(const CallEvent &Call,
2600b57cec5SDimitry Andric                                        CheckerContext &C) const {
2615ffd83dbSDimitry Andric   // An additional umbrella check that all functions modeled by this checker
2625ffd83dbSDimitry Andric   // are global C functions.
2635ffd83dbSDimitry Andric   // TODO: Maybe make this the default behavior of CallDescription
2645ffd83dbSDimitry Andric   // with exactly one identifier?
2655ffd83dbSDimitry Andric   // FIXME: Try to handle cases when the implementation was inlined rather
2665ffd83dbSDimitry Andric   // than just giving up.
2675ffd83dbSDimitry Andric   if (!Call.isGlobalCFunction() || C.wasInlined)
2680b57cec5SDimitry Andric     return;
2690b57cec5SDimitry Andric 
2705ffd83dbSDimitry Andric   if (const FnCheck *Callback = PThreadCallbacks.lookup(Call))
2715ffd83dbSDimitry Andric     (this->**Callback)(Call, C, CK_PthreadLockChecker);
2725ffd83dbSDimitry Andric   else if (const FnCheck *Callback = FuchsiaCallbacks.lookup(Call))
2735ffd83dbSDimitry Andric     (this->**Callback)(Call, C, CK_FuchsiaLockChecker);
2745ffd83dbSDimitry Andric   else if (const FnCheck *Callback = C11Callbacks.lookup(Call))
2755ffd83dbSDimitry Andric     (this->**Callback)(Call, C, CK_C11LockChecker);
2760b57cec5SDimitry Andric }
2770b57cec5SDimitry Andric 
2780b57cec5SDimitry Andric // When a lock is destroyed, in some semantics(like PthreadSemantics) we are not
2790b57cec5SDimitry Andric // sure if the destroy call has succeeded or failed, and the lock enters one of
2800b57cec5SDimitry Andric // the 'possibly destroyed' state. There is a short time frame for the
2810b57cec5SDimitry Andric // programmer to check the return value to see if the lock was successfully
2820b57cec5SDimitry Andric // destroyed. Before we model the next operation over that lock, we call this
2830b57cec5SDimitry Andric // function to see if the return value was checked by now and set the lock state
2840b57cec5SDimitry Andric // - either to destroyed state or back to its previous state.
2850b57cec5SDimitry Andric 
2860b57cec5SDimitry Andric // In PthreadSemantics, pthread_mutex_destroy() returns zero if the lock is
2870b57cec5SDimitry Andric // successfully destroyed and it returns a non-zero value otherwise.
2880b57cec5SDimitry Andric ProgramStateRef PthreadLockChecker::resolvePossiblyDestroyedMutex(
2890b57cec5SDimitry Andric     ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const {
2900b57cec5SDimitry Andric   const LockState *lstate = state->get<LockMap>(lockR);
2910b57cec5SDimitry Andric   // Existence in DestroyRetVal ensures existence in LockMap.
2920b57cec5SDimitry Andric   // Existence in Destroyed also ensures that the lock state for lockR is either
2930b57cec5SDimitry Andric   // UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed.
2940b57cec5SDimitry Andric   assert(lstate->isUntouchedAndPossiblyDestroyed() ||
2950b57cec5SDimitry Andric          lstate->isUnlockedAndPossiblyDestroyed());
2960b57cec5SDimitry Andric 
2970b57cec5SDimitry Andric   ConstraintManager &CMgr = state->getConstraintManager();
2980b57cec5SDimitry Andric   ConditionTruthVal retZero = CMgr.isNull(state, *sym);
2990b57cec5SDimitry Andric   if (retZero.isConstrainedFalse()) {
3000b57cec5SDimitry Andric     if (lstate->isUntouchedAndPossiblyDestroyed())
3010b57cec5SDimitry Andric       state = state->remove<LockMap>(lockR);
3020b57cec5SDimitry Andric     else if (lstate->isUnlockedAndPossiblyDestroyed())
3030b57cec5SDimitry Andric       state = state->set<LockMap>(lockR, LockState::getUnlocked());
3040b57cec5SDimitry Andric   } else
3050b57cec5SDimitry Andric     state = state->set<LockMap>(lockR, LockState::getDestroyed());
3060b57cec5SDimitry Andric 
3070b57cec5SDimitry Andric   // Removing the map entry (lockR, sym) from DestroyRetVal as the lock state is
3080b57cec5SDimitry Andric   // now resolved.
3090b57cec5SDimitry Andric   state = state->remove<DestroyRetVal>(lockR);
3100b57cec5SDimitry Andric   return state;
3110b57cec5SDimitry Andric }
3120b57cec5SDimitry Andric 
3130b57cec5SDimitry Andric void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State,
3140b57cec5SDimitry Andric                                     const char *NL, const char *Sep) const {
3150b57cec5SDimitry Andric   LockMapTy LM = State->get<LockMap>();
3160b57cec5SDimitry Andric   if (!LM.isEmpty()) {
3170b57cec5SDimitry Andric     Out << Sep << "Mutex states:" << NL;
3180b57cec5SDimitry Andric     for (auto I : LM) {
3190b57cec5SDimitry Andric       I.first->dumpToStream(Out);
3200b57cec5SDimitry Andric       if (I.second.isLocked())
3210b57cec5SDimitry Andric         Out << ": locked";
3220b57cec5SDimitry Andric       else if (I.second.isUnlocked())
3230b57cec5SDimitry Andric         Out << ": unlocked";
3240b57cec5SDimitry Andric       else if (I.second.isDestroyed())
3250b57cec5SDimitry Andric         Out << ": destroyed";
3260b57cec5SDimitry Andric       else if (I.second.isUntouchedAndPossiblyDestroyed())
3270b57cec5SDimitry Andric         Out << ": not tracked, possibly destroyed";
3280b57cec5SDimitry Andric       else if (I.second.isUnlockedAndPossiblyDestroyed())
3290b57cec5SDimitry Andric         Out << ": unlocked, possibly destroyed";
3300b57cec5SDimitry Andric       Out << NL;
3310b57cec5SDimitry Andric     }
3320b57cec5SDimitry Andric   }
3330b57cec5SDimitry Andric 
3340b57cec5SDimitry Andric   LockSetTy LS = State->get<LockSet>();
3350b57cec5SDimitry Andric   if (!LS.isEmpty()) {
3360b57cec5SDimitry Andric     Out << Sep << "Mutex lock order:" << NL;
3370b57cec5SDimitry Andric     for (auto I : LS) {
3380b57cec5SDimitry Andric       I->dumpToStream(Out);
3390b57cec5SDimitry Andric       Out << NL;
3400b57cec5SDimitry Andric     }
3410b57cec5SDimitry Andric   }
3420b57cec5SDimitry Andric 
343fe6060f1SDimitry Andric   DestroyRetValTy DRV = State->get<DestroyRetVal>();
344fe6060f1SDimitry Andric   if (!DRV.isEmpty()) {
345fe6060f1SDimitry Andric     Out << Sep << "Mutexes in unresolved possibly destroyed state:" << NL;
346fe6060f1SDimitry Andric     for (auto I : DRV) {
347fe6060f1SDimitry Andric       I.first->dumpToStream(Out);
348fe6060f1SDimitry Andric       Out << ": ";
349fe6060f1SDimitry Andric       I.second->dumpToStream(Out);
350fe6060f1SDimitry Andric       Out << NL;
351fe6060f1SDimitry Andric     }
352fe6060f1SDimitry Andric   }
3530b57cec5SDimitry Andric }
3540b57cec5SDimitry Andric 
3555ffd83dbSDimitry Andric void PthreadLockChecker::AcquirePthreadLock(const CallEvent &Call,
3565ffd83dbSDimitry Andric                                             CheckerContext &C,
357e8d8bef9SDimitry Andric                                             CheckerKind CheckKind) const {
358e8d8bef9SDimitry Andric   AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false,
359e8d8bef9SDimitry Andric                  PthreadSemantics, CheckKind);
3605ffd83dbSDimitry Andric }
3615ffd83dbSDimitry Andric 
3625ffd83dbSDimitry Andric void PthreadLockChecker::AcquireXNULock(const CallEvent &Call,
3635ffd83dbSDimitry Andric                                         CheckerContext &C,
364e8d8bef9SDimitry Andric                                         CheckerKind CheckKind) const {
365e8d8bef9SDimitry Andric   AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false,
366e8d8bef9SDimitry Andric                  XNUSemantics, CheckKind);
3675ffd83dbSDimitry Andric }
3685ffd83dbSDimitry Andric 
3695ffd83dbSDimitry Andric void PthreadLockChecker::TryPthreadLock(const CallEvent &Call,
3705ffd83dbSDimitry Andric                                         CheckerContext &C,
371e8d8bef9SDimitry Andric                                         CheckerKind CheckKind) const {
372e8d8bef9SDimitry Andric   AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
373e8d8bef9SDimitry Andric                  PthreadSemantics, CheckKind);
3745ffd83dbSDimitry Andric }
3755ffd83dbSDimitry Andric 
3765ffd83dbSDimitry Andric void PthreadLockChecker::TryXNULock(const CallEvent &Call, CheckerContext &C,
377e8d8bef9SDimitry Andric                                     CheckerKind CheckKind) const {
378e8d8bef9SDimitry Andric   AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
379e8d8bef9SDimitry Andric                  PthreadSemantics, CheckKind);
3805ffd83dbSDimitry Andric }
3815ffd83dbSDimitry Andric 
3825ffd83dbSDimitry Andric void PthreadLockChecker::TryFuchsiaLock(const CallEvent &Call,
3835ffd83dbSDimitry Andric                                         CheckerContext &C,
384e8d8bef9SDimitry Andric                                         CheckerKind CheckKind) const {
385e8d8bef9SDimitry Andric   AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
386e8d8bef9SDimitry Andric                  PthreadSemantics, CheckKind);
3875ffd83dbSDimitry Andric }
3885ffd83dbSDimitry Andric 
3895ffd83dbSDimitry Andric void PthreadLockChecker::TryC11Lock(const CallEvent &Call, CheckerContext &C,
390e8d8bef9SDimitry Andric                                     CheckerKind CheckKind) const {
391e8d8bef9SDimitry Andric   AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
392e8d8bef9SDimitry Andric                  PthreadSemantics, CheckKind);
3935ffd83dbSDimitry Andric }
3945ffd83dbSDimitry Andric 
3955ffd83dbSDimitry Andric void PthreadLockChecker::AcquireLockAux(const CallEvent &Call,
396e8d8bef9SDimitry Andric                                         CheckerContext &C, const Expr *MtxExpr,
397e8d8bef9SDimitry Andric                                         SVal MtxVal, bool IsTryLock,
398e8d8bef9SDimitry Andric                                         enum LockingSemantics Semantics,
399e8d8bef9SDimitry Andric                                         CheckerKind CheckKind) const {
400e8d8bef9SDimitry Andric   if (!ChecksEnabled[CheckKind])
4015ffd83dbSDimitry Andric     return;
4020b57cec5SDimitry Andric 
403e8d8bef9SDimitry Andric   const MemRegion *lockR = MtxVal.getAsRegion();
4040b57cec5SDimitry Andric   if (!lockR)
4050b57cec5SDimitry Andric     return;
4060b57cec5SDimitry Andric 
4070b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
4080b57cec5SDimitry Andric   const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
4090b57cec5SDimitry Andric   if (sym)
4100b57cec5SDimitry Andric     state = resolvePossiblyDestroyedMutex(state, lockR, sym);
4110b57cec5SDimitry Andric 
4120b57cec5SDimitry Andric   if (const LockState *LState = state->get<LockMap>(lockR)) {
4130b57cec5SDimitry Andric     if (LState->isLocked()) {
414e8d8bef9SDimitry Andric       reportBug(C, BT_doublelock, MtxExpr, CheckKind,
415e8d8bef9SDimitry Andric                 "This lock has already been acquired");
4160b57cec5SDimitry Andric       return;
4170b57cec5SDimitry Andric     } else if (LState->isDestroyed()) {
418e8d8bef9SDimitry Andric       reportBug(C, BT_destroylock, MtxExpr, CheckKind,
419e8d8bef9SDimitry Andric                 "This lock has already been destroyed");
4200b57cec5SDimitry Andric       return;
4210b57cec5SDimitry Andric     }
4220b57cec5SDimitry Andric   }
4230b57cec5SDimitry Andric 
4240b57cec5SDimitry Andric   ProgramStateRef lockSucc = state;
425e8d8bef9SDimitry Andric   if (IsTryLock) {
4260b57cec5SDimitry Andric     // Bifurcate the state, and allow a mode where the lock acquisition fails.
4275ffd83dbSDimitry Andric     SVal RetVal = Call.getReturnValue();
4285ffd83dbSDimitry Andric     if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
4290b57cec5SDimitry Andric       ProgramStateRef lockFail;
430e8d8bef9SDimitry Andric       switch (Semantics) {
4310b57cec5SDimitry Andric       case PthreadSemantics:
4325ffd83dbSDimitry Andric         std::tie(lockFail, lockSucc) = state->assume(*DefinedRetVal);
4330b57cec5SDimitry Andric         break;
4340b57cec5SDimitry Andric       case XNUSemantics:
4355ffd83dbSDimitry Andric         std::tie(lockSucc, lockFail) = state->assume(*DefinedRetVal);
4360b57cec5SDimitry Andric         break;
4370b57cec5SDimitry Andric       default:
4380b57cec5SDimitry Andric         llvm_unreachable("Unknown tryLock locking semantics");
4390b57cec5SDimitry Andric       }
4400b57cec5SDimitry Andric       assert(lockFail && lockSucc);
4410b57cec5SDimitry Andric       C.addTransition(lockFail);
4425ffd83dbSDimitry Andric     }
4435ffd83dbSDimitry Andric     // We might want to handle the case when the mutex lock function was inlined
4445ffd83dbSDimitry Andric     // and returned an Unknown or Undefined value.
445e8d8bef9SDimitry Andric   } else if (Semantics == PthreadSemantics) {
4460b57cec5SDimitry Andric     // Assume that the return value was 0.
4475ffd83dbSDimitry Andric     SVal RetVal = Call.getReturnValue();
4485ffd83dbSDimitry Andric     if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
4495ffd83dbSDimitry Andric       // FIXME: If the lock function was inlined and returned true,
4505ffd83dbSDimitry Andric       // we need to behave sanely - at least generate sink.
4515ffd83dbSDimitry Andric       lockSucc = state->assume(*DefinedRetVal, false);
4520b57cec5SDimitry Andric       assert(lockSucc);
4535ffd83dbSDimitry Andric     }
4545ffd83dbSDimitry Andric     // We might want to handle the case when the mutex lock function was inlined
4555ffd83dbSDimitry Andric     // and returned an Unknown or Undefined value.
4560b57cec5SDimitry Andric   } else {
4570b57cec5SDimitry Andric     // XNU locking semantics return void on non-try locks
458e8d8bef9SDimitry Andric     assert((Semantics == XNUSemantics) && "Unknown locking semantics");
4590b57cec5SDimitry Andric     lockSucc = state;
4600b57cec5SDimitry Andric   }
4610b57cec5SDimitry Andric 
4620b57cec5SDimitry Andric   // Record that the lock was acquired.
4630b57cec5SDimitry Andric   lockSucc = lockSucc->add<LockSet>(lockR);
4640b57cec5SDimitry Andric   lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked());
4650b57cec5SDimitry Andric   C.addTransition(lockSucc);
4660b57cec5SDimitry Andric }
4670b57cec5SDimitry Andric 
4685ffd83dbSDimitry Andric void PthreadLockChecker::ReleaseAnyLock(const CallEvent &Call,
4695ffd83dbSDimitry Andric                                         CheckerContext &C,
470e8d8bef9SDimitry Andric                                         CheckerKind CheckKind) const {
471e8d8bef9SDimitry Andric   ReleaseLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), CheckKind);
4725ffd83dbSDimitry Andric }
4735ffd83dbSDimitry Andric 
4745ffd83dbSDimitry Andric void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call,
475e8d8bef9SDimitry Andric                                         CheckerContext &C, const Expr *MtxExpr,
476e8d8bef9SDimitry Andric                                         SVal MtxVal,
477e8d8bef9SDimitry Andric                                         CheckerKind CheckKind) const {
478e8d8bef9SDimitry Andric   if (!ChecksEnabled[CheckKind])
4795ffd83dbSDimitry Andric     return;
4800b57cec5SDimitry Andric 
481e8d8bef9SDimitry Andric   const MemRegion *lockR = MtxVal.getAsRegion();
4820b57cec5SDimitry Andric   if (!lockR)
4830b57cec5SDimitry Andric     return;
4840b57cec5SDimitry Andric 
4850b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
4860b57cec5SDimitry Andric   const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
4870b57cec5SDimitry Andric   if (sym)
4880b57cec5SDimitry Andric     state = resolvePossiblyDestroyedMutex(state, lockR, sym);
4890b57cec5SDimitry Andric 
4900b57cec5SDimitry Andric   if (const LockState *LState = state->get<LockMap>(lockR)) {
4910b57cec5SDimitry Andric     if (LState->isUnlocked()) {
492e8d8bef9SDimitry Andric       reportBug(C, BT_doubleunlock, MtxExpr, CheckKind,
493e8d8bef9SDimitry Andric                 "This lock has already been unlocked");
4940b57cec5SDimitry Andric       return;
4950b57cec5SDimitry Andric     } else if (LState->isDestroyed()) {
496e8d8bef9SDimitry Andric       reportBug(C, BT_destroylock, MtxExpr, CheckKind,
497e8d8bef9SDimitry Andric                 "This lock has already been destroyed");
4980b57cec5SDimitry Andric       return;
4990b57cec5SDimitry Andric     }
5000b57cec5SDimitry Andric   }
5010b57cec5SDimitry Andric 
5020b57cec5SDimitry Andric   LockSetTy LS = state->get<LockSet>();
5030b57cec5SDimitry Andric 
5040b57cec5SDimitry Andric   if (!LS.isEmpty()) {
5050b57cec5SDimitry Andric     const MemRegion *firstLockR = LS.getHead();
5060b57cec5SDimitry Andric     if (firstLockR != lockR) {
507e8d8bef9SDimitry Andric       reportBug(C, BT_lor, MtxExpr, CheckKind,
508e8d8bef9SDimitry Andric                 "This was not the most recently acquired lock. Possible lock "
509e8d8bef9SDimitry Andric                 "order reversal");
5100b57cec5SDimitry Andric       return;
5110b57cec5SDimitry Andric     }
5120b57cec5SDimitry Andric     // Record that the lock was released.
5130b57cec5SDimitry Andric     state = state->set<LockSet>(LS.getTail());
5140b57cec5SDimitry Andric   }
5150b57cec5SDimitry Andric 
5160b57cec5SDimitry Andric   state = state->set<LockMap>(lockR, LockState::getUnlocked());
5170b57cec5SDimitry Andric   C.addTransition(state);
5180b57cec5SDimitry Andric }
5190b57cec5SDimitry Andric 
5205ffd83dbSDimitry Andric void PthreadLockChecker::DestroyPthreadLock(const CallEvent &Call,
5215ffd83dbSDimitry Andric                                             CheckerContext &C,
522e8d8bef9SDimitry Andric                                             CheckerKind CheckKind) const {
523e8d8bef9SDimitry Andric   DestroyLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0),
524e8d8bef9SDimitry Andric                  PthreadSemantics, CheckKind);
5255ffd83dbSDimitry Andric }
5265ffd83dbSDimitry Andric 
5275ffd83dbSDimitry Andric void PthreadLockChecker::DestroyXNULock(const CallEvent &Call,
5285ffd83dbSDimitry Andric                                         CheckerContext &C,
529e8d8bef9SDimitry Andric                                         CheckerKind CheckKind) const {
530e8d8bef9SDimitry Andric   DestroyLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), XNUSemantics,
531e8d8bef9SDimitry Andric                  CheckKind);
5325ffd83dbSDimitry Andric }
5335ffd83dbSDimitry Andric 
5345ffd83dbSDimitry Andric void PthreadLockChecker::DestroyLockAux(const CallEvent &Call,
535e8d8bef9SDimitry Andric                                         CheckerContext &C, const Expr *MtxExpr,
536e8d8bef9SDimitry Andric                                         SVal MtxVal,
537e8d8bef9SDimitry Andric                                         enum LockingSemantics Semantics,
538e8d8bef9SDimitry Andric                                         CheckerKind CheckKind) const {
539e8d8bef9SDimitry Andric   if (!ChecksEnabled[CheckKind])
5405ffd83dbSDimitry Andric     return;
5410b57cec5SDimitry Andric 
542e8d8bef9SDimitry Andric   const MemRegion *LockR = MtxVal.getAsRegion();
5430b57cec5SDimitry Andric   if (!LockR)
5440b57cec5SDimitry Andric     return;
5450b57cec5SDimitry Andric 
5460b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
5470b57cec5SDimitry Andric 
5480b57cec5SDimitry Andric   const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
5490b57cec5SDimitry Andric   if (sym)
5500b57cec5SDimitry Andric     State = resolvePossiblyDestroyedMutex(State, LockR, sym);
5510b57cec5SDimitry Andric 
5520b57cec5SDimitry Andric   const LockState *LState = State->get<LockMap>(LockR);
5530b57cec5SDimitry Andric   // Checking the return value of the destroy method only in the case of
5540b57cec5SDimitry Andric   // PthreadSemantics
555e8d8bef9SDimitry Andric   if (Semantics == PthreadSemantics) {
5560b57cec5SDimitry Andric     if (!LState || LState->isUnlocked()) {
5575ffd83dbSDimitry Andric       SymbolRef sym = Call.getReturnValue().getAsSymbol();
5580b57cec5SDimitry Andric       if (!sym) {
5590b57cec5SDimitry Andric         State = State->remove<LockMap>(LockR);
5600b57cec5SDimitry Andric         C.addTransition(State);
5610b57cec5SDimitry Andric         return;
5620b57cec5SDimitry Andric       }
5630b57cec5SDimitry Andric       State = State->set<DestroyRetVal>(LockR, sym);
5640b57cec5SDimitry Andric       if (LState && LState->isUnlocked())
5650b57cec5SDimitry Andric         State = State->set<LockMap>(
5660b57cec5SDimitry Andric             LockR, LockState::getUnlockedAndPossiblyDestroyed());
5670b57cec5SDimitry Andric       else
5680b57cec5SDimitry Andric         State = State->set<LockMap>(
5690b57cec5SDimitry Andric             LockR, LockState::getUntouchedAndPossiblyDestroyed());
5700b57cec5SDimitry Andric       C.addTransition(State);
5710b57cec5SDimitry Andric       return;
5720b57cec5SDimitry Andric     }
5730b57cec5SDimitry Andric   } else {
5740b57cec5SDimitry Andric     if (!LState || LState->isUnlocked()) {
5750b57cec5SDimitry Andric       State = State->set<LockMap>(LockR, LockState::getDestroyed());
5760b57cec5SDimitry Andric       C.addTransition(State);
5770b57cec5SDimitry Andric       return;
5780b57cec5SDimitry Andric     }
5790b57cec5SDimitry Andric   }
5800b57cec5SDimitry Andric 
581e8d8bef9SDimitry Andric   StringRef Message = LState->isLocked()
582e8d8bef9SDimitry Andric                           ? "This lock is still locked"
583e8d8bef9SDimitry Andric                           : "This lock has already been destroyed";
5840b57cec5SDimitry Andric 
585e8d8bef9SDimitry Andric   reportBug(C, BT_destroylock, MtxExpr, CheckKind, Message);
5860b57cec5SDimitry Andric }
5870b57cec5SDimitry Andric 
5885ffd83dbSDimitry Andric void PthreadLockChecker::InitAnyLock(const CallEvent &Call, CheckerContext &C,
589e8d8bef9SDimitry Andric                                      CheckerKind CheckKind) const {
590e8d8bef9SDimitry Andric   InitLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), CheckKind);
5915ffd83dbSDimitry Andric }
5925ffd83dbSDimitry Andric 
5935ffd83dbSDimitry Andric void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C,
594e8d8bef9SDimitry Andric                                      const Expr *MtxExpr, SVal MtxVal,
595e8d8bef9SDimitry Andric                                      CheckerKind CheckKind) const {
596e8d8bef9SDimitry Andric   if (!ChecksEnabled[CheckKind])
5975ffd83dbSDimitry Andric     return;
5980b57cec5SDimitry Andric 
599e8d8bef9SDimitry Andric   const MemRegion *LockR = MtxVal.getAsRegion();
6000b57cec5SDimitry Andric   if (!LockR)
6010b57cec5SDimitry Andric     return;
6020b57cec5SDimitry Andric 
6030b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
6040b57cec5SDimitry Andric 
6050b57cec5SDimitry Andric   const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
6060b57cec5SDimitry Andric   if (sym)
6070b57cec5SDimitry Andric     State = resolvePossiblyDestroyedMutex(State, LockR, sym);
6080b57cec5SDimitry Andric 
6090b57cec5SDimitry Andric   const struct LockState *LState = State->get<LockMap>(LockR);
6100b57cec5SDimitry Andric   if (!LState || LState->isDestroyed()) {
6110b57cec5SDimitry Andric     State = State->set<LockMap>(LockR, LockState::getUnlocked());
6120b57cec5SDimitry Andric     C.addTransition(State);
6130b57cec5SDimitry Andric     return;
6140b57cec5SDimitry Andric   }
6150b57cec5SDimitry Andric 
616e8d8bef9SDimitry Andric   StringRef Message = LState->isLocked()
617e8d8bef9SDimitry Andric                           ? "This lock is still being held"
618e8d8bef9SDimitry Andric                           : "This lock has already been initialized";
6190b57cec5SDimitry Andric 
620e8d8bef9SDimitry Andric   reportBug(C, BT_initlock, MtxExpr, CheckKind, Message);
6210b57cec5SDimitry Andric }
6220b57cec5SDimitry Andric 
623e8d8bef9SDimitry Andric void PthreadLockChecker::reportBug(CheckerContext &C,
624e8d8bef9SDimitry Andric                                    std::unique_ptr<BugType> BT[],
625e8d8bef9SDimitry Andric                                    const Expr *MtxExpr, CheckerKind CheckKind,
626e8d8bef9SDimitry Andric                                    StringRef Desc) const {
6270b57cec5SDimitry Andric   ExplodedNode *N = C.generateErrorNode();
6280b57cec5SDimitry Andric   if (!N)
6290b57cec5SDimitry Andric     return;
630e8d8bef9SDimitry Andric   initBugType(CheckKind);
631e8d8bef9SDimitry Andric   auto Report =
632e8d8bef9SDimitry Andric       std::make_unique<PathSensitiveBugReport>(*BT[CheckKind], Desc, N);
633e8d8bef9SDimitry Andric   Report->addRange(MtxExpr->getSourceRange());
6340b57cec5SDimitry Andric   C.emitReport(std::move(Report));
6350b57cec5SDimitry Andric }
6360b57cec5SDimitry Andric 
6370b57cec5SDimitry Andric void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper,
6380b57cec5SDimitry Andric                                           CheckerContext &C) const {
6390b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
6400b57cec5SDimitry Andric 
6415ffd83dbSDimitry Andric   for (auto I : State->get<DestroyRetVal>()) {
6425ffd83dbSDimitry Andric     // Once the return value symbol dies, no more checks can be performed
6435ffd83dbSDimitry Andric     // against it. See if the return value was checked before this point.
6445ffd83dbSDimitry Andric     // This would remove the symbol from the map as well.
6455ffd83dbSDimitry Andric     if (SymReaper.isDead(I.second))
6465ffd83dbSDimitry Andric       State = resolvePossiblyDestroyedMutex(State, I.first, &I.second);
6470b57cec5SDimitry Andric   }
6485ffd83dbSDimitry Andric 
6495ffd83dbSDimitry Andric   for (auto I : State->get<LockMap>()) {
6505ffd83dbSDimitry Andric     // Stop tracking dead mutex regions as well.
651fe6060f1SDimitry Andric     if (!SymReaper.isLiveRegion(I.first)) {
6525ffd83dbSDimitry Andric       State = State->remove<LockMap>(I.first);
653fe6060f1SDimitry Andric       State = State->remove<DestroyRetVal>(I.first);
654fe6060f1SDimitry Andric     }
6555ffd83dbSDimitry Andric   }
6565ffd83dbSDimitry Andric 
6575ffd83dbSDimitry Andric   // TODO: We probably need to clean up the lock stack as well.
6585ffd83dbSDimitry Andric   // It is tricky though: even if the mutex cannot be unlocked anymore,
6595ffd83dbSDimitry Andric   // it can still participate in lock order reversal resolution.
6605ffd83dbSDimitry Andric 
6610b57cec5SDimitry Andric   C.addTransition(State);
6620b57cec5SDimitry Andric }
6630b57cec5SDimitry Andric 
6645ffd83dbSDimitry Andric ProgramStateRef PthreadLockChecker::checkRegionChanges(
6655ffd83dbSDimitry Andric     ProgramStateRef State, const InvalidatedSymbols *Symbols,
6665ffd83dbSDimitry Andric     ArrayRef<const MemRegion *> ExplicitRegions,
6675ffd83dbSDimitry Andric     ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
6685ffd83dbSDimitry Andric     const CallEvent *Call) const {
6695ffd83dbSDimitry Andric 
6705ffd83dbSDimitry Andric   bool IsLibraryFunction = false;
6715ffd83dbSDimitry Andric   if (Call && Call->isGlobalCFunction()) {
6725ffd83dbSDimitry Andric     // Avoid invalidating mutex state when a known supported function is called.
6735ffd83dbSDimitry Andric     if (PThreadCallbacks.lookup(*Call) || FuchsiaCallbacks.lookup(*Call) ||
6745ffd83dbSDimitry Andric         C11Callbacks.lookup(*Call))
6755ffd83dbSDimitry Andric       return State;
6765ffd83dbSDimitry Andric 
6775ffd83dbSDimitry Andric     if (Call->isInSystemHeader())
6785ffd83dbSDimitry Andric       IsLibraryFunction = true;
6795ffd83dbSDimitry Andric   }
6805ffd83dbSDimitry Andric 
6815ffd83dbSDimitry Andric   for (auto R : Regions) {
6825ffd83dbSDimitry Andric     // We assume that system library function wouldn't touch the mutex unless
6835ffd83dbSDimitry Andric     // it takes the mutex explicitly as an argument.
6845ffd83dbSDimitry Andric     // FIXME: This is a bit quadratic.
685349cc55cSDimitry Andric     if (IsLibraryFunction && !llvm::is_contained(ExplicitRegions, R))
6865ffd83dbSDimitry Andric       continue;
6875ffd83dbSDimitry Andric 
6885ffd83dbSDimitry Andric     State = State->remove<LockMap>(R);
6895ffd83dbSDimitry Andric     State = State->remove<DestroyRetVal>(R);
6905ffd83dbSDimitry Andric 
6915ffd83dbSDimitry Andric     // TODO: We need to invalidate the lock stack as well. This is tricky
6925ffd83dbSDimitry Andric     // to implement correctly and efficiently though, because the effects
6935ffd83dbSDimitry Andric     // of mutex escapes on lock order may be fairly varied.
6945ffd83dbSDimitry Andric   }
6955ffd83dbSDimitry Andric 
6965ffd83dbSDimitry Andric   return State;
6975ffd83dbSDimitry Andric }
6985ffd83dbSDimitry Andric 
6995ffd83dbSDimitry Andric void ento::registerPthreadLockBase(CheckerManager &mgr) {
7000b57cec5SDimitry Andric   mgr.registerChecker<PthreadLockChecker>();
7010b57cec5SDimitry Andric }
7020b57cec5SDimitry Andric 
7035ffd83dbSDimitry Andric bool ento::shouldRegisterPthreadLockBase(const CheckerManager &mgr) { return true; }
7045ffd83dbSDimitry Andric 
7055ffd83dbSDimitry Andric #define REGISTER_CHECKER(name)                                                 \
7065ffd83dbSDimitry Andric   void ento::register##name(CheckerManager &mgr) {                             \
7075ffd83dbSDimitry Andric     PthreadLockChecker *checker = mgr.getChecker<PthreadLockChecker>();        \
7085ffd83dbSDimitry Andric     checker->ChecksEnabled[PthreadLockChecker::CK_##name] = true;              \
7095ffd83dbSDimitry Andric     checker->CheckNames[PthreadLockChecker::CK_##name] =                       \
7105ffd83dbSDimitry Andric         mgr.getCurrentCheckerName();                                           \
7115ffd83dbSDimitry Andric   }                                                                            \
7125ffd83dbSDimitry Andric                                                                                \
7135ffd83dbSDimitry Andric   bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
7145ffd83dbSDimitry Andric 
7155ffd83dbSDimitry Andric REGISTER_CHECKER(PthreadLockChecker)
7165ffd83dbSDimitry Andric REGISTER_CHECKER(FuchsiaLockChecker)
7175ffd83dbSDimitry Andric REGISTER_CHECKER(C11LockChecker)
718