1*67e74705SXin Li //===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===//
2*67e74705SXin Li //
3*67e74705SXin Li // The LLVM Compiler Infrastructure
4*67e74705SXin Li //
5*67e74705SXin Li // This file is distributed under the University of Illinois Open Source
6*67e74705SXin Li // License. See LICENSE.TXT for details.
7*67e74705SXin Li //
8*67e74705SXin Li //===----------------------------------------------------------------------===//
9*67e74705SXin Li //
10*67e74705SXin Li // This defines PthreadLockChecker, a simple lock -> unlock checker.
11*67e74705SXin Li // Also handles XNU locks, which behave similarly enough to share code.
12*67e74705SXin Li //
13*67e74705SXin Li //===----------------------------------------------------------------------===//
14*67e74705SXin Li
15*67e74705SXin Li #include "ClangSACheckers.h"
16*67e74705SXin Li #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17*67e74705SXin Li #include "clang/StaticAnalyzer/Core/Checker.h"
18*67e74705SXin Li #include "clang/StaticAnalyzer/Core/CheckerManager.h"
19*67e74705SXin Li #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20*67e74705SXin Li #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
21*67e74705SXin Li #include "llvm/ADT/ImmutableList.h"
22*67e74705SXin Li
23*67e74705SXin Li using namespace clang;
24*67e74705SXin Li using namespace ento;
25*67e74705SXin Li
26*67e74705SXin Li namespace {
27*67e74705SXin Li
28*67e74705SXin Li struct LockState {
29*67e74705SXin Li enum Kind { Destroyed, Locked, Unlocked } K;
30*67e74705SXin Li
31*67e74705SXin Li private:
LockState__anonef6189ac0111::LockState32*67e74705SXin Li LockState(Kind K) : K(K) {}
33*67e74705SXin Li
34*67e74705SXin Li public:
getLocked__anonef6189ac0111::LockState35*67e74705SXin Li static LockState getLocked() { return LockState(Locked); }
getUnlocked__anonef6189ac0111::LockState36*67e74705SXin Li static LockState getUnlocked() { return LockState(Unlocked); }
getDestroyed__anonef6189ac0111::LockState37*67e74705SXin Li static LockState getDestroyed() { return LockState(Destroyed); }
38*67e74705SXin Li
operator ==__anonef6189ac0111::LockState39*67e74705SXin Li bool operator==(const LockState &X) const {
40*67e74705SXin Li return K == X.K;
41*67e74705SXin Li }
42*67e74705SXin Li
isLocked__anonef6189ac0111::LockState43*67e74705SXin Li bool isLocked() const { return K == Locked; }
isUnlocked__anonef6189ac0111::LockState44*67e74705SXin Li bool isUnlocked() const { return K == Unlocked; }
isDestroyed__anonef6189ac0111::LockState45*67e74705SXin Li bool isDestroyed() const { return K == Destroyed; }
46*67e74705SXin Li
Profile__anonef6189ac0111::LockState47*67e74705SXin Li void Profile(llvm::FoldingSetNodeID &ID) const {
48*67e74705SXin Li ID.AddInteger(K);
49*67e74705SXin Li }
50*67e74705SXin Li };
51*67e74705SXin Li
52*67e74705SXin Li class PthreadLockChecker : public Checker< check::PostStmt<CallExpr> > {
53*67e74705SXin Li mutable std::unique_ptr<BugType> BT_doublelock;
54*67e74705SXin Li mutable std::unique_ptr<BugType> BT_doubleunlock;
55*67e74705SXin Li mutable std::unique_ptr<BugType> BT_destroylock;
56*67e74705SXin Li mutable std::unique_ptr<BugType> BT_initlock;
57*67e74705SXin Li mutable std::unique_ptr<BugType> BT_lor;
58*67e74705SXin Li enum LockingSemantics {
59*67e74705SXin Li NotApplicable = 0,
60*67e74705SXin Li PthreadSemantics,
61*67e74705SXin Li XNUSemantics
62*67e74705SXin Li };
63*67e74705SXin Li public:
64*67e74705SXin Li void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
65*67e74705SXin Li
66*67e74705SXin Li void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock,
67*67e74705SXin Li bool isTryLock, enum LockingSemantics semantics) const;
68*67e74705SXin Li
69*67e74705SXin Li void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const;
70*67e74705SXin Li void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
71*67e74705SXin Li void InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
72*67e74705SXin Li void reportUseDestroyedBug(CheckerContext &C, const CallExpr *CE) const;
73*67e74705SXin Li };
74*67e74705SXin Li } // end anonymous namespace
75*67e74705SXin Li
76*67e74705SXin Li // GDM Entry for tracking lock state.
REGISTER_LIST_WITH_PROGRAMSTATE(LockSet,const MemRegion *)77*67e74705SXin Li REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *)
78*67e74705SXin Li
79*67e74705SXin Li REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState)
80*67e74705SXin Li
81*67e74705SXin Li void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
82*67e74705SXin Li CheckerContext &C) const {
83*67e74705SXin Li ProgramStateRef state = C.getState();
84*67e74705SXin Li const LocationContext *LCtx = C.getLocationContext();
85*67e74705SXin Li StringRef FName = C.getCalleeName(CE);
86*67e74705SXin Li if (FName.empty())
87*67e74705SXin Li return;
88*67e74705SXin Li
89*67e74705SXin Li if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
90*67e74705SXin Li return;
91*67e74705SXin Li
92*67e74705SXin Li if (FName == "pthread_mutex_lock" ||
93*67e74705SXin Li FName == "pthread_rwlock_rdlock" ||
94*67e74705SXin Li FName == "pthread_rwlock_wrlock")
95*67e74705SXin Li AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
96*67e74705SXin Li false, PthreadSemantics);
97*67e74705SXin Li else if (FName == "lck_mtx_lock" ||
98*67e74705SXin Li FName == "lck_rw_lock_exclusive" ||
99*67e74705SXin Li FName == "lck_rw_lock_shared")
100*67e74705SXin Li AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
101*67e74705SXin Li false, XNUSemantics);
102*67e74705SXin Li else if (FName == "pthread_mutex_trylock" ||
103*67e74705SXin Li FName == "pthread_rwlock_tryrdlock" ||
104*67e74705SXin Li FName == "pthread_rwlock_trywrlock")
105*67e74705SXin Li AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
106*67e74705SXin Li true, PthreadSemantics);
107*67e74705SXin Li else if (FName == "lck_mtx_try_lock" ||
108*67e74705SXin Li FName == "lck_rw_try_lock_exclusive" ||
109*67e74705SXin Li FName == "lck_rw_try_lock_shared")
110*67e74705SXin Li AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
111*67e74705SXin Li true, XNUSemantics);
112*67e74705SXin Li else if (FName == "pthread_mutex_unlock" ||
113*67e74705SXin Li FName == "pthread_rwlock_unlock" ||
114*67e74705SXin Li FName == "lck_mtx_unlock" ||
115*67e74705SXin Li FName == "lck_rw_done")
116*67e74705SXin Li ReleaseLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
117*67e74705SXin Li else if (FName == "pthread_mutex_destroy" ||
118*67e74705SXin Li FName == "lck_mtx_destroy")
119*67e74705SXin Li DestroyLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
120*67e74705SXin Li else if (FName == "pthread_mutex_init")
121*67e74705SXin Li InitLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
122*67e74705SXin Li }
123*67e74705SXin Li
AcquireLock(CheckerContext & C,const CallExpr * CE,SVal lock,bool isTryLock,enum LockingSemantics semantics) const124*67e74705SXin Li void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
125*67e74705SXin Li SVal lock, bool isTryLock,
126*67e74705SXin Li enum LockingSemantics semantics) const {
127*67e74705SXin Li
128*67e74705SXin Li const MemRegion *lockR = lock.getAsRegion();
129*67e74705SXin Li if (!lockR)
130*67e74705SXin Li return;
131*67e74705SXin Li
132*67e74705SXin Li ProgramStateRef state = C.getState();
133*67e74705SXin Li
134*67e74705SXin Li SVal X = state->getSVal(CE, C.getLocationContext());
135*67e74705SXin Li if (X.isUnknownOrUndef())
136*67e74705SXin Li return;
137*67e74705SXin Li
138*67e74705SXin Li DefinedSVal retVal = X.castAs<DefinedSVal>();
139*67e74705SXin Li
140*67e74705SXin Li if (const LockState *LState = state->get<LockMap>(lockR)) {
141*67e74705SXin Li if (LState->isLocked()) {
142*67e74705SXin Li if (!BT_doublelock)
143*67e74705SXin Li BT_doublelock.reset(new BugType(this, "Double locking",
144*67e74705SXin Li "Lock checker"));
145*67e74705SXin Li ExplodedNode *N = C.generateErrorNode();
146*67e74705SXin Li if (!N)
147*67e74705SXin Li return;
148*67e74705SXin Li auto report = llvm::make_unique<BugReport>(
149*67e74705SXin Li *BT_doublelock, "This lock has already been acquired", N);
150*67e74705SXin Li report->addRange(CE->getArg(0)->getSourceRange());
151*67e74705SXin Li C.emitReport(std::move(report));
152*67e74705SXin Li return;
153*67e74705SXin Li } else if (LState->isDestroyed()) {
154*67e74705SXin Li reportUseDestroyedBug(C, CE);
155*67e74705SXin Li return;
156*67e74705SXin Li }
157*67e74705SXin Li }
158*67e74705SXin Li
159*67e74705SXin Li ProgramStateRef lockSucc = state;
160*67e74705SXin Li if (isTryLock) {
161*67e74705SXin Li // Bifurcate the state, and allow a mode where the lock acquisition fails.
162*67e74705SXin Li ProgramStateRef lockFail;
163*67e74705SXin Li switch (semantics) {
164*67e74705SXin Li case PthreadSemantics:
165*67e74705SXin Li std::tie(lockFail, lockSucc) = state->assume(retVal);
166*67e74705SXin Li break;
167*67e74705SXin Li case XNUSemantics:
168*67e74705SXin Li std::tie(lockSucc, lockFail) = state->assume(retVal);
169*67e74705SXin Li break;
170*67e74705SXin Li default:
171*67e74705SXin Li llvm_unreachable("Unknown tryLock locking semantics");
172*67e74705SXin Li }
173*67e74705SXin Li assert(lockFail && lockSucc);
174*67e74705SXin Li C.addTransition(lockFail);
175*67e74705SXin Li
176*67e74705SXin Li } else if (semantics == PthreadSemantics) {
177*67e74705SXin Li // Assume that the return value was 0.
178*67e74705SXin Li lockSucc = state->assume(retVal, false);
179*67e74705SXin Li assert(lockSucc);
180*67e74705SXin Li
181*67e74705SXin Li } else {
182*67e74705SXin Li // XNU locking semantics return void on non-try locks
183*67e74705SXin Li assert((semantics == XNUSemantics) && "Unknown locking semantics");
184*67e74705SXin Li lockSucc = state;
185*67e74705SXin Li }
186*67e74705SXin Li
187*67e74705SXin Li // Record that the lock was acquired.
188*67e74705SXin Li lockSucc = lockSucc->add<LockSet>(lockR);
189*67e74705SXin Li lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked());
190*67e74705SXin Li C.addTransition(lockSucc);
191*67e74705SXin Li }
192*67e74705SXin Li
ReleaseLock(CheckerContext & C,const CallExpr * CE,SVal lock) const193*67e74705SXin Li void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
194*67e74705SXin Li SVal lock) const {
195*67e74705SXin Li
196*67e74705SXin Li const MemRegion *lockR = lock.getAsRegion();
197*67e74705SXin Li if (!lockR)
198*67e74705SXin Li return;
199*67e74705SXin Li
200*67e74705SXin Li ProgramStateRef state = C.getState();
201*67e74705SXin Li
202*67e74705SXin Li if (const LockState *LState = state->get<LockMap>(lockR)) {
203*67e74705SXin Li if (LState->isUnlocked()) {
204*67e74705SXin Li if (!BT_doubleunlock)
205*67e74705SXin Li BT_doubleunlock.reset(new BugType(this, "Double unlocking",
206*67e74705SXin Li "Lock checker"));
207*67e74705SXin Li ExplodedNode *N = C.generateErrorNode();
208*67e74705SXin Li if (!N)
209*67e74705SXin Li return;
210*67e74705SXin Li auto Report = llvm::make_unique<BugReport>(
211*67e74705SXin Li *BT_doubleunlock, "This lock has already been unlocked", N);
212*67e74705SXin Li Report->addRange(CE->getArg(0)->getSourceRange());
213*67e74705SXin Li C.emitReport(std::move(Report));
214*67e74705SXin Li return;
215*67e74705SXin Li } else if (LState->isDestroyed()) {
216*67e74705SXin Li reportUseDestroyedBug(C, CE);
217*67e74705SXin Li return;
218*67e74705SXin Li }
219*67e74705SXin Li }
220*67e74705SXin Li
221*67e74705SXin Li LockSetTy LS = state->get<LockSet>();
222*67e74705SXin Li
223*67e74705SXin Li // FIXME: Better analysis requires IPA for wrappers.
224*67e74705SXin Li
225*67e74705SXin Li if (!LS.isEmpty()) {
226*67e74705SXin Li const MemRegion *firstLockR = LS.getHead();
227*67e74705SXin Li if (firstLockR != lockR) {
228*67e74705SXin Li if (!BT_lor)
229*67e74705SXin Li BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker"));
230*67e74705SXin Li ExplodedNode *N = C.generateErrorNode();
231*67e74705SXin Li if (!N)
232*67e74705SXin Li return;
233*67e74705SXin Li auto report = llvm::make_unique<BugReport>(
234*67e74705SXin Li *BT_lor, "This was not the most recently acquired lock. Possible "
235*67e74705SXin Li "lock order reversal", N);
236*67e74705SXin Li report->addRange(CE->getArg(0)->getSourceRange());
237*67e74705SXin Li C.emitReport(std::move(report));
238*67e74705SXin Li return;
239*67e74705SXin Li }
240*67e74705SXin Li // Record that the lock was released.
241*67e74705SXin Li state = state->set<LockSet>(LS.getTail());
242*67e74705SXin Li }
243*67e74705SXin Li
244*67e74705SXin Li state = state->set<LockMap>(lockR, LockState::getUnlocked());
245*67e74705SXin Li C.addTransition(state);
246*67e74705SXin Li }
247*67e74705SXin Li
DestroyLock(CheckerContext & C,const CallExpr * CE,SVal Lock) const248*67e74705SXin Li void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE,
249*67e74705SXin Li SVal Lock) const {
250*67e74705SXin Li
251*67e74705SXin Li const MemRegion *LockR = Lock.getAsRegion();
252*67e74705SXin Li if (!LockR)
253*67e74705SXin Li return;
254*67e74705SXin Li
255*67e74705SXin Li ProgramStateRef State = C.getState();
256*67e74705SXin Li
257*67e74705SXin Li const LockState *LState = State->get<LockMap>(LockR);
258*67e74705SXin Li if (!LState || LState->isUnlocked()) {
259*67e74705SXin Li State = State->set<LockMap>(LockR, LockState::getDestroyed());
260*67e74705SXin Li C.addTransition(State);
261*67e74705SXin Li return;
262*67e74705SXin Li }
263*67e74705SXin Li
264*67e74705SXin Li StringRef Message;
265*67e74705SXin Li
266*67e74705SXin Li if (LState->isLocked()) {
267*67e74705SXin Li Message = "This lock is still locked";
268*67e74705SXin Li } else {
269*67e74705SXin Li Message = "This lock has already been destroyed";
270*67e74705SXin Li }
271*67e74705SXin Li
272*67e74705SXin Li if (!BT_destroylock)
273*67e74705SXin Li BT_destroylock.reset(new BugType(this, "Destroy invalid lock",
274*67e74705SXin Li "Lock checker"));
275*67e74705SXin Li ExplodedNode *N = C.generateErrorNode();
276*67e74705SXin Li if (!N)
277*67e74705SXin Li return;
278*67e74705SXin Li auto Report = llvm::make_unique<BugReport>(*BT_destroylock, Message, N);
279*67e74705SXin Li Report->addRange(CE->getArg(0)->getSourceRange());
280*67e74705SXin Li C.emitReport(std::move(Report));
281*67e74705SXin Li }
282*67e74705SXin Li
InitLock(CheckerContext & C,const CallExpr * CE,SVal Lock) const283*67e74705SXin Li void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE,
284*67e74705SXin Li SVal Lock) const {
285*67e74705SXin Li
286*67e74705SXin Li const MemRegion *LockR = Lock.getAsRegion();
287*67e74705SXin Li if (!LockR)
288*67e74705SXin Li return;
289*67e74705SXin Li
290*67e74705SXin Li ProgramStateRef State = C.getState();
291*67e74705SXin Li
292*67e74705SXin Li const struct LockState *LState = State->get<LockMap>(LockR);
293*67e74705SXin Li if (!LState || LState->isDestroyed()) {
294*67e74705SXin Li State = State->set<LockMap>(LockR, LockState::getUnlocked());
295*67e74705SXin Li C.addTransition(State);
296*67e74705SXin Li return;
297*67e74705SXin Li }
298*67e74705SXin Li
299*67e74705SXin Li StringRef Message;
300*67e74705SXin Li
301*67e74705SXin Li if (LState->isLocked()) {
302*67e74705SXin Li Message = "This lock is still being held";
303*67e74705SXin Li } else {
304*67e74705SXin Li Message = "This lock has already been initialized";
305*67e74705SXin Li }
306*67e74705SXin Li
307*67e74705SXin Li if (!BT_initlock)
308*67e74705SXin Li BT_initlock.reset(new BugType(this, "Init invalid lock",
309*67e74705SXin Li "Lock checker"));
310*67e74705SXin Li ExplodedNode *N = C.generateErrorNode();
311*67e74705SXin Li if (!N)
312*67e74705SXin Li return;
313*67e74705SXin Li auto Report = llvm::make_unique<BugReport>(*BT_initlock, Message, N);
314*67e74705SXin Li Report->addRange(CE->getArg(0)->getSourceRange());
315*67e74705SXin Li C.emitReport(std::move(Report));
316*67e74705SXin Li }
317*67e74705SXin Li
reportUseDestroyedBug(CheckerContext & C,const CallExpr * CE) const318*67e74705SXin Li void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C,
319*67e74705SXin Li const CallExpr *CE) const {
320*67e74705SXin Li if (!BT_destroylock)
321*67e74705SXin Li BT_destroylock.reset(new BugType(this, "Use destroyed lock",
322*67e74705SXin Li "Lock checker"));
323*67e74705SXin Li ExplodedNode *N = C.generateErrorNode();
324*67e74705SXin Li if (!N)
325*67e74705SXin Li return;
326*67e74705SXin Li auto Report = llvm::make_unique<BugReport>(
327*67e74705SXin Li *BT_destroylock, "This lock has already been destroyed", N);
328*67e74705SXin Li Report->addRange(CE->getArg(0)->getSourceRange());
329*67e74705SXin Li C.emitReport(std::move(Report));
330*67e74705SXin Li }
331*67e74705SXin Li
registerPthreadLockChecker(CheckerManager & mgr)332*67e74705SXin Li void ento::registerPthreadLockChecker(CheckerManager &mgr) {
333*67e74705SXin Li mgr.registerChecker<PthreadLockChecker>();
334*67e74705SXin Li }
335