xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp (revision 5ffd83dbcc34f10e07f6d3e968ae6365869615f4)
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 //
9*5ffd83dbSDimitry Andric // This file defines:
10*5ffd83dbSDimitry Andric //  * PthreadLockChecker, a simple lock -> unlock checker.
11*5ffd83dbSDimitry Andric //    Which also checks for XNU locks, which behave similarly enough to share
12*5ffd83dbSDimitry Andric //    code.
13*5ffd83dbSDimitry Andric //  * FuchsiaLocksChecker, which is also rather similar.
14*5ffd83dbSDimitry Andric //  * C11LockChecker which also closely follows Pthread semantics.
15*5ffd83dbSDimitry Andric //
16*5ffd83dbSDimitry 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"
24*5ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
250b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
260b57cec5SDimitry Andric 
270b57cec5SDimitry Andric using namespace clang;
280b57cec5SDimitry Andric using namespace ento;
290b57cec5SDimitry Andric 
300b57cec5SDimitry Andric namespace {
310b57cec5SDimitry Andric 
320b57cec5SDimitry Andric struct LockState {
330b57cec5SDimitry Andric   enum Kind {
340b57cec5SDimitry Andric     Destroyed,
350b57cec5SDimitry Andric     Locked,
360b57cec5SDimitry Andric     Unlocked,
370b57cec5SDimitry Andric     UntouchedAndPossiblyDestroyed,
380b57cec5SDimitry Andric     UnlockedAndPossiblyDestroyed
390b57cec5SDimitry Andric   } K;
400b57cec5SDimitry Andric 
410b57cec5SDimitry Andric private:
420b57cec5SDimitry Andric   LockState(Kind K) : K(K) {}
430b57cec5SDimitry Andric 
440b57cec5SDimitry Andric public:
450b57cec5SDimitry Andric   static LockState getLocked() { return LockState(Locked); }
460b57cec5SDimitry Andric   static LockState getUnlocked() { return LockState(Unlocked); }
470b57cec5SDimitry Andric   static LockState getDestroyed() { return LockState(Destroyed); }
480b57cec5SDimitry Andric   static LockState getUntouchedAndPossiblyDestroyed() {
490b57cec5SDimitry Andric     return LockState(UntouchedAndPossiblyDestroyed);
500b57cec5SDimitry Andric   }
510b57cec5SDimitry Andric   static LockState getUnlockedAndPossiblyDestroyed() {
520b57cec5SDimitry Andric     return LockState(UnlockedAndPossiblyDestroyed);
530b57cec5SDimitry Andric   }
540b57cec5SDimitry Andric 
55*5ffd83dbSDimitry Andric   bool operator==(const LockState &X) const { return K == X.K; }
560b57cec5SDimitry Andric 
570b57cec5SDimitry Andric   bool isLocked() const { return K == Locked; }
580b57cec5SDimitry Andric   bool isUnlocked() const { return K == Unlocked; }
590b57cec5SDimitry Andric   bool isDestroyed() const { return K == Destroyed; }
600b57cec5SDimitry Andric   bool isUntouchedAndPossiblyDestroyed() const {
610b57cec5SDimitry Andric     return K == UntouchedAndPossiblyDestroyed;
620b57cec5SDimitry Andric   }
630b57cec5SDimitry Andric   bool isUnlockedAndPossiblyDestroyed() const {
640b57cec5SDimitry Andric     return K == UnlockedAndPossiblyDestroyed;
650b57cec5SDimitry Andric   }
660b57cec5SDimitry Andric 
67*5ffd83dbSDimitry Andric   void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
680b57cec5SDimitry Andric };
690b57cec5SDimitry Andric 
70*5ffd83dbSDimitry Andric class PthreadLockChecker : public Checker<check::PostCall, check::DeadSymbols,
71*5ffd83dbSDimitry Andric                                           check::RegionChanges> {
720b57cec5SDimitry Andric public:
73*5ffd83dbSDimitry Andric   enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics };
74*5ffd83dbSDimitry Andric   enum CheckerKind {
75*5ffd83dbSDimitry Andric     CK_PthreadLockChecker,
76*5ffd83dbSDimitry Andric     CK_FuchsiaLockChecker,
77*5ffd83dbSDimitry Andric     CK_C11LockChecker,
78*5ffd83dbSDimitry Andric     CK_NumCheckKinds
79*5ffd83dbSDimitry Andric   };
80*5ffd83dbSDimitry Andric   DefaultBool ChecksEnabled[CK_NumCheckKinds];
81*5ffd83dbSDimitry Andric   CheckerNameRef CheckNames[CK_NumCheckKinds];
820b57cec5SDimitry Andric 
83*5ffd83dbSDimitry Andric private:
84*5ffd83dbSDimitry Andric   typedef void (PthreadLockChecker::*FnCheck)(const CallEvent &Call,
85*5ffd83dbSDimitry Andric                                               CheckerContext &C,
86*5ffd83dbSDimitry Andric                                               CheckerKind checkkind) const;
87*5ffd83dbSDimitry Andric   CallDescriptionMap<FnCheck> PThreadCallbacks = {
88*5ffd83dbSDimitry Andric       // Init.
89*5ffd83dbSDimitry Andric       {{"pthread_mutex_init", 2}, &PthreadLockChecker::InitAnyLock},
90*5ffd83dbSDimitry Andric       // TODO: pthread_rwlock_init(2 arguments).
91*5ffd83dbSDimitry Andric       // TODO: lck_mtx_init(3 arguments).
92*5ffd83dbSDimitry Andric       // TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex.
93*5ffd83dbSDimitry Andric       // TODO: lck_rw_init(3 arguments).
94*5ffd83dbSDimitry Andric       // TODO: lck_rw_alloc_init(2 arguments) => returns the mutex.
950b57cec5SDimitry Andric 
96*5ffd83dbSDimitry Andric       // Acquire.
97*5ffd83dbSDimitry Andric       {{"pthread_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
98*5ffd83dbSDimitry Andric       {{"pthread_rwlock_rdlock", 1}, &PthreadLockChecker::AcquirePthreadLock},
99*5ffd83dbSDimitry Andric       {{"pthread_rwlock_wrlock", 1}, &PthreadLockChecker::AcquirePthreadLock},
100*5ffd83dbSDimitry Andric       {{"lck_mtx_lock", 1}, &PthreadLockChecker::AcquireXNULock},
101*5ffd83dbSDimitry Andric       {{"lck_rw_lock_exclusive", 1}, &PthreadLockChecker::AcquireXNULock},
102*5ffd83dbSDimitry Andric       {{"lck_rw_lock_shared", 1}, &PthreadLockChecker::AcquireXNULock},
103*5ffd83dbSDimitry Andric 
104*5ffd83dbSDimitry Andric       // Try.
105*5ffd83dbSDimitry Andric       {{"pthread_mutex_trylock", 1}, &PthreadLockChecker::TryPthreadLock},
106*5ffd83dbSDimitry Andric       {{"pthread_rwlock_tryrdlock", 1}, &PthreadLockChecker::TryPthreadLock},
107*5ffd83dbSDimitry Andric       {{"pthread_rwlock_trywrlock", 1}, &PthreadLockChecker::TryPthreadLock},
108*5ffd83dbSDimitry Andric       {{"lck_mtx_try_lock", 1}, &PthreadLockChecker::TryXNULock},
109*5ffd83dbSDimitry Andric       {{"lck_rw_try_lock_exclusive", 1}, &PthreadLockChecker::TryXNULock},
110*5ffd83dbSDimitry Andric       {{"lck_rw_try_lock_shared", 1}, &PthreadLockChecker::TryXNULock},
111*5ffd83dbSDimitry Andric 
112*5ffd83dbSDimitry Andric       // Release.
113*5ffd83dbSDimitry Andric       {{"pthread_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
114*5ffd83dbSDimitry Andric       {{"pthread_rwlock_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
115*5ffd83dbSDimitry Andric       {{"lck_mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
116*5ffd83dbSDimitry Andric       {{"lck_rw_unlock_exclusive", 1}, &PthreadLockChecker::ReleaseAnyLock},
117*5ffd83dbSDimitry Andric       {{"lck_rw_unlock_shared", 1}, &PthreadLockChecker::ReleaseAnyLock},
118*5ffd83dbSDimitry Andric       {{"lck_rw_done", 1}, &PthreadLockChecker::ReleaseAnyLock},
119*5ffd83dbSDimitry Andric 
120*5ffd83dbSDimitry Andric       // Destroy.
121*5ffd83dbSDimitry Andric       {{"pthread_mutex_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock},
122*5ffd83dbSDimitry Andric       {{"lck_mtx_destroy", 2}, &PthreadLockChecker::DestroyXNULock},
123*5ffd83dbSDimitry Andric       // TODO: pthread_rwlock_destroy(1 argument).
124*5ffd83dbSDimitry Andric       // TODO: lck_rw_destroy(2 arguments).
125*5ffd83dbSDimitry Andric   };
126*5ffd83dbSDimitry Andric 
127*5ffd83dbSDimitry Andric   CallDescriptionMap<FnCheck> FuchsiaCallbacks = {
128*5ffd83dbSDimitry Andric       // Init.
129*5ffd83dbSDimitry Andric       {{"spin_lock_init", 1}, &PthreadLockChecker::InitAnyLock},
130*5ffd83dbSDimitry Andric 
131*5ffd83dbSDimitry Andric       // Acquire.
132*5ffd83dbSDimitry Andric       {{"spin_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
133*5ffd83dbSDimitry Andric       {{"spin_lock_save", 3}, &PthreadLockChecker::AcquirePthreadLock},
134*5ffd83dbSDimitry Andric       {{"sync_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
135*5ffd83dbSDimitry Andric       {{"sync_mutex_lock_with_waiter", 1},
136*5ffd83dbSDimitry Andric        &PthreadLockChecker::AcquirePthreadLock},
137*5ffd83dbSDimitry Andric 
138*5ffd83dbSDimitry Andric       // Try.
139*5ffd83dbSDimitry Andric       {{"spin_trylock", 1}, &PthreadLockChecker::TryFuchsiaLock},
140*5ffd83dbSDimitry Andric       {{"sync_mutex_trylock", 1}, &PthreadLockChecker::TryFuchsiaLock},
141*5ffd83dbSDimitry Andric       {{"sync_mutex_timedlock", 2}, &PthreadLockChecker::TryFuchsiaLock},
142*5ffd83dbSDimitry Andric 
143*5ffd83dbSDimitry Andric       // Release.
144*5ffd83dbSDimitry Andric       {{"spin_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
145*5ffd83dbSDimitry Andric       {{"spin_unlock_restore", 3}, &PthreadLockChecker::ReleaseAnyLock},
146*5ffd83dbSDimitry Andric       {{"sync_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
147*5ffd83dbSDimitry Andric   };
148*5ffd83dbSDimitry Andric 
149*5ffd83dbSDimitry Andric   CallDescriptionMap<FnCheck> C11Callbacks = {
150*5ffd83dbSDimitry Andric       // Init.
151*5ffd83dbSDimitry Andric       {{"mtx_init", 2}, &PthreadLockChecker::InitAnyLock},
152*5ffd83dbSDimitry Andric 
153*5ffd83dbSDimitry Andric       // Acquire.
154*5ffd83dbSDimitry Andric       {{"mtx_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
155*5ffd83dbSDimitry Andric 
156*5ffd83dbSDimitry Andric       // Try.
157*5ffd83dbSDimitry Andric       {{"mtx_trylock", 1}, &PthreadLockChecker::TryC11Lock},
158*5ffd83dbSDimitry Andric       {{"mtx_timedlock", 2}, &PthreadLockChecker::TryC11Lock},
159*5ffd83dbSDimitry Andric 
160*5ffd83dbSDimitry Andric       // Release.
161*5ffd83dbSDimitry Andric       {{"mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
162*5ffd83dbSDimitry Andric 
163*5ffd83dbSDimitry Andric       // Destroy
164*5ffd83dbSDimitry Andric       {{"mtx_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock},
165*5ffd83dbSDimitry Andric   };
166*5ffd83dbSDimitry Andric 
1670b57cec5SDimitry Andric   ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state,
1680b57cec5SDimitry Andric                                                 const MemRegion *lockR,
1690b57cec5SDimitry Andric                                                 const SymbolRef *sym) const;
170*5ffd83dbSDimitry Andric   void reportUseDestroyedBug(const CallEvent &Call, CheckerContext &C,
171*5ffd83dbSDimitry Andric                              unsigned ArgNo, CheckerKind checkKind) const;
172*5ffd83dbSDimitry Andric 
173*5ffd83dbSDimitry Andric   // Init.
174*5ffd83dbSDimitry Andric   void InitAnyLock(const CallEvent &Call, CheckerContext &C,
175*5ffd83dbSDimitry Andric                    CheckerKind checkkind) const;
176*5ffd83dbSDimitry Andric   void InitLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
177*5ffd83dbSDimitry Andric                    SVal Lock, CheckerKind checkkind) const;
178*5ffd83dbSDimitry Andric 
179*5ffd83dbSDimitry Andric   // Lock, Try-lock.
180*5ffd83dbSDimitry Andric   void AcquirePthreadLock(const CallEvent &Call, CheckerContext &C,
181*5ffd83dbSDimitry Andric                           CheckerKind checkkind) const;
182*5ffd83dbSDimitry Andric   void AcquireXNULock(const CallEvent &Call, CheckerContext &C,
183*5ffd83dbSDimitry Andric                       CheckerKind checkkind) const;
184*5ffd83dbSDimitry Andric   void TryPthreadLock(const CallEvent &Call, CheckerContext &C,
185*5ffd83dbSDimitry Andric                       CheckerKind checkkind) const;
186*5ffd83dbSDimitry Andric   void TryXNULock(const CallEvent &Call, CheckerContext &C,
187*5ffd83dbSDimitry Andric                   CheckerKind checkkind) const;
188*5ffd83dbSDimitry Andric   void TryFuchsiaLock(const CallEvent &Call, CheckerContext &C,
189*5ffd83dbSDimitry Andric                       CheckerKind checkkind) const;
190*5ffd83dbSDimitry Andric   void TryC11Lock(const CallEvent &Call, CheckerContext &C,
191*5ffd83dbSDimitry Andric                   CheckerKind checkkind) const;
192*5ffd83dbSDimitry Andric   void AcquireLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
193*5ffd83dbSDimitry Andric                       SVal lock, bool isTryLock, LockingSemantics semantics,
194*5ffd83dbSDimitry Andric                       CheckerKind checkkind) const;
195*5ffd83dbSDimitry Andric 
196*5ffd83dbSDimitry Andric   // Release.
197*5ffd83dbSDimitry Andric   void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C,
198*5ffd83dbSDimitry Andric                       CheckerKind checkkind) const;
199*5ffd83dbSDimitry Andric   void ReleaseLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
200*5ffd83dbSDimitry Andric                       SVal lock, CheckerKind checkkind) const;
201*5ffd83dbSDimitry Andric 
202*5ffd83dbSDimitry Andric   // Destroy.
203*5ffd83dbSDimitry Andric   void DestroyPthreadLock(const CallEvent &Call, CheckerContext &C,
204*5ffd83dbSDimitry Andric                           CheckerKind checkkind) const;
205*5ffd83dbSDimitry Andric   void DestroyXNULock(const CallEvent &Call, CheckerContext &C,
206*5ffd83dbSDimitry Andric                       CheckerKind checkkind) const;
207*5ffd83dbSDimitry Andric   void DestroyLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
208*5ffd83dbSDimitry Andric                       SVal Lock, LockingSemantics semantics,
209*5ffd83dbSDimitry Andric                       CheckerKind checkkind) const;
210*5ffd83dbSDimitry Andric 
211*5ffd83dbSDimitry Andric public:
212*5ffd83dbSDimitry Andric   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
213*5ffd83dbSDimitry Andric   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
214*5ffd83dbSDimitry Andric   ProgramStateRef
215*5ffd83dbSDimitry Andric   checkRegionChanges(ProgramStateRef State, const InvalidatedSymbols *Symbols,
216*5ffd83dbSDimitry Andric                      ArrayRef<const MemRegion *> ExplicitRegions,
217*5ffd83dbSDimitry Andric                      ArrayRef<const MemRegion *> Regions,
218*5ffd83dbSDimitry Andric                      const LocationContext *LCtx, const CallEvent *Call) const;
219*5ffd83dbSDimitry Andric   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
220*5ffd83dbSDimitry Andric                   const char *Sep) const override;
221*5ffd83dbSDimitry Andric 
222*5ffd83dbSDimitry Andric private:
223*5ffd83dbSDimitry Andric   mutable std::unique_ptr<BugType> BT_doublelock[CK_NumCheckKinds];
224*5ffd83dbSDimitry Andric   mutable std::unique_ptr<BugType> BT_doubleunlock[CK_NumCheckKinds];
225*5ffd83dbSDimitry Andric   mutable std::unique_ptr<BugType> BT_destroylock[CK_NumCheckKinds];
226*5ffd83dbSDimitry Andric   mutable std::unique_ptr<BugType> BT_initlock[CK_NumCheckKinds];
227*5ffd83dbSDimitry Andric   mutable std::unique_ptr<BugType> BT_lor[CK_NumCheckKinds];
228*5ffd83dbSDimitry Andric 
229*5ffd83dbSDimitry Andric   void initBugType(CheckerKind checkKind) const {
230*5ffd83dbSDimitry Andric     if (BT_doublelock[checkKind])
231*5ffd83dbSDimitry Andric       return;
232*5ffd83dbSDimitry Andric     BT_doublelock[checkKind].reset(
233*5ffd83dbSDimitry Andric         new BugType{CheckNames[checkKind], "Double locking", "Lock checker"});
234*5ffd83dbSDimitry Andric     BT_doubleunlock[checkKind].reset(
235*5ffd83dbSDimitry Andric         new BugType{CheckNames[checkKind], "Double unlocking", "Lock checker"});
236*5ffd83dbSDimitry Andric     BT_destroylock[checkKind].reset(new BugType{
237*5ffd83dbSDimitry Andric         CheckNames[checkKind], "Use destroyed lock", "Lock checker"});
238*5ffd83dbSDimitry Andric     BT_initlock[checkKind].reset(new BugType{
239*5ffd83dbSDimitry Andric         CheckNames[checkKind], "Init invalid lock", "Lock checker"});
240*5ffd83dbSDimitry Andric     BT_lor[checkKind].reset(new BugType{CheckNames[checkKind],
241*5ffd83dbSDimitry Andric                                         "Lock order reversal", "Lock checker"});
242*5ffd83dbSDimitry Andric   }
2430b57cec5SDimitry Andric };
2440b57cec5SDimitry Andric } // end anonymous namespace
2450b57cec5SDimitry Andric 
2460b57cec5SDimitry Andric // A stack of locks for tracking lock-unlock order.
2470b57cec5SDimitry Andric REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *)
2480b57cec5SDimitry Andric 
2490b57cec5SDimitry Andric // An entry for tracking lock states.
2500b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState)
2510b57cec5SDimitry Andric 
2520b57cec5SDimitry Andric // Return values for unresolved calls to pthread_mutex_destroy().
2530b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(DestroyRetVal, const MemRegion *, SymbolRef)
2540b57cec5SDimitry Andric 
255*5ffd83dbSDimitry Andric void PthreadLockChecker::checkPostCall(const CallEvent &Call,
2560b57cec5SDimitry Andric                                        CheckerContext &C) const {
257*5ffd83dbSDimitry Andric   // An additional umbrella check that all functions modeled by this checker
258*5ffd83dbSDimitry Andric   // are global C functions.
259*5ffd83dbSDimitry Andric   // TODO: Maybe make this the default behavior of CallDescription
260*5ffd83dbSDimitry Andric   // with exactly one identifier?
261*5ffd83dbSDimitry Andric   // FIXME: Try to handle cases when the implementation was inlined rather
262*5ffd83dbSDimitry Andric   // than just giving up.
263*5ffd83dbSDimitry Andric   if (!Call.isGlobalCFunction() || C.wasInlined)
2640b57cec5SDimitry Andric     return;
2650b57cec5SDimitry Andric 
266*5ffd83dbSDimitry Andric   if (const FnCheck *Callback = PThreadCallbacks.lookup(Call))
267*5ffd83dbSDimitry Andric     (this->**Callback)(Call, C, CK_PthreadLockChecker);
268*5ffd83dbSDimitry Andric   else if (const FnCheck *Callback = FuchsiaCallbacks.lookup(Call))
269*5ffd83dbSDimitry Andric     (this->**Callback)(Call, C, CK_FuchsiaLockChecker);
270*5ffd83dbSDimitry Andric   else if (const FnCheck *Callback = C11Callbacks.lookup(Call))
271*5ffd83dbSDimitry Andric     (this->**Callback)(Call, C, CK_C11LockChecker);
2720b57cec5SDimitry Andric }
2730b57cec5SDimitry Andric 
2740b57cec5SDimitry Andric // When a lock is destroyed, in some semantics(like PthreadSemantics) we are not
2750b57cec5SDimitry Andric // sure if the destroy call has succeeded or failed, and the lock enters one of
2760b57cec5SDimitry Andric // the 'possibly destroyed' state. There is a short time frame for the
2770b57cec5SDimitry Andric // programmer to check the return value to see if the lock was successfully
2780b57cec5SDimitry Andric // destroyed. Before we model the next operation over that lock, we call this
2790b57cec5SDimitry Andric // function to see if the return value was checked by now and set the lock state
2800b57cec5SDimitry Andric // - either to destroyed state or back to its previous state.
2810b57cec5SDimitry Andric 
2820b57cec5SDimitry Andric // In PthreadSemantics, pthread_mutex_destroy() returns zero if the lock is
2830b57cec5SDimitry Andric // successfully destroyed and it returns a non-zero value otherwise.
2840b57cec5SDimitry Andric ProgramStateRef PthreadLockChecker::resolvePossiblyDestroyedMutex(
2850b57cec5SDimitry Andric     ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const {
2860b57cec5SDimitry Andric   const LockState *lstate = state->get<LockMap>(lockR);
2870b57cec5SDimitry Andric   // Existence in DestroyRetVal ensures existence in LockMap.
2880b57cec5SDimitry Andric   // Existence in Destroyed also ensures that the lock state for lockR is either
2890b57cec5SDimitry Andric   // UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed.
2900b57cec5SDimitry Andric   assert(lstate->isUntouchedAndPossiblyDestroyed() ||
2910b57cec5SDimitry Andric          lstate->isUnlockedAndPossiblyDestroyed());
2920b57cec5SDimitry Andric 
2930b57cec5SDimitry Andric   ConstraintManager &CMgr = state->getConstraintManager();
2940b57cec5SDimitry Andric   ConditionTruthVal retZero = CMgr.isNull(state, *sym);
2950b57cec5SDimitry Andric   if (retZero.isConstrainedFalse()) {
2960b57cec5SDimitry Andric     if (lstate->isUntouchedAndPossiblyDestroyed())
2970b57cec5SDimitry Andric       state = state->remove<LockMap>(lockR);
2980b57cec5SDimitry Andric     else if (lstate->isUnlockedAndPossiblyDestroyed())
2990b57cec5SDimitry Andric       state = state->set<LockMap>(lockR, LockState::getUnlocked());
3000b57cec5SDimitry Andric   } else
3010b57cec5SDimitry Andric     state = state->set<LockMap>(lockR, LockState::getDestroyed());
3020b57cec5SDimitry Andric 
3030b57cec5SDimitry Andric   // Removing the map entry (lockR, sym) from DestroyRetVal as the lock state is
3040b57cec5SDimitry Andric   // now resolved.
3050b57cec5SDimitry Andric   state = state->remove<DestroyRetVal>(lockR);
3060b57cec5SDimitry Andric   return state;
3070b57cec5SDimitry Andric }
3080b57cec5SDimitry Andric 
3090b57cec5SDimitry Andric void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State,
3100b57cec5SDimitry Andric                                     const char *NL, const char *Sep) const {
3110b57cec5SDimitry Andric   LockMapTy LM = State->get<LockMap>();
3120b57cec5SDimitry Andric   if (!LM.isEmpty()) {
3130b57cec5SDimitry Andric     Out << Sep << "Mutex states:" << NL;
3140b57cec5SDimitry Andric     for (auto I : LM) {
3150b57cec5SDimitry Andric       I.first->dumpToStream(Out);
3160b57cec5SDimitry Andric       if (I.second.isLocked())
3170b57cec5SDimitry Andric         Out << ": locked";
3180b57cec5SDimitry Andric       else if (I.second.isUnlocked())
3190b57cec5SDimitry Andric         Out << ": unlocked";
3200b57cec5SDimitry Andric       else if (I.second.isDestroyed())
3210b57cec5SDimitry Andric         Out << ": destroyed";
3220b57cec5SDimitry Andric       else if (I.second.isUntouchedAndPossiblyDestroyed())
3230b57cec5SDimitry Andric         Out << ": not tracked, possibly destroyed";
3240b57cec5SDimitry Andric       else if (I.second.isUnlockedAndPossiblyDestroyed())
3250b57cec5SDimitry Andric         Out << ": unlocked, possibly destroyed";
3260b57cec5SDimitry Andric       Out << NL;
3270b57cec5SDimitry Andric     }
3280b57cec5SDimitry Andric   }
3290b57cec5SDimitry Andric 
3300b57cec5SDimitry Andric   LockSetTy LS = State->get<LockSet>();
3310b57cec5SDimitry Andric   if (!LS.isEmpty()) {
3320b57cec5SDimitry Andric     Out << Sep << "Mutex lock order:" << NL;
3330b57cec5SDimitry Andric     for (auto I : LS) {
3340b57cec5SDimitry Andric       I->dumpToStream(Out);
3350b57cec5SDimitry Andric       Out << NL;
3360b57cec5SDimitry Andric     }
3370b57cec5SDimitry Andric   }
3380b57cec5SDimitry Andric 
3390b57cec5SDimitry Andric   // TODO: Dump destroyed mutex symbols?
3400b57cec5SDimitry Andric }
3410b57cec5SDimitry Andric 
342*5ffd83dbSDimitry Andric void PthreadLockChecker::AcquirePthreadLock(const CallEvent &Call,
343*5ffd83dbSDimitry Andric                                             CheckerContext &C,
344*5ffd83dbSDimitry Andric                                             CheckerKind checkKind) const {
345*5ffd83dbSDimitry Andric   AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, PthreadSemantics,
346*5ffd83dbSDimitry Andric                  checkKind);
347*5ffd83dbSDimitry Andric }
348*5ffd83dbSDimitry Andric 
349*5ffd83dbSDimitry Andric void PthreadLockChecker::AcquireXNULock(const CallEvent &Call,
350*5ffd83dbSDimitry Andric                                         CheckerContext &C,
351*5ffd83dbSDimitry Andric                                         CheckerKind checkKind) const {
352*5ffd83dbSDimitry Andric   AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, XNUSemantics,
353*5ffd83dbSDimitry Andric                  checkKind);
354*5ffd83dbSDimitry Andric }
355*5ffd83dbSDimitry Andric 
356*5ffd83dbSDimitry Andric void PthreadLockChecker::TryPthreadLock(const CallEvent &Call,
357*5ffd83dbSDimitry Andric                                         CheckerContext &C,
358*5ffd83dbSDimitry Andric                                         CheckerKind checkKind) const {
359*5ffd83dbSDimitry Andric   AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
360*5ffd83dbSDimitry Andric                  checkKind);
361*5ffd83dbSDimitry Andric }
362*5ffd83dbSDimitry Andric 
363*5ffd83dbSDimitry Andric void PthreadLockChecker::TryXNULock(const CallEvent &Call, CheckerContext &C,
364*5ffd83dbSDimitry Andric                                     CheckerKind checkKind) const {
365*5ffd83dbSDimitry Andric   AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
366*5ffd83dbSDimitry Andric                  checkKind);
367*5ffd83dbSDimitry Andric }
368*5ffd83dbSDimitry Andric 
369*5ffd83dbSDimitry Andric void PthreadLockChecker::TryFuchsiaLock(const CallEvent &Call,
370*5ffd83dbSDimitry Andric                                         CheckerContext &C,
371*5ffd83dbSDimitry Andric                                         CheckerKind checkKind) const {
372*5ffd83dbSDimitry Andric   AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
373*5ffd83dbSDimitry Andric                  checkKind);
374*5ffd83dbSDimitry Andric }
375*5ffd83dbSDimitry Andric 
376*5ffd83dbSDimitry Andric void PthreadLockChecker::TryC11Lock(const CallEvent &Call, CheckerContext &C,
377*5ffd83dbSDimitry Andric                                     CheckerKind checkKind) const {
378*5ffd83dbSDimitry Andric   AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
379*5ffd83dbSDimitry Andric                  checkKind);
380*5ffd83dbSDimitry Andric }
381*5ffd83dbSDimitry Andric 
382*5ffd83dbSDimitry Andric void PthreadLockChecker::AcquireLockAux(const CallEvent &Call,
383*5ffd83dbSDimitry Andric                                         CheckerContext &C, unsigned ArgNo,
3840b57cec5SDimitry Andric                                         SVal lock, bool isTryLock,
385*5ffd83dbSDimitry Andric                                         enum LockingSemantics semantics,
386*5ffd83dbSDimitry Andric                                         CheckerKind checkKind) const {
387*5ffd83dbSDimitry Andric   if (!ChecksEnabled[checkKind])
388*5ffd83dbSDimitry Andric     return;
3890b57cec5SDimitry Andric 
3900b57cec5SDimitry Andric   const MemRegion *lockR = lock.getAsRegion();
3910b57cec5SDimitry Andric   if (!lockR)
3920b57cec5SDimitry Andric     return;
3930b57cec5SDimitry Andric 
3940b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
3950b57cec5SDimitry Andric   const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
3960b57cec5SDimitry Andric   if (sym)
3970b57cec5SDimitry Andric     state = resolvePossiblyDestroyedMutex(state, lockR, sym);
3980b57cec5SDimitry Andric 
3990b57cec5SDimitry Andric   if (const LockState *LState = state->get<LockMap>(lockR)) {
4000b57cec5SDimitry Andric     if (LState->isLocked()) {
4010b57cec5SDimitry Andric       ExplodedNode *N = C.generateErrorNode();
4020b57cec5SDimitry Andric       if (!N)
4030b57cec5SDimitry Andric         return;
404*5ffd83dbSDimitry Andric       initBugType(checkKind);
405a7dea167SDimitry Andric       auto report = std::make_unique<PathSensitiveBugReport>(
406*5ffd83dbSDimitry Andric           *BT_doublelock[checkKind], "This lock has already been acquired", N);
407*5ffd83dbSDimitry Andric       report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
4080b57cec5SDimitry Andric       C.emitReport(std::move(report));
4090b57cec5SDimitry Andric       return;
4100b57cec5SDimitry Andric     } else if (LState->isDestroyed()) {
411*5ffd83dbSDimitry Andric       reportUseDestroyedBug(Call, C, ArgNo, checkKind);
4120b57cec5SDimitry Andric       return;
4130b57cec5SDimitry Andric     }
4140b57cec5SDimitry Andric   }
4150b57cec5SDimitry Andric 
4160b57cec5SDimitry Andric   ProgramStateRef lockSucc = state;
4170b57cec5SDimitry Andric   if (isTryLock) {
4180b57cec5SDimitry Andric     // Bifurcate the state, and allow a mode where the lock acquisition fails.
419*5ffd83dbSDimitry Andric     SVal RetVal = Call.getReturnValue();
420*5ffd83dbSDimitry Andric     if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
4210b57cec5SDimitry Andric       ProgramStateRef lockFail;
4220b57cec5SDimitry Andric       switch (semantics) {
4230b57cec5SDimitry Andric       case PthreadSemantics:
424*5ffd83dbSDimitry Andric         std::tie(lockFail, lockSucc) = state->assume(*DefinedRetVal);
4250b57cec5SDimitry Andric         break;
4260b57cec5SDimitry Andric       case XNUSemantics:
427*5ffd83dbSDimitry Andric         std::tie(lockSucc, lockFail) = state->assume(*DefinedRetVal);
4280b57cec5SDimitry Andric         break;
4290b57cec5SDimitry Andric       default:
4300b57cec5SDimitry Andric         llvm_unreachable("Unknown tryLock locking semantics");
4310b57cec5SDimitry Andric       }
4320b57cec5SDimitry Andric       assert(lockFail && lockSucc);
4330b57cec5SDimitry Andric       C.addTransition(lockFail);
434*5ffd83dbSDimitry Andric     }
435*5ffd83dbSDimitry Andric     // We might want to handle the case when the mutex lock function was inlined
436*5ffd83dbSDimitry Andric     // and returned an Unknown or Undefined value.
4370b57cec5SDimitry Andric   } else if (semantics == PthreadSemantics) {
4380b57cec5SDimitry Andric     // Assume that the return value was 0.
439*5ffd83dbSDimitry Andric     SVal RetVal = Call.getReturnValue();
440*5ffd83dbSDimitry Andric     if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
441*5ffd83dbSDimitry Andric       // FIXME: If the lock function was inlined and returned true,
442*5ffd83dbSDimitry Andric       // we need to behave sanely - at least generate sink.
443*5ffd83dbSDimitry Andric       lockSucc = state->assume(*DefinedRetVal, false);
4440b57cec5SDimitry Andric       assert(lockSucc);
445*5ffd83dbSDimitry Andric     }
446*5ffd83dbSDimitry Andric     // We might want to handle the case when the mutex lock function was inlined
447*5ffd83dbSDimitry Andric     // and returned an Unknown or Undefined value.
4480b57cec5SDimitry Andric   } else {
4490b57cec5SDimitry Andric     // XNU locking semantics return void on non-try locks
4500b57cec5SDimitry Andric     assert((semantics == XNUSemantics) && "Unknown locking semantics");
4510b57cec5SDimitry Andric     lockSucc = state;
4520b57cec5SDimitry Andric   }
4530b57cec5SDimitry Andric 
4540b57cec5SDimitry Andric   // Record that the lock was acquired.
4550b57cec5SDimitry Andric   lockSucc = lockSucc->add<LockSet>(lockR);
4560b57cec5SDimitry Andric   lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked());
4570b57cec5SDimitry Andric   C.addTransition(lockSucc);
4580b57cec5SDimitry Andric }
4590b57cec5SDimitry Andric 
460*5ffd83dbSDimitry Andric void PthreadLockChecker::ReleaseAnyLock(const CallEvent &Call,
461*5ffd83dbSDimitry Andric                                         CheckerContext &C,
462*5ffd83dbSDimitry Andric                                         CheckerKind checkKind) const {
463*5ffd83dbSDimitry Andric   ReleaseLockAux(Call, C, 0, Call.getArgSVal(0), checkKind);
464*5ffd83dbSDimitry Andric }
465*5ffd83dbSDimitry Andric 
466*5ffd83dbSDimitry Andric void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call,
467*5ffd83dbSDimitry Andric                                         CheckerContext &C, unsigned ArgNo,
468*5ffd83dbSDimitry Andric                                         SVal lock,
469*5ffd83dbSDimitry Andric                                         CheckerKind checkKind) const {
470*5ffd83dbSDimitry Andric   if (!ChecksEnabled[checkKind])
471*5ffd83dbSDimitry Andric     return;
4720b57cec5SDimitry Andric 
4730b57cec5SDimitry Andric   const MemRegion *lockR = lock.getAsRegion();
4740b57cec5SDimitry Andric   if (!lockR)
4750b57cec5SDimitry Andric     return;
4760b57cec5SDimitry Andric 
4770b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
4780b57cec5SDimitry Andric   const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
4790b57cec5SDimitry Andric   if (sym)
4800b57cec5SDimitry Andric     state = resolvePossiblyDestroyedMutex(state, lockR, sym);
4810b57cec5SDimitry Andric 
4820b57cec5SDimitry Andric   if (const LockState *LState = state->get<LockMap>(lockR)) {
4830b57cec5SDimitry Andric     if (LState->isUnlocked()) {
4840b57cec5SDimitry Andric       ExplodedNode *N = C.generateErrorNode();
4850b57cec5SDimitry Andric       if (!N)
4860b57cec5SDimitry Andric         return;
487*5ffd83dbSDimitry Andric       initBugType(checkKind);
488a7dea167SDimitry Andric       auto Report = std::make_unique<PathSensitiveBugReport>(
489*5ffd83dbSDimitry Andric           *BT_doubleunlock[checkKind], "This lock has already been unlocked",
490*5ffd83dbSDimitry Andric           N);
491*5ffd83dbSDimitry Andric       Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
4920b57cec5SDimitry Andric       C.emitReport(std::move(Report));
4930b57cec5SDimitry Andric       return;
4940b57cec5SDimitry Andric     } else if (LState->isDestroyed()) {
495*5ffd83dbSDimitry Andric       reportUseDestroyedBug(Call, C, ArgNo, checkKind);
4960b57cec5SDimitry Andric       return;
4970b57cec5SDimitry Andric     }
4980b57cec5SDimitry Andric   }
4990b57cec5SDimitry Andric 
5000b57cec5SDimitry Andric   LockSetTy LS = state->get<LockSet>();
5010b57cec5SDimitry Andric 
5020b57cec5SDimitry Andric   if (!LS.isEmpty()) {
5030b57cec5SDimitry Andric     const MemRegion *firstLockR = LS.getHead();
5040b57cec5SDimitry Andric     if (firstLockR != lockR) {
5050b57cec5SDimitry Andric       ExplodedNode *N = C.generateErrorNode();
5060b57cec5SDimitry Andric       if (!N)
5070b57cec5SDimitry Andric         return;
508*5ffd83dbSDimitry Andric       initBugType(checkKind);
509a7dea167SDimitry Andric       auto report = std::make_unique<PathSensitiveBugReport>(
510*5ffd83dbSDimitry Andric           *BT_lor[checkKind],
511*5ffd83dbSDimitry Andric           "This was not the most recently acquired lock. Possible "
512*5ffd83dbSDimitry Andric           "lock order reversal",
513*5ffd83dbSDimitry Andric           N);
514*5ffd83dbSDimitry Andric       report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
5150b57cec5SDimitry Andric       C.emitReport(std::move(report));
5160b57cec5SDimitry Andric       return;
5170b57cec5SDimitry Andric     }
5180b57cec5SDimitry Andric     // Record that the lock was released.
5190b57cec5SDimitry Andric     state = state->set<LockSet>(LS.getTail());
5200b57cec5SDimitry Andric   }
5210b57cec5SDimitry Andric 
5220b57cec5SDimitry Andric   state = state->set<LockMap>(lockR, LockState::getUnlocked());
5230b57cec5SDimitry Andric   C.addTransition(state);
5240b57cec5SDimitry Andric }
5250b57cec5SDimitry Andric 
526*5ffd83dbSDimitry Andric void PthreadLockChecker::DestroyPthreadLock(const CallEvent &Call,
527*5ffd83dbSDimitry Andric                                             CheckerContext &C,
528*5ffd83dbSDimitry Andric                                             CheckerKind checkKind) const {
529*5ffd83dbSDimitry Andric   DestroyLockAux(Call, C, 0, Call.getArgSVal(0), PthreadSemantics, checkKind);
530*5ffd83dbSDimitry Andric }
531*5ffd83dbSDimitry Andric 
532*5ffd83dbSDimitry Andric void PthreadLockChecker::DestroyXNULock(const CallEvent &Call,
533*5ffd83dbSDimitry Andric                                         CheckerContext &C,
534*5ffd83dbSDimitry Andric                                         CheckerKind checkKind) const {
535*5ffd83dbSDimitry Andric   DestroyLockAux(Call, C, 0, Call.getArgSVal(0), XNUSemantics, checkKind);
536*5ffd83dbSDimitry Andric }
537*5ffd83dbSDimitry Andric 
538*5ffd83dbSDimitry Andric void PthreadLockChecker::DestroyLockAux(const CallEvent &Call,
539*5ffd83dbSDimitry Andric                                         CheckerContext &C, unsigned ArgNo,
5400b57cec5SDimitry Andric                                         SVal Lock,
541*5ffd83dbSDimitry Andric                                         enum LockingSemantics semantics,
542*5ffd83dbSDimitry Andric                                         CheckerKind checkKind) const {
543*5ffd83dbSDimitry Andric   if (!ChecksEnabled[checkKind])
544*5ffd83dbSDimitry Andric     return;
5450b57cec5SDimitry Andric 
5460b57cec5SDimitry Andric   const MemRegion *LockR = Lock.getAsRegion();
5470b57cec5SDimitry Andric   if (!LockR)
5480b57cec5SDimitry Andric     return;
5490b57cec5SDimitry Andric 
5500b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
5510b57cec5SDimitry Andric 
5520b57cec5SDimitry Andric   const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
5530b57cec5SDimitry Andric   if (sym)
5540b57cec5SDimitry Andric     State = resolvePossiblyDestroyedMutex(State, LockR, sym);
5550b57cec5SDimitry Andric 
5560b57cec5SDimitry Andric   const LockState *LState = State->get<LockMap>(LockR);
5570b57cec5SDimitry Andric   // Checking the return value of the destroy method only in the case of
5580b57cec5SDimitry Andric   // PthreadSemantics
5590b57cec5SDimitry Andric   if (semantics == PthreadSemantics) {
5600b57cec5SDimitry Andric     if (!LState || LState->isUnlocked()) {
561*5ffd83dbSDimitry Andric       SymbolRef sym = Call.getReturnValue().getAsSymbol();
5620b57cec5SDimitry Andric       if (!sym) {
5630b57cec5SDimitry Andric         State = State->remove<LockMap>(LockR);
5640b57cec5SDimitry Andric         C.addTransition(State);
5650b57cec5SDimitry Andric         return;
5660b57cec5SDimitry Andric       }
5670b57cec5SDimitry Andric       State = State->set<DestroyRetVal>(LockR, sym);
5680b57cec5SDimitry Andric       if (LState && LState->isUnlocked())
5690b57cec5SDimitry Andric         State = State->set<LockMap>(
5700b57cec5SDimitry Andric             LockR, LockState::getUnlockedAndPossiblyDestroyed());
5710b57cec5SDimitry Andric       else
5720b57cec5SDimitry Andric         State = State->set<LockMap>(
5730b57cec5SDimitry Andric             LockR, LockState::getUntouchedAndPossiblyDestroyed());
5740b57cec5SDimitry Andric       C.addTransition(State);
5750b57cec5SDimitry Andric       return;
5760b57cec5SDimitry Andric     }
5770b57cec5SDimitry Andric   } else {
5780b57cec5SDimitry Andric     if (!LState || LState->isUnlocked()) {
5790b57cec5SDimitry Andric       State = State->set<LockMap>(LockR, LockState::getDestroyed());
5800b57cec5SDimitry Andric       C.addTransition(State);
5810b57cec5SDimitry Andric       return;
5820b57cec5SDimitry Andric     }
5830b57cec5SDimitry Andric   }
5840b57cec5SDimitry Andric   StringRef Message;
5850b57cec5SDimitry Andric 
5860b57cec5SDimitry Andric   if (LState->isLocked()) {
5870b57cec5SDimitry Andric     Message = "This lock is still locked";
5880b57cec5SDimitry Andric   } else {
5890b57cec5SDimitry Andric     Message = "This lock has already been destroyed";
5900b57cec5SDimitry Andric   }
5910b57cec5SDimitry Andric 
5920b57cec5SDimitry Andric   ExplodedNode *N = C.generateErrorNode();
5930b57cec5SDimitry Andric   if (!N)
5940b57cec5SDimitry Andric     return;
595*5ffd83dbSDimitry Andric   initBugType(checkKind);
596*5ffd83dbSDimitry Andric   auto Report = std::make_unique<PathSensitiveBugReport>(
597*5ffd83dbSDimitry Andric       *BT_destroylock[checkKind], Message, N);
598*5ffd83dbSDimitry Andric   Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
5990b57cec5SDimitry Andric   C.emitReport(std::move(Report));
6000b57cec5SDimitry Andric }
6010b57cec5SDimitry Andric 
602*5ffd83dbSDimitry Andric void PthreadLockChecker::InitAnyLock(const CallEvent &Call, CheckerContext &C,
603*5ffd83dbSDimitry Andric                                      CheckerKind checkKind) const {
604*5ffd83dbSDimitry Andric   InitLockAux(Call, C, 0, Call.getArgSVal(0), checkKind);
605*5ffd83dbSDimitry Andric }
606*5ffd83dbSDimitry Andric 
607*5ffd83dbSDimitry Andric void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C,
608*5ffd83dbSDimitry Andric                                      unsigned ArgNo, SVal Lock,
609*5ffd83dbSDimitry Andric                                      CheckerKind checkKind) const {
610*5ffd83dbSDimitry Andric   if (!ChecksEnabled[checkKind])
611*5ffd83dbSDimitry Andric     return;
6120b57cec5SDimitry Andric 
6130b57cec5SDimitry Andric   const MemRegion *LockR = Lock.getAsRegion();
6140b57cec5SDimitry Andric   if (!LockR)
6150b57cec5SDimitry Andric     return;
6160b57cec5SDimitry Andric 
6170b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
6180b57cec5SDimitry Andric 
6190b57cec5SDimitry Andric   const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
6200b57cec5SDimitry Andric   if (sym)
6210b57cec5SDimitry Andric     State = resolvePossiblyDestroyedMutex(State, LockR, sym);
6220b57cec5SDimitry Andric 
6230b57cec5SDimitry Andric   const struct LockState *LState = State->get<LockMap>(LockR);
6240b57cec5SDimitry Andric   if (!LState || LState->isDestroyed()) {
6250b57cec5SDimitry Andric     State = State->set<LockMap>(LockR, LockState::getUnlocked());
6260b57cec5SDimitry Andric     C.addTransition(State);
6270b57cec5SDimitry Andric     return;
6280b57cec5SDimitry Andric   }
6290b57cec5SDimitry Andric 
6300b57cec5SDimitry Andric   StringRef Message;
6310b57cec5SDimitry Andric 
6320b57cec5SDimitry Andric   if (LState->isLocked()) {
6330b57cec5SDimitry Andric     Message = "This lock is still being held";
6340b57cec5SDimitry Andric   } else {
6350b57cec5SDimitry Andric     Message = "This lock has already been initialized";
6360b57cec5SDimitry Andric   }
6370b57cec5SDimitry Andric 
6380b57cec5SDimitry Andric   ExplodedNode *N = C.generateErrorNode();
6390b57cec5SDimitry Andric   if (!N)
6400b57cec5SDimitry Andric     return;
641*5ffd83dbSDimitry Andric   initBugType(checkKind);
642*5ffd83dbSDimitry Andric   auto Report = std::make_unique<PathSensitiveBugReport>(
643*5ffd83dbSDimitry Andric       *BT_initlock[checkKind], Message, N);
644*5ffd83dbSDimitry Andric   Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
6450b57cec5SDimitry Andric   C.emitReport(std::move(Report));
6460b57cec5SDimitry Andric }
6470b57cec5SDimitry Andric 
648*5ffd83dbSDimitry Andric void PthreadLockChecker::reportUseDestroyedBug(const CallEvent &Call,
649*5ffd83dbSDimitry Andric                                                CheckerContext &C,
650*5ffd83dbSDimitry Andric                                                unsigned ArgNo,
651*5ffd83dbSDimitry Andric                                                CheckerKind checkKind) const {
6520b57cec5SDimitry Andric   ExplodedNode *N = C.generateErrorNode();
6530b57cec5SDimitry Andric   if (!N)
6540b57cec5SDimitry Andric     return;
655*5ffd83dbSDimitry Andric   initBugType(checkKind);
656a7dea167SDimitry Andric   auto Report = std::make_unique<PathSensitiveBugReport>(
657*5ffd83dbSDimitry Andric       *BT_destroylock[checkKind], "This lock has already been destroyed", N);
658*5ffd83dbSDimitry Andric   Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
6590b57cec5SDimitry Andric   C.emitReport(std::move(Report));
6600b57cec5SDimitry Andric }
6610b57cec5SDimitry Andric 
6620b57cec5SDimitry Andric void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper,
6630b57cec5SDimitry Andric                                           CheckerContext &C) const {
6640b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
6650b57cec5SDimitry Andric 
666*5ffd83dbSDimitry Andric   for (auto I : State->get<DestroyRetVal>()) {
667*5ffd83dbSDimitry Andric     // Once the return value symbol dies, no more checks can be performed
668*5ffd83dbSDimitry Andric     // against it. See if the return value was checked before this point.
669*5ffd83dbSDimitry Andric     // This would remove the symbol from the map as well.
670*5ffd83dbSDimitry Andric     if (SymReaper.isDead(I.second))
671*5ffd83dbSDimitry Andric       State = resolvePossiblyDestroyedMutex(State, I.first, &I.second);
6720b57cec5SDimitry Andric   }
673*5ffd83dbSDimitry Andric 
674*5ffd83dbSDimitry Andric   for (auto I : State->get<LockMap>()) {
675*5ffd83dbSDimitry Andric     // Stop tracking dead mutex regions as well.
676*5ffd83dbSDimitry Andric     if (!SymReaper.isLiveRegion(I.first))
677*5ffd83dbSDimitry Andric       State = State->remove<LockMap>(I.first);
678*5ffd83dbSDimitry Andric   }
679*5ffd83dbSDimitry Andric 
680*5ffd83dbSDimitry Andric   // TODO: We probably need to clean up the lock stack as well.
681*5ffd83dbSDimitry Andric   // It is tricky though: even if the mutex cannot be unlocked anymore,
682*5ffd83dbSDimitry Andric   // it can still participate in lock order reversal resolution.
683*5ffd83dbSDimitry Andric 
6840b57cec5SDimitry Andric   C.addTransition(State);
6850b57cec5SDimitry Andric }
6860b57cec5SDimitry Andric 
687*5ffd83dbSDimitry Andric ProgramStateRef PthreadLockChecker::checkRegionChanges(
688*5ffd83dbSDimitry Andric     ProgramStateRef State, const InvalidatedSymbols *Symbols,
689*5ffd83dbSDimitry Andric     ArrayRef<const MemRegion *> ExplicitRegions,
690*5ffd83dbSDimitry Andric     ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
691*5ffd83dbSDimitry Andric     const CallEvent *Call) const {
692*5ffd83dbSDimitry Andric 
693*5ffd83dbSDimitry Andric   bool IsLibraryFunction = false;
694*5ffd83dbSDimitry Andric   if (Call && Call->isGlobalCFunction()) {
695*5ffd83dbSDimitry Andric     // Avoid invalidating mutex state when a known supported function is called.
696*5ffd83dbSDimitry Andric     if (PThreadCallbacks.lookup(*Call) || FuchsiaCallbacks.lookup(*Call) ||
697*5ffd83dbSDimitry Andric         C11Callbacks.lookup(*Call))
698*5ffd83dbSDimitry Andric       return State;
699*5ffd83dbSDimitry Andric 
700*5ffd83dbSDimitry Andric     if (Call->isInSystemHeader())
701*5ffd83dbSDimitry Andric       IsLibraryFunction = true;
702*5ffd83dbSDimitry Andric   }
703*5ffd83dbSDimitry Andric 
704*5ffd83dbSDimitry Andric   for (auto R : Regions) {
705*5ffd83dbSDimitry Andric     // We assume that system library function wouldn't touch the mutex unless
706*5ffd83dbSDimitry Andric     // it takes the mutex explicitly as an argument.
707*5ffd83dbSDimitry Andric     // FIXME: This is a bit quadratic.
708*5ffd83dbSDimitry Andric     if (IsLibraryFunction &&
709*5ffd83dbSDimitry Andric         std::find(ExplicitRegions.begin(), ExplicitRegions.end(), R) ==
710*5ffd83dbSDimitry Andric             ExplicitRegions.end())
711*5ffd83dbSDimitry Andric       continue;
712*5ffd83dbSDimitry Andric 
713*5ffd83dbSDimitry Andric     State = State->remove<LockMap>(R);
714*5ffd83dbSDimitry Andric     State = State->remove<DestroyRetVal>(R);
715*5ffd83dbSDimitry Andric 
716*5ffd83dbSDimitry Andric     // TODO: We need to invalidate the lock stack as well. This is tricky
717*5ffd83dbSDimitry Andric     // to implement correctly and efficiently though, because the effects
718*5ffd83dbSDimitry Andric     // of mutex escapes on lock order may be fairly varied.
719*5ffd83dbSDimitry Andric   }
720*5ffd83dbSDimitry Andric 
721*5ffd83dbSDimitry Andric   return State;
722*5ffd83dbSDimitry Andric }
723*5ffd83dbSDimitry Andric 
724*5ffd83dbSDimitry Andric void ento::registerPthreadLockBase(CheckerManager &mgr) {
7250b57cec5SDimitry Andric   mgr.registerChecker<PthreadLockChecker>();
7260b57cec5SDimitry Andric }
7270b57cec5SDimitry Andric 
728*5ffd83dbSDimitry Andric bool ento::shouldRegisterPthreadLockBase(const CheckerManager &mgr) { return true; }
729*5ffd83dbSDimitry Andric 
730*5ffd83dbSDimitry Andric #define REGISTER_CHECKER(name)                                                 \
731*5ffd83dbSDimitry Andric   void ento::register##name(CheckerManager &mgr) {                             \
732*5ffd83dbSDimitry Andric     PthreadLockChecker *checker = mgr.getChecker<PthreadLockChecker>();        \
733*5ffd83dbSDimitry Andric     checker->ChecksEnabled[PthreadLockChecker::CK_##name] = true;              \
734*5ffd83dbSDimitry Andric     checker->CheckNames[PthreadLockChecker::CK_##name] =                       \
735*5ffd83dbSDimitry Andric         mgr.getCurrentCheckerName();                                           \
736*5ffd83dbSDimitry Andric   }                                                                            \
737*5ffd83dbSDimitry Andric                                                                                \
738*5ffd83dbSDimitry Andric   bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
739*5ffd83dbSDimitry Andric 
740*5ffd83dbSDimitry Andric REGISTER_CHECKER(PthreadLockChecker)
741*5ffd83dbSDimitry Andric REGISTER_CHECKER(FuchsiaLockChecker)
742*5ffd83dbSDimitry Andric REGISTER_CHECKER(C11LockChecker)
743