clang 24.0.0git
PthreadLockChecker.cpp
Go to the documentation of this file.
1//===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines:
10// * PthreadLockChecker, a simple lock -> unlock checker.
11// Which also checks for XNU locks, which behave similarly enough to share
12// code.
13// * FuchsiaLocksChecker, which is also rather similar.
14// * C11LockChecker which also closely follows Pthread semantics.
15//
16//===----------------------------------------------------------------------===//
17
25#include "llvm/ADT/STLExtras.h"
26#include "llvm/ADT/StringRef.h"
27
28using namespace clang;
29using namespace ento;
30
31constexpr llvm::StringRef LOCK_CHECKER_CATEGORY = "Lock checker";
32
33static bool isLockRelevant(const MemRegion *R,
34 const PathSensitiveBugReport &BR) {
36 BR.isInteresting(R);
37}
38
40 StringRef MsgForNamed,
41 StringRef MsgForUnnamed) {
42 return C.getNoteTag(
43 [R, Named = MsgForNamed.str(), Unnamed = MsgForUnnamed.str()](
44 PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
45 if (!isLockRelevant(R, BR))
46 return;
47 std::string Name = R->getDescriptiveName();
48 if (Name.empty())
49 OS << Unnamed;
50 else
51 OS << Named << Name << " here";
52 });
53}
54
55namespace {
56
57struct LockState {
58 enum Kind {
59 Destroyed,
60 Locked,
61 Unlocked,
62 UntouchedAndPossiblyDestroyed,
63 UnlockedAndPossiblyDestroyed
64 } K;
65
66private:
67 LockState(Kind K) : K(K) {}
68
69public:
70 static LockState getLocked() { return LockState(Locked); }
71 static LockState getUnlocked() { return LockState(Unlocked); }
72 static LockState getDestroyed() { return LockState(Destroyed); }
73 static LockState getUntouchedAndPossiblyDestroyed() {
74 return LockState(UntouchedAndPossiblyDestroyed);
75 }
76 static LockState getUnlockedAndPossiblyDestroyed() {
77 return LockState(UnlockedAndPossiblyDestroyed);
78 }
79
80 bool operator==(const LockState &X) const { return K == X.K; }
81
82 bool isLocked() const { return K == Locked; }
83 bool isUnlocked() const { return K == Unlocked; }
84 bool isDestroyed() const { return K == Destroyed; }
85 bool isUntouchedAndPossiblyDestroyed() const {
86 return K == UntouchedAndPossiblyDestroyed;
87 }
88 bool isUnlockedAndPossiblyDestroyed() const {
89 return K == UnlockedAndPossiblyDestroyed;
90 }
91
92 void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
93};
94
95class PthreadLockChecker : public Checker<check::PostCall, check::DeadSymbols,
96 check::RegionChanges> {
97public:
98 enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics };
99 enum CheckerKind {
100 CK_PthreadLockChecker,
101 CK_FuchsiaLockChecker,
102 CK_C11LockChecker,
103 CK_NumCheckKinds
104 };
105 bool ChecksEnabled[CK_NumCheckKinds] = {false};
106 CheckerNameRef CheckNames[CK_NumCheckKinds];
107 bool WarnOnLockOrderReversal = false;
108
109private:
110 typedef void (PthreadLockChecker::*FnCheck)(const CallEvent &Call,
111 CheckerContext &C,
112 CheckerKind CheckKind) const;
113 CallDescriptionMap<FnCheck> PThreadCallbacks = {
114 // Init.
115 {{CDM::CLibrary, {"pthread_mutex_init"}, 2},
116 &PthreadLockChecker::InitAnyLock},
117 // TODO: pthread_rwlock_init(2 arguments).
118 // TODO: lck_mtx_init(3 arguments).
119 // TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex.
120 // TODO: lck_rw_init(3 arguments).
121 // TODO: lck_rw_alloc_init(2 arguments) => returns the mutex.
122
123 // Acquire.
124 {{CDM::CLibrary, {"pthread_mutex_lock"}, 1},
125 &PthreadLockChecker::AcquirePthreadLock},
126 {{CDM::CLibrary, {"pthread_rwlock_rdlock"}, 1},
127 &PthreadLockChecker::AcquirePthreadLock},
128 {{CDM::CLibrary, {"pthread_rwlock_wrlock"}, 1},
129 &PthreadLockChecker::AcquirePthreadLock},
130 {{CDM::CLibrary, {"lck_mtx_lock"}, 1},
131 &PthreadLockChecker::AcquireXNULock},
132 {{CDM::CLibrary, {"lck_rw_lock_exclusive"}, 1},
133 &PthreadLockChecker::AcquireXNULock},
134 {{CDM::CLibrary, {"lck_rw_lock_shared"}, 1},
135 &PthreadLockChecker::AcquireXNULock},
136
137 // Try.
138 {{CDM::CLibrary, {"pthread_mutex_trylock"}, 1},
139 &PthreadLockChecker::TryPthreadLock},
140 {{CDM::CLibrary, {"pthread_rwlock_tryrdlock"}, 1},
141 &PthreadLockChecker::TryPthreadLock},
142 {{CDM::CLibrary, {"pthread_rwlock_trywrlock"}, 1},
143 &PthreadLockChecker::TryPthreadLock},
144 {{CDM::CLibrary, {"lck_mtx_try_lock"}, 1},
145 &PthreadLockChecker::TryXNULock},
146 {{CDM::CLibrary, {"lck_rw_try_lock_exclusive"}, 1},
147 &PthreadLockChecker::TryXNULock},
148 {{CDM::CLibrary, {"lck_rw_try_lock_shared"}, 1},
149 &PthreadLockChecker::TryXNULock},
150
151 // Release.
152 {{CDM::CLibrary, {"pthread_mutex_unlock"}, 1},
153 &PthreadLockChecker::ReleaseAnyLock},
154 {{CDM::CLibrary, {"pthread_rwlock_unlock"}, 1},
155 &PthreadLockChecker::ReleaseAnyLock},
156 {{CDM::CLibrary, {"lck_mtx_unlock"}, 1},
157 &PthreadLockChecker::ReleaseAnyLock},
158 {{CDM::CLibrary, {"lck_rw_unlock_exclusive"}, 1},
159 &PthreadLockChecker::ReleaseAnyLock},
160 {{CDM::CLibrary, {"lck_rw_unlock_shared"}, 1},
161 &PthreadLockChecker::ReleaseAnyLock},
162 {{CDM::CLibrary, {"lck_rw_done"}, 1},
163 &PthreadLockChecker::ReleaseAnyLock},
164
165 // Destroy.
166 {{CDM::CLibrary, {"pthread_mutex_destroy"}, 1},
167 &PthreadLockChecker::DestroyPthreadLock},
168 {{CDM::CLibrary, {"lck_mtx_destroy"}, 2},
169 &PthreadLockChecker::DestroyXNULock},
170 // TODO: pthread_rwlock_destroy(1 argument).
171 // TODO: lck_rw_destroy(2 arguments).
172 };
173
174 CallDescriptionMap<FnCheck> FuchsiaCallbacks = {
175 // Init.
176 {{CDM::CLibrary, {"spin_lock_init"}, 1},
177 &PthreadLockChecker::InitAnyLock},
178
179 // Acquire.
180 {{CDM::CLibrary, {"spin_lock"}, 1},
181 &PthreadLockChecker::AcquirePthreadLock},
182 {{CDM::CLibrary, {"spin_lock_save"}, 3},
183 &PthreadLockChecker::AcquirePthreadLock},
184 {{CDM::CLibrary, {"sync_mutex_lock"}, 1},
185 &PthreadLockChecker::AcquirePthreadLock},
186 {{CDM::CLibrary, {"sync_mutex_lock_with_waiter"}, 1},
187 &PthreadLockChecker::AcquirePthreadLock},
188
189 // Try.
190 {{CDM::CLibrary, {"spin_trylock"}, 1},
191 &PthreadLockChecker::TryFuchsiaLock},
192 {{CDM::CLibrary, {"sync_mutex_trylock"}, 1},
193 &PthreadLockChecker::TryFuchsiaLock},
194 {{CDM::CLibrary, {"sync_mutex_timedlock"}, 2},
195 &PthreadLockChecker::TryFuchsiaLock},
196
197 // Release.
198 {{CDM::CLibrary, {"spin_unlock"}, 1},
199 &PthreadLockChecker::ReleaseAnyLock},
200 {{CDM::CLibrary, {"spin_unlock_restore"}, 3},
201 &PthreadLockChecker::ReleaseAnyLock},
202 {{CDM::CLibrary, {"sync_mutex_unlock"}, 1},
203 &PthreadLockChecker::ReleaseAnyLock},
204 };
205
206 CallDescriptionMap<FnCheck> C11Callbacks = {
207 // Init.
208 {{CDM::CLibrary, {"mtx_init"}, 2}, &PthreadLockChecker::InitAnyLock},
209
210 // Acquire.
211 {{CDM::CLibrary, {"mtx_lock"}, 1},
212 &PthreadLockChecker::AcquirePthreadLock},
213
214 // Try.
215 {{CDM::CLibrary, {"mtx_trylock"}, 1}, &PthreadLockChecker::TryC11Lock},
216 {{CDM::CLibrary, {"mtx_timedlock"}, 2}, &PthreadLockChecker::TryC11Lock},
217
218 // Release.
219 {{CDM::CLibrary, {"mtx_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
220
221 // Destroy
222 {{CDM::CLibrary, {"mtx_destroy"}, 1},
223 &PthreadLockChecker::DestroyPthreadLock},
224 };
225
226 ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state,
227 const MemRegion *lockR,
228 const SymbolRef *sym) const;
229 void reportBug(CheckerContext &C, std::unique_ptr<BugType> BT[],
230 const Expr *MtxExpr, const MemRegion *MtxRegion,
231 CheckerKind CheckKind, StringRef Desc,
232 const MemRegion *ExtraInteresting = nullptr) const;
233
234 // Init.
235 void InitAnyLock(const CallEvent &Call, CheckerContext &C,
236 CheckerKind CheckKind) const;
237 void InitLockAux(const CallEvent &Call, CheckerContext &C,
238 const Expr *MtxExpr, SVal MtxVal,
239 CheckerKind CheckKind) const;
240
241 // Lock, Try-lock.
242 void AcquirePthreadLock(const CallEvent &Call, CheckerContext &C,
243 CheckerKind CheckKind) const;
244 void AcquireXNULock(const CallEvent &Call, CheckerContext &C,
245 CheckerKind CheckKind) const;
246 void TryPthreadLock(const CallEvent &Call, CheckerContext &C,
247 CheckerKind CheckKind) const;
248 void TryXNULock(const CallEvent &Call, CheckerContext &C,
249 CheckerKind CheckKind) const;
250 void TryFuchsiaLock(const CallEvent &Call, CheckerContext &C,
251 CheckerKind CheckKind) const;
252 void TryC11Lock(const CallEvent &Call, CheckerContext &C,
253 CheckerKind CheckKind) const;
254 void AcquireLockAux(const CallEvent &Call, CheckerContext &C,
255 const Expr *MtxExpr, SVal MtxVal, bool IsTryLock,
256 LockingSemantics Semantics, CheckerKind CheckKind) const;
257
258 // Release.
259 void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C,
260 CheckerKind CheckKind) const;
261 void ReleaseLockAux(const CallEvent &Call, CheckerContext &C,
262 const Expr *MtxExpr, SVal MtxVal,
263 CheckerKind CheckKind) const;
264
265 // Destroy.
266 void DestroyPthreadLock(const CallEvent &Call, CheckerContext &C,
267 CheckerKind CheckKind) const;
268 void DestroyXNULock(const CallEvent &Call, CheckerContext &C,
269 CheckerKind CheckKind) const;
270 void DestroyLockAux(const CallEvent &Call, CheckerContext &C,
271 const Expr *MtxExpr, SVal MtxVal,
272 LockingSemantics Semantics, CheckerKind CheckKind) const;
273
274public:
275 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
276 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
278 checkRegionChanges(ProgramStateRef State, const InvalidatedSymbols *Symbols,
279 ArrayRef<const MemRegion *> ExplicitRegions,
280 ArrayRef<const MemRegion *> Regions, const StackFrame *SF,
281 const CallEvent *Call) const;
282 void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
283 const char *Sep) const override;
284
285private:
286 mutable std::unique_ptr<BugType> BT_doublelock[CK_NumCheckKinds];
287 mutable std::unique_ptr<BugType> BT_doubleunlock[CK_NumCheckKinds];
288 mutable std::unique_ptr<BugType> BT_destroylock[CK_NumCheckKinds];
289 mutable std::unique_ptr<BugType> BT_initlock[CK_NumCheckKinds];
290 mutable std::unique_ptr<BugType> BT_lor[CK_NumCheckKinds];
291
292 void initBugType(CheckerKind CheckKind) const {
293 if (BT_doublelock[CheckKind])
294 return;
295 BT_doublelock[CheckKind].reset(new BugType{
296 CheckNames[CheckKind], "Double locking", LOCK_CHECKER_CATEGORY});
297 BT_doubleunlock[CheckKind].reset(new BugType{
298 CheckNames[CheckKind], "Double unlocking", LOCK_CHECKER_CATEGORY});
299 BT_destroylock[CheckKind].reset(new BugType{
300 CheckNames[CheckKind], "Use destroyed lock", LOCK_CHECKER_CATEGORY});
301 BT_initlock[CheckKind].reset(new BugType{
302 CheckNames[CheckKind], "Init invalid lock", LOCK_CHECKER_CATEGORY});
303 BT_lor[CheckKind].reset(new BugType{
304 CheckNames[CheckKind], "Lock order reversal", LOCK_CHECKER_CATEGORY});
305 }
306};
307} // end anonymous namespace
308
309// A stack of locks for tracking lock-unlock order.
311
312// An entry for tracking lock states.
313REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState)
314
315// Return values for unresolved calls to pthread_mutex_destroy().
317
318void PthreadLockChecker::checkPostCall(const CallEvent &Call,
319 CheckerContext &C) const {
320 // FIXME: Try to handle cases when the implementation was inlined rather
321 // than just giving up.
322 if (C.wasInlined)
323 return;
324
325 if (const FnCheck *Callback = PThreadCallbacks.lookup(Call))
326 (this->**Callback)(Call, C, CK_PthreadLockChecker);
327 else if (const FnCheck *Callback = FuchsiaCallbacks.lookup(Call))
328 (this->**Callback)(Call, C, CK_FuchsiaLockChecker);
329 else if (const FnCheck *Callback = C11Callbacks.lookup(Call))
330 (this->**Callback)(Call, C, CK_C11LockChecker);
331}
332
333// When a lock is destroyed, in some semantics(like PthreadSemantics) we are not
334// sure if the destroy call has succeeded or failed, and the lock enters one of
335// the 'possibly destroyed' state. There is a short time frame for the
336// programmer to check the return value to see if the lock was successfully
337// destroyed. Before we model the next operation over that lock, we call this
338// function to see if the return value was checked by now and set the lock state
339// - either to destroyed state or back to its previous state.
340
341// In PthreadSemantics, pthread_mutex_destroy() returns zero if the lock is
342// successfully destroyed and it returns a non-zero value otherwise.
343ProgramStateRef PthreadLockChecker::resolvePossiblyDestroyedMutex(
344 ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const {
345 const LockState *lstate = state->get<LockMap>(lockR);
346 // Existence in DestroyRetVal ensures existence in LockMap.
347 // Existence in Destroyed also ensures that the lock state for lockR is either
348 // UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed.
349 assert(lstate);
350 assert(lstate->isUntouchedAndPossiblyDestroyed() ||
351 lstate->isUnlockedAndPossiblyDestroyed());
352
353 ConstraintManager &CMgr = state->getConstraintManager();
354 ConditionTruthVal retZero = CMgr.isNull(state, *sym);
355 if (retZero.isConstrainedFalse()) {
356 if (lstate->isUntouchedAndPossiblyDestroyed())
357 state = state->remove<LockMap>(lockR);
358 else if (lstate->isUnlockedAndPossiblyDestroyed())
359 state = state->set<LockMap>(lockR, LockState::getUnlocked());
360 } else
361 state = state->set<LockMap>(lockR, LockState::getDestroyed());
362
363 // Removing the map entry (lockR, sym) from DestroyRetVal as the lock state is
364 // now resolved.
365 state = state->remove<DestroyRetVal>(lockR);
366 return state;
367}
368
369void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State,
370 const char *NL, const char *Sep) const {
371 LockMapTy LM = State->get<LockMap>();
372 if (!LM.isEmpty()) {
373 Out << Sep << "Mutex states:" << NL;
374 for (auto I : LM) {
375 I.first->dumpToStream(Out);
376 if (I.second.isLocked())
377 Out << ": locked";
378 else if (I.second.isUnlocked())
379 Out << ": unlocked";
380 else if (I.second.isDestroyed())
381 Out << ": destroyed";
382 else if (I.second.isUntouchedAndPossiblyDestroyed())
383 Out << ": not tracked, possibly destroyed";
384 else if (I.second.isUnlockedAndPossiblyDestroyed())
385 Out << ": unlocked, possibly destroyed";
386 Out << NL;
387 }
388 }
389
390 LockSetTy LS = State->get<LockSet>();
391 if (!LS.isEmpty()) {
392 Out << Sep << "Mutex lock order:" << NL;
393 for (auto I : LS) {
394 I->dumpToStream(Out);
395 Out << NL;
396 }
397 }
398
399 DestroyRetValTy DRV = State->get<DestroyRetVal>();
400 if (!DRV.isEmpty()) {
401 Out << Sep << "Mutexes in unresolved possibly destroyed state:" << NL;
402 for (auto I : DRV) {
403 I.first->dumpToStream(Out);
404 Out << ": ";
405 I.second->dumpToStream(Out);
406 Out << NL;
407 }
408 }
409}
410
411void PthreadLockChecker::AcquirePthreadLock(const CallEvent &Call,
412 CheckerContext &C,
413 CheckerKind CheckKind) const {
414 AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false,
415 PthreadSemantics, CheckKind);
416}
417
418void PthreadLockChecker::AcquireXNULock(const CallEvent &Call,
419 CheckerContext &C,
420 CheckerKind CheckKind) const {
421 AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false,
422 XNUSemantics, CheckKind);
423}
424
425void PthreadLockChecker::TryPthreadLock(const CallEvent &Call,
426 CheckerContext &C,
427 CheckerKind CheckKind) const {
428 AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
429 PthreadSemantics, CheckKind);
430}
431
432void PthreadLockChecker::TryXNULock(const CallEvent &Call, CheckerContext &C,
433 CheckerKind CheckKind) const {
434 AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
435 PthreadSemantics, CheckKind);
436}
437
438void PthreadLockChecker::TryFuchsiaLock(const CallEvent &Call,
439 CheckerContext &C,
440 CheckerKind CheckKind) const {
441 AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
442 PthreadSemantics, CheckKind);
443}
444
445void PthreadLockChecker::TryC11Lock(const CallEvent &Call, CheckerContext &C,
446 CheckerKind CheckKind) const {
447 AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
448 PthreadSemantics, CheckKind);
449}
450
451void PthreadLockChecker::AcquireLockAux(const CallEvent &Call,
452 CheckerContext &C, const Expr *MtxExpr,
453 SVal MtxVal, bool IsTryLock,
454 enum LockingSemantics Semantics,
455 CheckerKind CheckKind) const {
456 if (!ChecksEnabled[CheckKind])
457 return;
458
459 const MemRegion *lockR = MtxVal.getAsRegion();
460 if (!lockR)
461 return;
462
463 ProgramStateRef state = C.getState();
464 const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
465 if (sym)
466 state = resolvePossiblyDestroyedMutex(state, lockR, sym);
467
468 if (const LockState *LState = state->get<LockMap>(lockR)) {
469 if (LState->isLocked()) {
470 reportBug(C, BT_doublelock, MtxExpr, lockR, CheckKind,
471 "This lock has already been acquired");
472 return;
473 } else if (LState->isDestroyed()) {
474 reportBug(C, BT_destroylock, MtxExpr, lockR, CheckKind,
475 "This lock has already been destroyed");
476 return;
477 }
478 }
479
480 ProgramStateRef lockSucc = state;
481 if (IsTryLock) {
482 // Bifurcate the state, and allow a mode where the lock acquisition fails.
483 SVal RetVal = Call.getReturnValue();
484 if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
485 ProgramStateRef lockFail;
486 switch (Semantics) {
487 case PthreadSemantics:
488 std::tie(lockFail, lockSucc) = state->assume(*DefinedRetVal);
489 break;
490 case XNUSemantics:
491 std::tie(lockSucc, lockFail) = state->assume(*DefinedRetVal);
492 break;
493 default:
494 llvm_unreachable("Unknown tryLock locking semantics");
495 }
496 assert(lockFail && lockSucc);
497 C.addTransition(lockFail);
498 }
499 // We might want to handle the case when the mutex lock function was inlined
500 // and returned an Unknown or Undefined value.
501 } else if (Semantics == PthreadSemantics) {
502 // Assume that the return value was 0.
503 SVal RetVal = Call.getReturnValue();
504 if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
505 // FIXME: If the lock function was inlined and returned true,
506 // we need to behave sanely - at least generate sink.
507 lockSucc = state->assume(*DefinedRetVal, false);
508 assert(lockSucc);
509 }
510 // We might want to handle the case when the mutex lock function was inlined
511 // and returned an Unknown or Undefined value.
512 } else {
513 // XNU locking semantics return void on non-try locks
514 assert((Semantics == XNUSemantics) && "Unknown locking semantics");
515 lockSucc = state;
516 }
517
518 // Record that the lock was acquired.
519 lockSucc = lockSucc->add<LockSet>(lockR);
520 lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked());
521 C.addTransition(lockSucc,
522 createMutexNote(C, lockR, "Locking ", "Mutex acquired here"));
523}
524
525void PthreadLockChecker::ReleaseAnyLock(const CallEvent &Call,
526 CheckerContext &C,
527 CheckerKind CheckKind) const {
528 ReleaseLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), CheckKind);
529}
530
531void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call,
532 CheckerContext &C, const Expr *MtxExpr,
533 SVal MtxVal,
534 CheckerKind CheckKind) const {
535 if (!ChecksEnabled[CheckKind])
536 return;
537
538 const MemRegion *lockR = MtxVal.getAsRegion();
539 if (!lockR)
540 return;
541
542 ProgramStateRef state = C.getState();
543 const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
544 if (sym)
545 state = resolvePossiblyDestroyedMutex(state, lockR, sym);
546
547 if (const LockState *LState = state->get<LockMap>(lockR)) {
548 if (LState->isUnlocked()) {
549 reportBug(C, BT_doubleunlock, MtxExpr, lockR, CheckKind,
550 "This lock has already been unlocked");
551 return;
552 } else if (LState->isDestroyed()) {
553 reportBug(C, BT_destroylock, MtxExpr, lockR, CheckKind,
554 "This lock has already been destroyed");
555 return;
556 }
557 }
558
559 LockSetTy LS = state->get<LockSet>();
560
561 if (!LS.isEmpty()) {
562 if (WarnOnLockOrderReversal && LS.getHead() != lockR) {
563 reportBug(C, BT_lor, MtxExpr, lockR, CheckKind,
564 "This was not the most recently acquired lock. Possible lock "
565 "order reversal",
566 LS.getHead());
567 return;
568 }
569
570 auto &Factory = state->get_context<LockSet>();
571 llvm::ImmutableList<const MemRegion *> NewLS = Factory.getEmptyList();
572 for (const MemRegion *LockReg :
573 llvm::make_filter_range(LS, llvm::not_equal_to(lockR))) {
574 NewLS = Factory.add(LockReg, NewLS);
575 }
576 state = state->set<LockSet>(NewLS);
577 }
578
579 state = state->set<LockMap>(lockR, LockState::getUnlocked());
580 C.addTransition(
581 state, createMutexNote(C, lockR, "Unlocking ", "Mutex released here"));
582}
583
584void PthreadLockChecker::DestroyPthreadLock(const CallEvent &Call,
585 CheckerContext &C,
586 CheckerKind CheckKind) const {
587 DestroyLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0),
588 PthreadSemantics, CheckKind);
589}
590
591void PthreadLockChecker::DestroyXNULock(const CallEvent &Call,
592 CheckerContext &C,
593 CheckerKind CheckKind) const {
594 DestroyLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), XNUSemantics,
595 CheckKind);
596}
597
598void PthreadLockChecker::DestroyLockAux(const CallEvent &Call,
599 CheckerContext &C, const Expr *MtxExpr,
600 SVal MtxVal,
601 enum LockingSemantics Semantics,
602 CheckerKind CheckKind) const {
603 if (!ChecksEnabled[CheckKind])
604 return;
605
606 const MemRegion *LockR = MtxVal.getAsRegion();
607 if (!LockR)
608 return;
609
610 ProgramStateRef State = C.getState();
611
612 const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
613 if (sym)
614 State = resolvePossiblyDestroyedMutex(State, LockR, sym);
615
616 const LockState *LState = State->get<LockMap>(LockR);
617 // Checking the return value of the destroy method only in the case of
618 // PthreadSemantics
619 if (Semantics == PthreadSemantics) {
620 if (!LState || LState->isUnlocked()) {
621 SymbolRef sym = Call.getReturnValue().getAsSymbol();
622 if (!sym) {
623 State = State->remove<LockMap>(LockR);
624 C.addTransition(State, createMutexNote(C, LockR, "Destroying ",
625 "Mutex destroyed here"));
626 return;
627 }
628 State = State->set<DestroyRetVal>(LockR, sym);
629 if (LState && LState->isUnlocked())
630 State = State->set<LockMap>(
631 LockR, LockState::getUnlockedAndPossiblyDestroyed());
632 else
633 State = State->set<LockMap>(
634 LockR, LockState::getUntouchedAndPossiblyDestroyed());
635 C.addTransition(State, createMutexNote(C, LockR, "Destroying ",
636 "Mutex destroyed here"));
637 return;
638 }
639 } else {
640 if (!LState || LState->isUnlocked()) {
641 State = State->set<LockMap>(LockR, LockState::getDestroyed());
642 C.addTransition(State, createMutexNote(C, LockR, "Destroying ",
643 "Mutex destroyed here"));
644 return;
645 }
646 }
647
648 StringRef Message = LState->isLocked()
649 ? "This lock is still locked"
650 : "This lock has already been destroyed";
651
652 reportBug(C, BT_destroylock, MtxExpr, LockR, CheckKind, Message);
653}
654
655void PthreadLockChecker::InitAnyLock(const CallEvent &Call, CheckerContext &C,
656 CheckerKind CheckKind) const {
657 InitLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), CheckKind);
658}
659
660void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C,
661 const Expr *MtxExpr, SVal MtxVal,
662 CheckerKind CheckKind) const {
663 if (!ChecksEnabled[CheckKind])
664 return;
665
666 const MemRegion *LockR = MtxVal.getAsRegion();
667 if (!LockR)
668 return;
669
670 ProgramStateRef State = C.getState();
671
672 const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
673 if (sym)
674 State = resolvePossiblyDestroyedMutex(State, LockR, sym);
675
676 const struct LockState *LState = State->get<LockMap>(LockR);
677 if (!LState || LState->isDestroyed()) {
678 State = State->set<LockMap>(LockR, LockState::getUnlocked());
679 C.addTransition(State, createMutexNote(C, LockR, "Initializing ",
680 "Mutex initialized here"));
681 return;
682 }
683
684 StringRef Message = LState->isLocked()
685 ? "This lock is still being held"
686 : "This lock has already been initialized";
687
688 reportBug(C, BT_initlock, MtxExpr, LockR, CheckKind, Message);
689}
690
691void PthreadLockChecker::reportBug(CheckerContext &C,
692 std::unique_ptr<BugType> BT[],
693 const Expr *MtxExpr,
694 const MemRegion *MtxRegion,
695 CheckerKind CheckKind, StringRef Desc,
696 const MemRegion *ExtraInteresting) const {
697 ExplodedNode *N = C.generateErrorNode();
698 if (!N)
699 return;
700 initBugType(CheckKind);
701 auto Report =
702 std::make_unique<PathSensitiveBugReport>(*BT[CheckKind], Desc, N);
703 Report->addRange(MtxExpr->getSourceRange());
704 if (MtxRegion)
705 Report->markInteresting(MtxRegion);
706 if (ExtraInteresting)
707 Report->markInteresting(ExtraInteresting);
708 C.emitReport(std::move(Report));
709}
710
711void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper,
712 CheckerContext &C) const {
713 ProgramStateRef State = C.getState();
714
715 for (auto I : State->get<DestroyRetVal>()) {
716 // Once the return value symbol dies, no more checks can be performed
717 // against it. See if the return value was checked before this point.
718 // This would remove the symbol from the map as well.
719 if (SymReaper.isDead(I.second))
720 State = resolvePossiblyDestroyedMutex(State, I.first, &I.second);
721 }
722
723 for (auto I : State->get<LockMap>()) {
724 // Stop tracking dead mutex regions as well.
725 if (!SymReaper.isLiveRegion(I.first)) {
726 State = State->remove<LockMap>(I.first);
727 State = State->remove<DestroyRetVal>(I.first);
728 }
729 }
730
731 // TODO: We probably need to clean up the lock stack as well.
732 // It is tricky though: even if the mutex cannot be unlocked anymore,
733 // it can still participate in lock order reversal resolution.
734
735 C.addTransition(State);
736}
737
738ProgramStateRef PthreadLockChecker::checkRegionChanges(
739 ProgramStateRef State, const InvalidatedSymbols *Symbols,
740 ArrayRef<const MemRegion *> ExplicitRegions,
741 ArrayRef<const MemRegion *> Regions, const StackFrame *SF,
742 const CallEvent *Call) const {
743
744 bool IsLibraryFunction = false;
745 if (Call && Call->isGlobalCFunction()) {
746 // Avoid invalidating mutex state when a known supported function is called.
747 if (PThreadCallbacks.lookup(*Call) || FuchsiaCallbacks.lookup(*Call) ||
748 C11Callbacks.lookup(*Call))
749 return State;
750
751 if (Call->isInSystemHeader())
752 IsLibraryFunction = true;
753 }
754
755 for (auto R : Regions) {
756 // We assume that system library function wouldn't touch the mutex unless
757 // it takes the mutex explicitly as an argument.
758 // FIXME: This is a bit quadratic.
759 if (IsLibraryFunction && !llvm::is_contained(ExplicitRegions, R))
760 continue;
761
762 State = State->remove<LockMap>(R);
763 State = State->remove<DestroyRetVal>(R);
764
765 // TODO: We need to invalidate the lock stack as well. This is tricky
766 // to implement correctly and efficiently though, because the effects
767 // of mutex escapes on lock order may be fairly varied.
768 }
769
770 return State;
771}
772
773void ento::registerPthreadLockBase(CheckerManager &mgr) {
774 mgr.registerChecker<PthreadLockChecker>();
775}
776
777bool ento::shouldRegisterPthreadLockBase(const CheckerManager &mgr) { return true; }
778
779#define REGISTER_CHECKER(name) \
780 void ento::register##name(CheckerManager &mgr) { \
781 PthreadLockChecker *checker = mgr.getChecker<PthreadLockChecker>(); \
782 checker->ChecksEnabled[PthreadLockChecker::CK_##name] = true; \
783 checker->CheckNames[PthreadLockChecker::CK_##name] = \
784 mgr.getCurrentCheckerName(); \
785 } \
786 \
787 bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
788
789REGISTER_CHECKER(FuchsiaLockChecker)
790REGISTER_CHECKER(C11LockChecker)
791
792#undef REGISTER_CHECKER
793
794void ento::registerPthreadLockChecker(CheckerManager &Mgr) {
795 PthreadLockChecker *Checker = Mgr.getChecker<PthreadLockChecker>();
796 Checker->ChecksEnabled[PthreadLockChecker::CK_PthreadLockChecker] = true;
797 Checker->CheckNames[PthreadLockChecker::CK_PthreadLockChecker] =
799 Checker->WarnOnLockOrderReversal =
800 Mgr.getAnalyzerOptions().getCheckerBooleanOption(
801 Mgr.getCurrentCheckerName(), "WarnOnLockOrderReversal");
802}
803
804bool ento::shouldRegisterPthreadLockChecker(const CheckerManager &) {
805 return true;
806}
#define REGISTER_CHECKER(name)
#define X(type, name)
Definition Value.h:97
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
#define REGISTER_LIST_WITH_PROGRAMSTATE(Name, Elem)
Declares an immutable list type NameTy, suitable for placement into the ProgramState.
static bool isLockRelevant(const MemRegion *R, const PathSensitiveBugReport &BR)
static const NoteTag * createMutexNote(CheckerContext &C, const MemRegion *R, StringRef MsgForNamed, StringRef MsgForUnnamed)
constexpr llvm::StringRef LOCK_CHECKER_CATEGORY
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp:343
const BugType & getBugType() const
StringRef getCategory() const
Definition BugType.h:59
const T * lookup(const CallEvent &Call) const
Represents an abstract call to a function or method along a particular path.
Definition CallEvent.h:152
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
CheckerNameRef getCurrentCheckerName() const
CHECKER * getChecker(AT &&...Args)
If the the singleton instance of a checker class is not yet constructed, then construct it (with the ...
Simple checker classes that implement one frontend (i.e.
Definition Checker.h:565
bool isConstrainedFalse() const
Return true if the constraint is perfectly constrained to 'false'.
ConditionTruthVal isNull(ProgramStateRef State, SymbolRef Sym)
Convenience method to query the state to see if a symbol is null or not null, or if neither assumptio...
MemRegion - The root abstract class for all memory regions.
Definition MemRegion.h:97
The tag upon which the TagVisitor reacts.
bool isInteresting(SymbolRef sym) const
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
Definition SVals.h:88
const MemRegion * getAsRegion() const
Definition SVals.cpp:119
bool isDead(SymbolRef sym)
Returns whether or not a symbol has been confirmed dead.
bool isLiveRegion(const MemRegion *region)
llvm::DenseSet< SymbolRef > InvalidatedSymbols
Definition Store.h:50
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const SymExpr * SymbolRef
Definition SymExpr.h:133
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
The JSON file list parser is used to communicate input to InstallAPI.
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)
Definition CallGraph.h:218