xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
1*0b57cec5SDimitry Andric //===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric // This defines PthreadLockChecker, a simple lock -> unlock checker.
10*0b57cec5SDimitry Andric // Also handles XNU locks, which behave similarly enough to share code.
11*0b57cec5SDimitry Andric //
12*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
13*0b57cec5SDimitry Andric 
14*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
16*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
17*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
18*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
20*0b57cec5SDimitry Andric 
21*0b57cec5SDimitry Andric using namespace clang;
22*0b57cec5SDimitry Andric using namespace ento;
23*0b57cec5SDimitry Andric 
24*0b57cec5SDimitry Andric namespace {
25*0b57cec5SDimitry Andric 
26*0b57cec5SDimitry Andric struct LockState {
27*0b57cec5SDimitry Andric   enum Kind {
28*0b57cec5SDimitry Andric     Destroyed,
29*0b57cec5SDimitry Andric     Locked,
30*0b57cec5SDimitry Andric     Unlocked,
31*0b57cec5SDimitry Andric     UntouchedAndPossiblyDestroyed,
32*0b57cec5SDimitry Andric     UnlockedAndPossiblyDestroyed
33*0b57cec5SDimitry Andric   } K;
34*0b57cec5SDimitry Andric 
35*0b57cec5SDimitry Andric private:
36*0b57cec5SDimitry Andric   LockState(Kind K) : K(K) {}
37*0b57cec5SDimitry Andric 
38*0b57cec5SDimitry Andric public:
39*0b57cec5SDimitry Andric   static LockState getLocked() { return LockState(Locked); }
40*0b57cec5SDimitry Andric   static LockState getUnlocked() { return LockState(Unlocked); }
41*0b57cec5SDimitry Andric   static LockState getDestroyed() { return LockState(Destroyed); }
42*0b57cec5SDimitry Andric   static LockState getUntouchedAndPossiblyDestroyed() {
43*0b57cec5SDimitry Andric     return LockState(UntouchedAndPossiblyDestroyed);
44*0b57cec5SDimitry Andric   }
45*0b57cec5SDimitry Andric   static LockState getUnlockedAndPossiblyDestroyed() {
46*0b57cec5SDimitry Andric     return LockState(UnlockedAndPossiblyDestroyed);
47*0b57cec5SDimitry Andric   }
48*0b57cec5SDimitry Andric 
49*0b57cec5SDimitry Andric   bool operator==(const LockState &X) const {
50*0b57cec5SDimitry Andric     return K == X.K;
51*0b57cec5SDimitry Andric   }
52*0b57cec5SDimitry Andric 
53*0b57cec5SDimitry Andric   bool isLocked() const { return K == Locked; }
54*0b57cec5SDimitry Andric   bool isUnlocked() const { return K == Unlocked; }
55*0b57cec5SDimitry Andric   bool isDestroyed() const { return K == Destroyed; }
56*0b57cec5SDimitry Andric   bool isUntouchedAndPossiblyDestroyed() const {
57*0b57cec5SDimitry Andric     return K == UntouchedAndPossiblyDestroyed;
58*0b57cec5SDimitry Andric   }
59*0b57cec5SDimitry Andric   bool isUnlockedAndPossiblyDestroyed() const {
60*0b57cec5SDimitry Andric     return K == UnlockedAndPossiblyDestroyed;
61*0b57cec5SDimitry Andric   }
62*0b57cec5SDimitry Andric 
63*0b57cec5SDimitry Andric   void Profile(llvm::FoldingSetNodeID &ID) const {
64*0b57cec5SDimitry Andric     ID.AddInteger(K);
65*0b57cec5SDimitry Andric   }
66*0b57cec5SDimitry Andric };
67*0b57cec5SDimitry Andric 
68*0b57cec5SDimitry Andric class PthreadLockChecker
69*0b57cec5SDimitry Andric     : public Checker<check::PostStmt<CallExpr>, check::DeadSymbols> {
70*0b57cec5SDimitry Andric   mutable std::unique_ptr<BugType> BT_doublelock;
71*0b57cec5SDimitry Andric   mutable std::unique_ptr<BugType> BT_doubleunlock;
72*0b57cec5SDimitry Andric   mutable std::unique_ptr<BugType> BT_destroylock;
73*0b57cec5SDimitry Andric   mutable std::unique_ptr<BugType> BT_initlock;
74*0b57cec5SDimitry Andric   mutable std::unique_ptr<BugType> BT_lor;
75*0b57cec5SDimitry Andric   enum LockingSemantics {
76*0b57cec5SDimitry Andric     NotApplicable = 0,
77*0b57cec5SDimitry Andric     PthreadSemantics,
78*0b57cec5SDimitry Andric     XNUSemantics
79*0b57cec5SDimitry Andric   };
80*0b57cec5SDimitry Andric public:
81*0b57cec5SDimitry Andric   void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
82*0b57cec5SDimitry Andric   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
83*0b57cec5SDimitry Andric   void printState(raw_ostream &Out, ProgramStateRef State,
84*0b57cec5SDimitry Andric                   const char *NL, const char *Sep) const override;
85*0b57cec5SDimitry Andric 
86*0b57cec5SDimitry Andric   void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock,
87*0b57cec5SDimitry Andric                    bool isTryLock, enum LockingSemantics semantics) const;
88*0b57cec5SDimitry Andric 
89*0b57cec5SDimitry Andric   void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const;
90*0b57cec5SDimitry Andric   void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock,
91*0b57cec5SDimitry Andric                    enum LockingSemantics semantics) const;
92*0b57cec5SDimitry Andric   void InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
93*0b57cec5SDimitry Andric   void reportUseDestroyedBug(CheckerContext &C, const CallExpr *CE) const;
94*0b57cec5SDimitry Andric   ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state,
95*0b57cec5SDimitry Andric                                                 const MemRegion *lockR,
96*0b57cec5SDimitry Andric                                                 const SymbolRef *sym) const;
97*0b57cec5SDimitry Andric };
98*0b57cec5SDimitry Andric } // end anonymous namespace
99*0b57cec5SDimitry Andric 
100*0b57cec5SDimitry Andric // A stack of locks for tracking lock-unlock order.
101*0b57cec5SDimitry Andric REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *)
102*0b57cec5SDimitry Andric 
103*0b57cec5SDimitry Andric // An entry for tracking lock states.
104*0b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState)
105*0b57cec5SDimitry Andric 
106*0b57cec5SDimitry Andric // Return values for unresolved calls to pthread_mutex_destroy().
107*0b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(DestroyRetVal, const MemRegion *, SymbolRef)
108*0b57cec5SDimitry Andric 
109*0b57cec5SDimitry Andric void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
110*0b57cec5SDimitry Andric                                        CheckerContext &C) const {
111*0b57cec5SDimitry Andric   StringRef FName = C.getCalleeName(CE);
112*0b57cec5SDimitry Andric   if (FName.empty())
113*0b57cec5SDimitry Andric     return;
114*0b57cec5SDimitry Andric 
115*0b57cec5SDimitry Andric   if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
116*0b57cec5SDimitry Andric     return;
117*0b57cec5SDimitry Andric 
118*0b57cec5SDimitry Andric   if (FName == "pthread_mutex_lock" ||
119*0b57cec5SDimitry Andric       FName == "pthread_rwlock_rdlock" ||
120*0b57cec5SDimitry Andric       FName == "pthread_rwlock_wrlock")
121*0b57cec5SDimitry Andric     AcquireLock(C, CE, C.getSVal(CE->getArg(0)), false, PthreadSemantics);
122*0b57cec5SDimitry Andric   else if (FName == "lck_mtx_lock" ||
123*0b57cec5SDimitry Andric            FName == "lck_rw_lock_exclusive" ||
124*0b57cec5SDimitry Andric            FName == "lck_rw_lock_shared")
125*0b57cec5SDimitry Andric     AcquireLock(C, CE, C.getSVal(CE->getArg(0)), false, XNUSemantics);
126*0b57cec5SDimitry Andric   else if (FName == "pthread_mutex_trylock" ||
127*0b57cec5SDimitry Andric            FName == "pthread_rwlock_tryrdlock" ||
128*0b57cec5SDimitry Andric            FName == "pthread_rwlock_trywrlock")
129*0b57cec5SDimitry Andric     AcquireLock(C, CE, C.getSVal(CE->getArg(0)),
130*0b57cec5SDimitry Andric                 true, PthreadSemantics);
131*0b57cec5SDimitry Andric   else if (FName == "lck_mtx_try_lock" ||
132*0b57cec5SDimitry Andric            FName == "lck_rw_try_lock_exclusive" ||
133*0b57cec5SDimitry Andric            FName == "lck_rw_try_lock_shared")
134*0b57cec5SDimitry Andric     AcquireLock(C, CE, C.getSVal(CE->getArg(0)), true, XNUSemantics);
135*0b57cec5SDimitry Andric   else if (FName == "pthread_mutex_unlock" ||
136*0b57cec5SDimitry Andric            FName == "pthread_rwlock_unlock" ||
137*0b57cec5SDimitry Andric            FName == "lck_mtx_unlock" ||
138*0b57cec5SDimitry Andric            FName == "lck_rw_done")
139*0b57cec5SDimitry Andric     ReleaseLock(C, CE, C.getSVal(CE->getArg(0)));
140*0b57cec5SDimitry Andric   else if (FName == "pthread_mutex_destroy")
141*0b57cec5SDimitry Andric     DestroyLock(C, CE, C.getSVal(CE->getArg(0)), PthreadSemantics);
142*0b57cec5SDimitry Andric   else if (FName == "lck_mtx_destroy")
143*0b57cec5SDimitry Andric     DestroyLock(C, CE, C.getSVal(CE->getArg(0)), XNUSemantics);
144*0b57cec5SDimitry Andric   else if (FName == "pthread_mutex_init")
145*0b57cec5SDimitry Andric     InitLock(C, CE, C.getSVal(CE->getArg(0)));
146*0b57cec5SDimitry Andric }
147*0b57cec5SDimitry Andric 
148*0b57cec5SDimitry Andric // When a lock is destroyed, in some semantics(like PthreadSemantics) we are not
149*0b57cec5SDimitry Andric // sure if the destroy call has succeeded or failed, and the lock enters one of
150*0b57cec5SDimitry Andric // the 'possibly destroyed' state. There is a short time frame for the
151*0b57cec5SDimitry Andric // programmer to check the return value to see if the lock was successfully
152*0b57cec5SDimitry Andric // destroyed. Before we model the next operation over that lock, we call this
153*0b57cec5SDimitry Andric // function to see if the return value was checked by now and set the lock state
154*0b57cec5SDimitry Andric // - either to destroyed state or back to its previous state.
155*0b57cec5SDimitry Andric 
156*0b57cec5SDimitry Andric // In PthreadSemantics, pthread_mutex_destroy() returns zero if the lock is
157*0b57cec5SDimitry Andric // successfully destroyed and it returns a non-zero value otherwise.
158*0b57cec5SDimitry Andric ProgramStateRef PthreadLockChecker::resolvePossiblyDestroyedMutex(
159*0b57cec5SDimitry Andric     ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const {
160*0b57cec5SDimitry Andric   const LockState *lstate = state->get<LockMap>(lockR);
161*0b57cec5SDimitry Andric   // Existence in DestroyRetVal ensures existence in LockMap.
162*0b57cec5SDimitry Andric   // Existence in Destroyed also ensures that the lock state for lockR is either
163*0b57cec5SDimitry Andric   // UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed.
164*0b57cec5SDimitry Andric   assert(lstate->isUntouchedAndPossiblyDestroyed() ||
165*0b57cec5SDimitry Andric          lstate->isUnlockedAndPossiblyDestroyed());
166*0b57cec5SDimitry Andric 
167*0b57cec5SDimitry Andric   ConstraintManager &CMgr = state->getConstraintManager();
168*0b57cec5SDimitry Andric   ConditionTruthVal retZero = CMgr.isNull(state, *sym);
169*0b57cec5SDimitry Andric   if (retZero.isConstrainedFalse()) {
170*0b57cec5SDimitry Andric     if (lstate->isUntouchedAndPossiblyDestroyed())
171*0b57cec5SDimitry Andric       state = state->remove<LockMap>(lockR);
172*0b57cec5SDimitry Andric     else if (lstate->isUnlockedAndPossiblyDestroyed())
173*0b57cec5SDimitry Andric       state = state->set<LockMap>(lockR, LockState::getUnlocked());
174*0b57cec5SDimitry Andric   } else
175*0b57cec5SDimitry Andric     state = state->set<LockMap>(lockR, LockState::getDestroyed());
176*0b57cec5SDimitry Andric 
177*0b57cec5SDimitry Andric   // Removing the map entry (lockR, sym) from DestroyRetVal as the lock state is
178*0b57cec5SDimitry Andric   // now resolved.
179*0b57cec5SDimitry Andric   state = state->remove<DestroyRetVal>(lockR);
180*0b57cec5SDimitry Andric   return state;
181*0b57cec5SDimitry Andric }
182*0b57cec5SDimitry Andric 
183*0b57cec5SDimitry Andric void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State,
184*0b57cec5SDimitry Andric                                     const char *NL, const char *Sep) const {
185*0b57cec5SDimitry Andric   LockMapTy LM = State->get<LockMap>();
186*0b57cec5SDimitry Andric   if (!LM.isEmpty()) {
187*0b57cec5SDimitry Andric     Out << Sep << "Mutex states:" << NL;
188*0b57cec5SDimitry Andric     for (auto I : LM) {
189*0b57cec5SDimitry Andric       I.first->dumpToStream(Out);
190*0b57cec5SDimitry Andric       if (I.second.isLocked())
191*0b57cec5SDimitry Andric         Out << ": locked";
192*0b57cec5SDimitry Andric       else if (I.second.isUnlocked())
193*0b57cec5SDimitry Andric         Out << ": unlocked";
194*0b57cec5SDimitry Andric       else if (I.second.isDestroyed())
195*0b57cec5SDimitry Andric         Out << ": destroyed";
196*0b57cec5SDimitry Andric       else if (I.second.isUntouchedAndPossiblyDestroyed())
197*0b57cec5SDimitry Andric         Out << ": not tracked, possibly destroyed";
198*0b57cec5SDimitry Andric       else if (I.second.isUnlockedAndPossiblyDestroyed())
199*0b57cec5SDimitry Andric         Out << ": unlocked, possibly destroyed";
200*0b57cec5SDimitry Andric       Out << NL;
201*0b57cec5SDimitry Andric     }
202*0b57cec5SDimitry Andric   }
203*0b57cec5SDimitry Andric 
204*0b57cec5SDimitry Andric   LockSetTy LS = State->get<LockSet>();
205*0b57cec5SDimitry Andric   if (!LS.isEmpty()) {
206*0b57cec5SDimitry Andric     Out << Sep << "Mutex lock order:" << NL;
207*0b57cec5SDimitry Andric     for (auto I: LS) {
208*0b57cec5SDimitry Andric       I->dumpToStream(Out);
209*0b57cec5SDimitry Andric       Out << NL;
210*0b57cec5SDimitry Andric     }
211*0b57cec5SDimitry Andric   }
212*0b57cec5SDimitry Andric 
213*0b57cec5SDimitry Andric   // TODO: Dump destroyed mutex symbols?
214*0b57cec5SDimitry Andric }
215*0b57cec5SDimitry Andric 
216*0b57cec5SDimitry Andric void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
217*0b57cec5SDimitry Andric                                      SVal lock, bool isTryLock,
218*0b57cec5SDimitry Andric                                      enum LockingSemantics semantics) const {
219*0b57cec5SDimitry Andric 
220*0b57cec5SDimitry Andric   const MemRegion *lockR = lock.getAsRegion();
221*0b57cec5SDimitry Andric   if (!lockR)
222*0b57cec5SDimitry Andric     return;
223*0b57cec5SDimitry Andric 
224*0b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
225*0b57cec5SDimitry Andric   const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
226*0b57cec5SDimitry Andric   if (sym)
227*0b57cec5SDimitry Andric     state = resolvePossiblyDestroyedMutex(state, lockR, sym);
228*0b57cec5SDimitry Andric 
229*0b57cec5SDimitry Andric   SVal X = C.getSVal(CE);
230*0b57cec5SDimitry Andric   if (X.isUnknownOrUndef())
231*0b57cec5SDimitry Andric     return;
232*0b57cec5SDimitry Andric 
233*0b57cec5SDimitry Andric   DefinedSVal retVal = X.castAs<DefinedSVal>();
234*0b57cec5SDimitry Andric 
235*0b57cec5SDimitry Andric   if (const LockState *LState = state->get<LockMap>(lockR)) {
236*0b57cec5SDimitry Andric     if (LState->isLocked()) {
237*0b57cec5SDimitry Andric       if (!BT_doublelock)
238*0b57cec5SDimitry Andric         BT_doublelock.reset(new BugType(this, "Double locking",
239*0b57cec5SDimitry Andric                                         "Lock checker"));
240*0b57cec5SDimitry Andric       ExplodedNode *N = C.generateErrorNode();
241*0b57cec5SDimitry Andric       if (!N)
242*0b57cec5SDimitry Andric         return;
243*0b57cec5SDimitry Andric       auto report = llvm::make_unique<BugReport>(
244*0b57cec5SDimitry Andric           *BT_doublelock, "This lock has already been acquired", N);
245*0b57cec5SDimitry Andric       report->addRange(CE->getArg(0)->getSourceRange());
246*0b57cec5SDimitry Andric       C.emitReport(std::move(report));
247*0b57cec5SDimitry Andric       return;
248*0b57cec5SDimitry Andric     } else if (LState->isDestroyed()) {
249*0b57cec5SDimitry Andric       reportUseDestroyedBug(C, CE);
250*0b57cec5SDimitry Andric       return;
251*0b57cec5SDimitry Andric     }
252*0b57cec5SDimitry Andric   }
253*0b57cec5SDimitry Andric 
254*0b57cec5SDimitry Andric   ProgramStateRef lockSucc = state;
255*0b57cec5SDimitry Andric   if (isTryLock) {
256*0b57cec5SDimitry Andric     // Bifurcate the state, and allow a mode where the lock acquisition fails.
257*0b57cec5SDimitry Andric     ProgramStateRef lockFail;
258*0b57cec5SDimitry Andric     switch (semantics) {
259*0b57cec5SDimitry Andric     case PthreadSemantics:
260*0b57cec5SDimitry Andric       std::tie(lockFail, lockSucc) = state->assume(retVal);
261*0b57cec5SDimitry Andric       break;
262*0b57cec5SDimitry Andric     case XNUSemantics:
263*0b57cec5SDimitry Andric       std::tie(lockSucc, lockFail) = state->assume(retVal);
264*0b57cec5SDimitry Andric       break;
265*0b57cec5SDimitry Andric     default:
266*0b57cec5SDimitry Andric       llvm_unreachable("Unknown tryLock locking semantics");
267*0b57cec5SDimitry Andric     }
268*0b57cec5SDimitry Andric     assert(lockFail && lockSucc);
269*0b57cec5SDimitry Andric     C.addTransition(lockFail);
270*0b57cec5SDimitry Andric 
271*0b57cec5SDimitry Andric   } else if (semantics == PthreadSemantics) {
272*0b57cec5SDimitry Andric     // Assume that the return value was 0.
273*0b57cec5SDimitry Andric     lockSucc = state->assume(retVal, false);
274*0b57cec5SDimitry Andric     assert(lockSucc);
275*0b57cec5SDimitry Andric 
276*0b57cec5SDimitry Andric   } else {
277*0b57cec5SDimitry Andric     // XNU locking semantics return void on non-try locks
278*0b57cec5SDimitry Andric     assert((semantics == XNUSemantics) && "Unknown locking semantics");
279*0b57cec5SDimitry Andric     lockSucc = state;
280*0b57cec5SDimitry Andric   }
281*0b57cec5SDimitry Andric 
282*0b57cec5SDimitry Andric   // Record that the lock was acquired.
283*0b57cec5SDimitry Andric   lockSucc = lockSucc->add<LockSet>(lockR);
284*0b57cec5SDimitry Andric   lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked());
285*0b57cec5SDimitry Andric   C.addTransition(lockSucc);
286*0b57cec5SDimitry Andric }
287*0b57cec5SDimitry Andric 
288*0b57cec5SDimitry Andric void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
289*0b57cec5SDimitry Andric                                      SVal lock) const {
290*0b57cec5SDimitry Andric 
291*0b57cec5SDimitry Andric   const MemRegion *lockR = lock.getAsRegion();
292*0b57cec5SDimitry Andric   if (!lockR)
293*0b57cec5SDimitry Andric     return;
294*0b57cec5SDimitry Andric 
295*0b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
296*0b57cec5SDimitry Andric   const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
297*0b57cec5SDimitry Andric   if (sym)
298*0b57cec5SDimitry Andric     state = resolvePossiblyDestroyedMutex(state, lockR, sym);
299*0b57cec5SDimitry Andric 
300*0b57cec5SDimitry Andric   if (const LockState *LState = state->get<LockMap>(lockR)) {
301*0b57cec5SDimitry Andric     if (LState->isUnlocked()) {
302*0b57cec5SDimitry Andric       if (!BT_doubleunlock)
303*0b57cec5SDimitry Andric         BT_doubleunlock.reset(new BugType(this, "Double unlocking",
304*0b57cec5SDimitry Andric                                           "Lock checker"));
305*0b57cec5SDimitry Andric       ExplodedNode *N = C.generateErrorNode();
306*0b57cec5SDimitry Andric       if (!N)
307*0b57cec5SDimitry Andric         return;
308*0b57cec5SDimitry Andric       auto Report = llvm::make_unique<BugReport>(
309*0b57cec5SDimitry Andric           *BT_doubleunlock, "This lock has already been unlocked", N);
310*0b57cec5SDimitry Andric       Report->addRange(CE->getArg(0)->getSourceRange());
311*0b57cec5SDimitry Andric       C.emitReport(std::move(Report));
312*0b57cec5SDimitry Andric       return;
313*0b57cec5SDimitry Andric     } else if (LState->isDestroyed()) {
314*0b57cec5SDimitry Andric       reportUseDestroyedBug(C, CE);
315*0b57cec5SDimitry Andric       return;
316*0b57cec5SDimitry Andric     }
317*0b57cec5SDimitry Andric   }
318*0b57cec5SDimitry Andric 
319*0b57cec5SDimitry Andric   LockSetTy LS = state->get<LockSet>();
320*0b57cec5SDimitry Andric 
321*0b57cec5SDimitry Andric   // FIXME: Better analysis requires IPA for wrappers.
322*0b57cec5SDimitry Andric 
323*0b57cec5SDimitry Andric   if (!LS.isEmpty()) {
324*0b57cec5SDimitry Andric     const MemRegion *firstLockR = LS.getHead();
325*0b57cec5SDimitry Andric     if (firstLockR != lockR) {
326*0b57cec5SDimitry Andric       if (!BT_lor)
327*0b57cec5SDimitry Andric         BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker"));
328*0b57cec5SDimitry Andric       ExplodedNode *N = C.generateErrorNode();
329*0b57cec5SDimitry Andric       if (!N)
330*0b57cec5SDimitry Andric         return;
331*0b57cec5SDimitry Andric       auto report = llvm::make_unique<BugReport>(
332*0b57cec5SDimitry Andric           *BT_lor, "This was not the most recently acquired lock. Possible "
333*0b57cec5SDimitry Andric                    "lock order reversal", N);
334*0b57cec5SDimitry Andric       report->addRange(CE->getArg(0)->getSourceRange());
335*0b57cec5SDimitry Andric       C.emitReport(std::move(report));
336*0b57cec5SDimitry Andric       return;
337*0b57cec5SDimitry Andric     }
338*0b57cec5SDimitry Andric     // Record that the lock was released.
339*0b57cec5SDimitry Andric     state = state->set<LockSet>(LS.getTail());
340*0b57cec5SDimitry Andric   }
341*0b57cec5SDimitry Andric 
342*0b57cec5SDimitry Andric   state = state->set<LockMap>(lockR, LockState::getUnlocked());
343*0b57cec5SDimitry Andric   C.addTransition(state);
344*0b57cec5SDimitry Andric }
345*0b57cec5SDimitry Andric 
346*0b57cec5SDimitry Andric void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE,
347*0b57cec5SDimitry Andric                                      SVal Lock,
348*0b57cec5SDimitry Andric                                      enum LockingSemantics semantics) const {
349*0b57cec5SDimitry Andric 
350*0b57cec5SDimitry Andric   const MemRegion *LockR = Lock.getAsRegion();
351*0b57cec5SDimitry Andric   if (!LockR)
352*0b57cec5SDimitry Andric     return;
353*0b57cec5SDimitry Andric 
354*0b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
355*0b57cec5SDimitry Andric 
356*0b57cec5SDimitry Andric   const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
357*0b57cec5SDimitry Andric   if (sym)
358*0b57cec5SDimitry Andric     State = resolvePossiblyDestroyedMutex(State, LockR, sym);
359*0b57cec5SDimitry Andric 
360*0b57cec5SDimitry Andric   const LockState *LState = State->get<LockMap>(LockR);
361*0b57cec5SDimitry Andric   // Checking the return value of the destroy method only in the case of
362*0b57cec5SDimitry Andric   // PthreadSemantics
363*0b57cec5SDimitry Andric   if (semantics == PthreadSemantics) {
364*0b57cec5SDimitry Andric     if (!LState || LState->isUnlocked()) {
365*0b57cec5SDimitry Andric       SymbolRef sym = C.getSVal(CE).getAsSymbol();
366*0b57cec5SDimitry Andric       if (!sym) {
367*0b57cec5SDimitry Andric         State = State->remove<LockMap>(LockR);
368*0b57cec5SDimitry Andric         C.addTransition(State);
369*0b57cec5SDimitry Andric         return;
370*0b57cec5SDimitry Andric       }
371*0b57cec5SDimitry Andric       State = State->set<DestroyRetVal>(LockR, sym);
372*0b57cec5SDimitry Andric       if (LState && LState->isUnlocked())
373*0b57cec5SDimitry Andric         State = State->set<LockMap>(
374*0b57cec5SDimitry Andric             LockR, LockState::getUnlockedAndPossiblyDestroyed());
375*0b57cec5SDimitry Andric       else
376*0b57cec5SDimitry Andric         State = State->set<LockMap>(
377*0b57cec5SDimitry Andric             LockR, LockState::getUntouchedAndPossiblyDestroyed());
378*0b57cec5SDimitry Andric       C.addTransition(State);
379*0b57cec5SDimitry Andric       return;
380*0b57cec5SDimitry Andric     }
381*0b57cec5SDimitry Andric   } else {
382*0b57cec5SDimitry Andric     if (!LState || LState->isUnlocked()) {
383*0b57cec5SDimitry Andric       State = State->set<LockMap>(LockR, LockState::getDestroyed());
384*0b57cec5SDimitry Andric       C.addTransition(State);
385*0b57cec5SDimitry Andric       return;
386*0b57cec5SDimitry Andric     }
387*0b57cec5SDimitry Andric   }
388*0b57cec5SDimitry Andric   StringRef Message;
389*0b57cec5SDimitry Andric 
390*0b57cec5SDimitry Andric   if (LState->isLocked()) {
391*0b57cec5SDimitry Andric     Message = "This lock is still locked";
392*0b57cec5SDimitry Andric   } else {
393*0b57cec5SDimitry Andric     Message = "This lock has already been destroyed";
394*0b57cec5SDimitry Andric   }
395*0b57cec5SDimitry Andric 
396*0b57cec5SDimitry Andric   if (!BT_destroylock)
397*0b57cec5SDimitry Andric     BT_destroylock.reset(new BugType(this, "Destroy invalid lock",
398*0b57cec5SDimitry Andric                                      "Lock checker"));
399*0b57cec5SDimitry Andric   ExplodedNode *N = C.generateErrorNode();
400*0b57cec5SDimitry Andric   if (!N)
401*0b57cec5SDimitry Andric     return;
402*0b57cec5SDimitry Andric   auto Report = llvm::make_unique<BugReport>(*BT_destroylock, Message, N);
403*0b57cec5SDimitry Andric   Report->addRange(CE->getArg(0)->getSourceRange());
404*0b57cec5SDimitry Andric   C.emitReport(std::move(Report));
405*0b57cec5SDimitry Andric }
406*0b57cec5SDimitry Andric 
407*0b57cec5SDimitry Andric void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE,
408*0b57cec5SDimitry Andric                                   SVal Lock) const {
409*0b57cec5SDimitry Andric 
410*0b57cec5SDimitry Andric   const MemRegion *LockR = Lock.getAsRegion();
411*0b57cec5SDimitry Andric   if (!LockR)
412*0b57cec5SDimitry Andric     return;
413*0b57cec5SDimitry Andric 
414*0b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
415*0b57cec5SDimitry Andric 
416*0b57cec5SDimitry Andric   const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
417*0b57cec5SDimitry Andric   if (sym)
418*0b57cec5SDimitry Andric     State = resolvePossiblyDestroyedMutex(State, LockR, sym);
419*0b57cec5SDimitry Andric 
420*0b57cec5SDimitry Andric   const struct LockState *LState = State->get<LockMap>(LockR);
421*0b57cec5SDimitry Andric   if (!LState || LState->isDestroyed()) {
422*0b57cec5SDimitry Andric     State = State->set<LockMap>(LockR, LockState::getUnlocked());
423*0b57cec5SDimitry Andric     C.addTransition(State);
424*0b57cec5SDimitry Andric     return;
425*0b57cec5SDimitry Andric   }
426*0b57cec5SDimitry Andric 
427*0b57cec5SDimitry Andric   StringRef Message;
428*0b57cec5SDimitry Andric 
429*0b57cec5SDimitry Andric   if (LState->isLocked()) {
430*0b57cec5SDimitry Andric     Message = "This lock is still being held";
431*0b57cec5SDimitry Andric   } else {
432*0b57cec5SDimitry Andric     Message = "This lock has already been initialized";
433*0b57cec5SDimitry Andric   }
434*0b57cec5SDimitry Andric 
435*0b57cec5SDimitry Andric   if (!BT_initlock)
436*0b57cec5SDimitry Andric     BT_initlock.reset(new BugType(this, "Init invalid lock",
437*0b57cec5SDimitry Andric                                   "Lock checker"));
438*0b57cec5SDimitry Andric   ExplodedNode *N = C.generateErrorNode();
439*0b57cec5SDimitry Andric   if (!N)
440*0b57cec5SDimitry Andric     return;
441*0b57cec5SDimitry Andric   auto Report = llvm::make_unique<BugReport>(*BT_initlock, Message, N);
442*0b57cec5SDimitry Andric   Report->addRange(CE->getArg(0)->getSourceRange());
443*0b57cec5SDimitry Andric   C.emitReport(std::move(Report));
444*0b57cec5SDimitry Andric }
445*0b57cec5SDimitry Andric 
446*0b57cec5SDimitry Andric void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C,
447*0b57cec5SDimitry Andric                                                const CallExpr *CE) const {
448*0b57cec5SDimitry Andric   if (!BT_destroylock)
449*0b57cec5SDimitry Andric     BT_destroylock.reset(new BugType(this, "Use destroyed lock",
450*0b57cec5SDimitry Andric                                      "Lock checker"));
451*0b57cec5SDimitry Andric   ExplodedNode *N = C.generateErrorNode();
452*0b57cec5SDimitry Andric   if (!N)
453*0b57cec5SDimitry Andric     return;
454*0b57cec5SDimitry Andric   auto Report = llvm::make_unique<BugReport>(
455*0b57cec5SDimitry Andric       *BT_destroylock, "This lock has already been destroyed", N);
456*0b57cec5SDimitry Andric   Report->addRange(CE->getArg(0)->getSourceRange());
457*0b57cec5SDimitry Andric   C.emitReport(std::move(Report));
458*0b57cec5SDimitry Andric }
459*0b57cec5SDimitry Andric 
460*0b57cec5SDimitry Andric void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper,
461*0b57cec5SDimitry Andric                                           CheckerContext &C) const {
462*0b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
463*0b57cec5SDimitry Andric 
464*0b57cec5SDimitry Andric   // TODO: Clean LockMap when a mutex region dies.
465*0b57cec5SDimitry Andric 
466*0b57cec5SDimitry Andric   DestroyRetValTy TrackedSymbols = State->get<DestroyRetVal>();
467*0b57cec5SDimitry Andric   for (DestroyRetValTy::iterator I = TrackedSymbols.begin(),
468*0b57cec5SDimitry Andric                                  E = TrackedSymbols.end();
469*0b57cec5SDimitry Andric        I != E; ++I) {
470*0b57cec5SDimitry Andric     const SymbolRef Sym = I->second;
471*0b57cec5SDimitry Andric     const MemRegion *lockR = I->first;
472*0b57cec5SDimitry Andric     bool IsSymDead = SymReaper.isDead(Sym);
473*0b57cec5SDimitry Andric     // Remove the dead symbol from the return value symbols map.
474*0b57cec5SDimitry Andric     if (IsSymDead)
475*0b57cec5SDimitry Andric       State = resolvePossiblyDestroyedMutex(State, lockR, &Sym);
476*0b57cec5SDimitry Andric   }
477*0b57cec5SDimitry Andric   C.addTransition(State);
478*0b57cec5SDimitry Andric }
479*0b57cec5SDimitry Andric 
480*0b57cec5SDimitry Andric void ento::registerPthreadLockChecker(CheckerManager &mgr) {
481*0b57cec5SDimitry Andric   mgr.registerChecker<PthreadLockChecker>();
482*0b57cec5SDimitry Andric }
483*0b57cec5SDimitry Andric 
484*0b57cec5SDimitry Andric bool ento::shouldRegisterPthreadLockChecker(const LangOptions &LO) {
485*0b57cec5SDimitry Andric   return true;
486*0b57cec5SDimitry Andric }
487