1 //===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===//
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
7 //===----------------------------------------------------------------------===//
10 // * PthreadLockChecker, a simple lock -> unlock checker.
11 // Which also checks for XNU locks, which behave similarly enough to share
13 // * FuchsiaLocksChecker, which is also rather similar.
14 // * C11LockChecker which also closely follows Pthread semantics.
18 //===----------------------------------------------------------------------===//
20 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
21 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
22 #include "clang/StaticAnalyzer/Core/Checker.h"
23 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
24 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
25 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
26 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
28 using namespace clang
;
38 UntouchedAndPossiblyDestroyed
,
39 UnlockedAndPossiblyDestroyed
43 LockState(Kind K
) : K(K
) {}
46 static LockState
getLocked() { return LockState(Locked
); }
47 static LockState
getUnlocked() { return LockState(Unlocked
); }
48 static LockState
getDestroyed() { return LockState(Destroyed
); }
49 static LockState
getUntouchedAndPossiblyDestroyed() {
50 return LockState(UntouchedAndPossiblyDestroyed
);
52 static LockState
getUnlockedAndPossiblyDestroyed() {
53 return LockState(UnlockedAndPossiblyDestroyed
);
56 bool operator==(const LockState
&X
) const { return K
== X
.K
; }
58 bool isLocked() const { return K
== Locked
; }
59 bool isUnlocked() const { return K
== Unlocked
; }
60 bool isDestroyed() const { return K
== Destroyed
; }
61 bool isUntouchedAndPossiblyDestroyed() const {
62 return K
== UntouchedAndPossiblyDestroyed
;
64 bool isUnlockedAndPossiblyDestroyed() const {
65 return K
== UnlockedAndPossiblyDestroyed
;
68 void Profile(llvm::FoldingSetNodeID
&ID
) const { ID
.AddInteger(K
); }
71 class PthreadLockChecker
: public Checker
<check::PostCall
, check::DeadSymbols
,
72 check::RegionChanges
> {
74 enum LockingSemantics
{ NotApplicable
= 0, PthreadSemantics
, XNUSemantics
};
76 CK_PthreadLockChecker
,
77 CK_FuchsiaLockChecker
,
81 bool ChecksEnabled
[CK_NumCheckKinds
] = {false};
82 CheckerNameRef CheckNames
[CK_NumCheckKinds
];
85 typedef void (PthreadLockChecker::*FnCheck
)(const CallEvent
&Call
,
87 CheckerKind CheckKind
) const;
88 CallDescriptionMap
<FnCheck
> PThreadCallbacks
= {
90 {{CDM::CLibrary
, {"pthread_mutex_init"}, 2},
91 &PthreadLockChecker::InitAnyLock
},
92 // TODO: pthread_rwlock_init(2 arguments).
93 // TODO: lck_mtx_init(3 arguments).
94 // TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex.
95 // TODO: lck_rw_init(3 arguments).
96 // TODO: lck_rw_alloc_init(2 arguments) => returns the mutex.
99 {{CDM::CLibrary
, {"pthread_mutex_lock"}, 1},
100 &PthreadLockChecker::AcquirePthreadLock
},
101 {{CDM::CLibrary
, {"pthread_rwlock_rdlock"}, 1},
102 &PthreadLockChecker::AcquirePthreadLock
},
103 {{CDM::CLibrary
, {"pthread_rwlock_wrlock"}, 1},
104 &PthreadLockChecker::AcquirePthreadLock
},
105 {{CDM::CLibrary
, {"lck_mtx_lock"}, 1},
106 &PthreadLockChecker::AcquireXNULock
},
107 {{CDM::CLibrary
, {"lck_rw_lock_exclusive"}, 1},
108 &PthreadLockChecker::AcquireXNULock
},
109 {{CDM::CLibrary
, {"lck_rw_lock_shared"}, 1},
110 &PthreadLockChecker::AcquireXNULock
},
113 {{CDM::CLibrary
, {"pthread_mutex_trylock"}, 1},
114 &PthreadLockChecker::TryPthreadLock
},
115 {{CDM::CLibrary
, {"pthread_rwlock_tryrdlock"}, 1},
116 &PthreadLockChecker::TryPthreadLock
},
117 {{CDM::CLibrary
, {"pthread_rwlock_trywrlock"}, 1},
118 &PthreadLockChecker::TryPthreadLock
},
119 {{CDM::CLibrary
, {"lck_mtx_try_lock"}, 1},
120 &PthreadLockChecker::TryXNULock
},
121 {{CDM::CLibrary
, {"lck_rw_try_lock_exclusive"}, 1},
122 &PthreadLockChecker::TryXNULock
},
123 {{CDM::CLibrary
, {"lck_rw_try_lock_shared"}, 1},
124 &PthreadLockChecker::TryXNULock
},
127 {{CDM::CLibrary
, {"pthread_mutex_unlock"}, 1},
128 &PthreadLockChecker::ReleaseAnyLock
},
129 {{CDM::CLibrary
, {"pthread_rwlock_unlock"}, 1},
130 &PthreadLockChecker::ReleaseAnyLock
},
131 {{CDM::CLibrary
, {"lck_mtx_unlock"}, 1},
132 &PthreadLockChecker::ReleaseAnyLock
},
133 {{CDM::CLibrary
, {"lck_rw_unlock_exclusive"}, 1},
134 &PthreadLockChecker::ReleaseAnyLock
},
135 {{CDM::CLibrary
, {"lck_rw_unlock_shared"}, 1},
136 &PthreadLockChecker::ReleaseAnyLock
},
137 {{CDM::CLibrary
, {"lck_rw_done"}, 1},
138 &PthreadLockChecker::ReleaseAnyLock
},
141 {{CDM::CLibrary
, {"pthread_mutex_destroy"}, 1},
142 &PthreadLockChecker::DestroyPthreadLock
},
143 {{CDM::CLibrary
, {"lck_mtx_destroy"}, 2},
144 &PthreadLockChecker::DestroyXNULock
},
145 // TODO: pthread_rwlock_destroy(1 argument).
146 // TODO: lck_rw_destroy(2 arguments).
149 CallDescriptionMap
<FnCheck
> FuchsiaCallbacks
= {
151 {{CDM::CLibrary
, {"spin_lock_init"}, 1},
152 &PthreadLockChecker::InitAnyLock
},
155 {{CDM::CLibrary
, {"spin_lock"}, 1},
156 &PthreadLockChecker::AcquirePthreadLock
},
157 {{CDM::CLibrary
, {"spin_lock_save"}, 3},
158 &PthreadLockChecker::AcquirePthreadLock
},
159 {{CDM::CLibrary
, {"sync_mutex_lock"}, 1},
160 &PthreadLockChecker::AcquirePthreadLock
},
161 {{CDM::CLibrary
, {"sync_mutex_lock_with_waiter"}, 1},
162 &PthreadLockChecker::AcquirePthreadLock
},
165 {{CDM::CLibrary
, {"spin_trylock"}, 1},
166 &PthreadLockChecker::TryFuchsiaLock
},
167 {{CDM::CLibrary
, {"sync_mutex_trylock"}, 1},
168 &PthreadLockChecker::TryFuchsiaLock
},
169 {{CDM::CLibrary
, {"sync_mutex_timedlock"}, 2},
170 &PthreadLockChecker::TryFuchsiaLock
},
173 {{CDM::CLibrary
, {"spin_unlock"}, 1},
174 &PthreadLockChecker::ReleaseAnyLock
},
175 {{CDM::CLibrary
, {"spin_unlock_restore"}, 3},
176 &PthreadLockChecker::ReleaseAnyLock
},
177 {{CDM::CLibrary
, {"sync_mutex_unlock"}, 1},
178 &PthreadLockChecker::ReleaseAnyLock
},
181 CallDescriptionMap
<FnCheck
> C11Callbacks
= {
183 {{CDM::CLibrary
, {"mtx_init"}, 2}, &PthreadLockChecker::InitAnyLock
},
186 {{CDM::CLibrary
, {"mtx_lock"}, 1},
187 &PthreadLockChecker::AcquirePthreadLock
},
190 {{CDM::CLibrary
, {"mtx_trylock"}, 1}, &PthreadLockChecker::TryC11Lock
},
191 {{CDM::CLibrary
, {"mtx_timedlock"}, 2}, &PthreadLockChecker::TryC11Lock
},
194 {{CDM::CLibrary
, {"mtx_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock
},
197 {{CDM::CLibrary
, {"mtx_destroy"}, 1},
198 &PthreadLockChecker::DestroyPthreadLock
},
201 ProgramStateRef
resolvePossiblyDestroyedMutex(ProgramStateRef state
,
202 const MemRegion
*lockR
,
203 const SymbolRef
*sym
) const;
204 void reportBug(CheckerContext
&C
, std::unique_ptr
<BugType
> BT
[],
205 const Expr
*MtxExpr
, CheckerKind CheckKind
,
206 StringRef Desc
) const;
209 void InitAnyLock(const CallEvent
&Call
, CheckerContext
&C
,
210 CheckerKind CheckKind
) const;
211 void InitLockAux(const CallEvent
&Call
, CheckerContext
&C
,
212 const Expr
*MtxExpr
, SVal MtxVal
,
213 CheckerKind CheckKind
) const;
216 void AcquirePthreadLock(const CallEvent
&Call
, CheckerContext
&C
,
217 CheckerKind CheckKind
) const;
218 void AcquireXNULock(const CallEvent
&Call
, CheckerContext
&C
,
219 CheckerKind CheckKind
) const;
220 void TryPthreadLock(const CallEvent
&Call
, CheckerContext
&C
,
221 CheckerKind CheckKind
) const;
222 void TryXNULock(const CallEvent
&Call
, CheckerContext
&C
,
223 CheckerKind CheckKind
) const;
224 void TryFuchsiaLock(const CallEvent
&Call
, CheckerContext
&C
,
225 CheckerKind CheckKind
) const;
226 void TryC11Lock(const CallEvent
&Call
, CheckerContext
&C
,
227 CheckerKind CheckKind
) const;
228 void AcquireLockAux(const CallEvent
&Call
, CheckerContext
&C
,
229 const Expr
*MtxExpr
, SVal MtxVal
, bool IsTryLock
,
230 LockingSemantics Semantics
, CheckerKind CheckKind
) const;
233 void ReleaseAnyLock(const CallEvent
&Call
, CheckerContext
&C
,
234 CheckerKind CheckKind
) const;
235 void ReleaseLockAux(const CallEvent
&Call
, CheckerContext
&C
,
236 const Expr
*MtxExpr
, SVal MtxVal
,
237 CheckerKind CheckKind
) const;
240 void DestroyPthreadLock(const CallEvent
&Call
, CheckerContext
&C
,
241 CheckerKind CheckKind
) const;
242 void DestroyXNULock(const CallEvent
&Call
, CheckerContext
&C
,
243 CheckerKind CheckKind
) const;
244 void DestroyLockAux(const CallEvent
&Call
, CheckerContext
&C
,
245 const Expr
*MtxExpr
, SVal MtxVal
,
246 LockingSemantics Semantics
, CheckerKind CheckKind
) const;
249 void checkPostCall(const CallEvent
&Call
, CheckerContext
&C
) const;
250 void checkDeadSymbols(SymbolReaper
&SymReaper
, CheckerContext
&C
) const;
252 checkRegionChanges(ProgramStateRef State
, const InvalidatedSymbols
*Symbols
,
253 ArrayRef
<const MemRegion
*> ExplicitRegions
,
254 ArrayRef
<const MemRegion
*> Regions
,
255 const LocationContext
*LCtx
, const CallEvent
*Call
) const;
256 void printState(raw_ostream
&Out
, ProgramStateRef State
, const char *NL
,
257 const char *Sep
) const override
;
260 mutable std::unique_ptr
<BugType
> BT_doublelock
[CK_NumCheckKinds
];
261 mutable std::unique_ptr
<BugType
> BT_doubleunlock
[CK_NumCheckKinds
];
262 mutable std::unique_ptr
<BugType
> BT_destroylock
[CK_NumCheckKinds
];
263 mutable std::unique_ptr
<BugType
> BT_initlock
[CK_NumCheckKinds
];
264 mutable std::unique_ptr
<BugType
> BT_lor
[CK_NumCheckKinds
];
266 void initBugType(CheckerKind CheckKind
) const {
267 if (BT_doublelock
[CheckKind
])
269 BT_doublelock
[CheckKind
].reset(
270 new BugType
{CheckNames
[CheckKind
], "Double locking", "Lock checker"});
271 BT_doubleunlock
[CheckKind
].reset(
272 new BugType
{CheckNames
[CheckKind
], "Double unlocking", "Lock checker"});
273 BT_destroylock
[CheckKind
].reset(new BugType
{
274 CheckNames
[CheckKind
], "Use destroyed lock", "Lock checker"});
275 BT_initlock
[CheckKind
].reset(new BugType
{
276 CheckNames
[CheckKind
], "Init invalid lock", "Lock checker"});
277 BT_lor
[CheckKind
].reset(new BugType
{CheckNames
[CheckKind
],
278 "Lock order reversal", "Lock checker"});
281 } // end anonymous namespace
283 // A stack of locks for tracking lock-unlock order.
284 REGISTER_LIST_WITH_PROGRAMSTATE(LockSet
, const MemRegion
*)
286 // An entry for tracking lock states.
287 REGISTER_MAP_WITH_PROGRAMSTATE(LockMap
, const MemRegion
*, LockState
)
289 // Return values for unresolved calls to pthread_mutex_destroy().
290 REGISTER_MAP_WITH_PROGRAMSTATE(DestroyRetVal
, const MemRegion
*, SymbolRef
)
292 void PthreadLockChecker::checkPostCall(const CallEvent
&Call
,
293 CheckerContext
&C
) const {
294 // FIXME: Try to handle cases when the implementation was inlined rather
295 // than just giving up.
299 if (const FnCheck
*Callback
= PThreadCallbacks
.lookup(Call
))
300 (this->**Callback
)(Call
, C
, CK_PthreadLockChecker
);
301 else if (const FnCheck
*Callback
= FuchsiaCallbacks
.lookup(Call
))
302 (this->**Callback
)(Call
, C
, CK_FuchsiaLockChecker
);
303 else if (const FnCheck
*Callback
= C11Callbacks
.lookup(Call
))
304 (this->**Callback
)(Call
, C
, CK_C11LockChecker
);
307 // When a lock is destroyed, in some semantics(like PthreadSemantics) we are not
308 // sure if the destroy call has succeeded or failed, and the lock enters one of
309 // the 'possibly destroyed' state. There is a short time frame for the
310 // programmer to check the return value to see if the lock was successfully
311 // destroyed. Before we model the next operation over that lock, we call this
312 // function to see if the return value was checked by now and set the lock state
313 // - either to destroyed state or back to its previous state.
315 // In PthreadSemantics, pthread_mutex_destroy() returns zero if the lock is
316 // successfully destroyed and it returns a non-zero value otherwise.
317 ProgramStateRef
PthreadLockChecker::resolvePossiblyDestroyedMutex(
318 ProgramStateRef state
, const MemRegion
*lockR
, const SymbolRef
*sym
) const {
319 const LockState
*lstate
= state
->get
<LockMap
>(lockR
);
320 // Existence in DestroyRetVal ensures existence in LockMap.
321 // Existence in Destroyed also ensures that the lock state for lockR is either
322 // UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed.
324 assert(lstate
->isUntouchedAndPossiblyDestroyed() ||
325 lstate
->isUnlockedAndPossiblyDestroyed());
327 ConstraintManager
&CMgr
= state
->getConstraintManager();
328 ConditionTruthVal retZero
= CMgr
.isNull(state
, *sym
);
329 if (retZero
.isConstrainedFalse()) {
330 if (lstate
->isUntouchedAndPossiblyDestroyed())
331 state
= state
->remove
<LockMap
>(lockR
);
332 else if (lstate
->isUnlockedAndPossiblyDestroyed())
333 state
= state
->set
<LockMap
>(lockR
, LockState::getUnlocked());
335 state
= state
->set
<LockMap
>(lockR
, LockState::getDestroyed());
337 // Removing the map entry (lockR, sym) from DestroyRetVal as the lock state is
339 state
= state
->remove
<DestroyRetVal
>(lockR
);
343 void PthreadLockChecker::printState(raw_ostream
&Out
, ProgramStateRef State
,
344 const char *NL
, const char *Sep
) const {
345 LockMapTy LM
= State
->get
<LockMap
>();
347 Out
<< Sep
<< "Mutex states:" << NL
;
349 I
.first
->dumpToStream(Out
);
350 if (I
.second
.isLocked())
352 else if (I
.second
.isUnlocked())
354 else if (I
.second
.isDestroyed())
355 Out
<< ": destroyed";
356 else if (I
.second
.isUntouchedAndPossiblyDestroyed())
357 Out
<< ": not tracked, possibly destroyed";
358 else if (I
.second
.isUnlockedAndPossiblyDestroyed())
359 Out
<< ": unlocked, possibly destroyed";
364 LockSetTy LS
= State
->get
<LockSet
>();
366 Out
<< Sep
<< "Mutex lock order:" << NL
;
368 I
->dumpToStream(Out
);
373 DestroyRetValTy DRV
= State
->get
<DestroyRetVal
>();
374 if (!DRV
.isEmpty()) {
375 Out
<< Sep
<< "Mutexes in unresolved possibly destroyed state:" << NL
;
377 I
.first
->dumpToStream(Out
);
379 I
.second
->dumpToStream(Out
);
385 void PthreadLockChecker::AcquirePthreadLock(const CallEvent
&Call
,
387 CheckerKind CheckKind
) const {
388 AcquireLockAux(Call
, C
, Call
.getArgExpr(0), Call
.getArgSVal(0), false,
389 PthreadSemantics
, CheckKind
);
392 void PthreadLockChecker::AcquireXNULock(const CallEvent
&Call
,
394 CheckerKind CheckKind
) const {
395 AcquireLockAux(Call
, C
, Call
.getArgExpr(0), Call
.getArgSVal(0), false,
396 XNUSemantics
, CheckKind
);
399 void PthreadLockChecker::TryPthreadLock(const CallEvent
&Call
,
401 CheckerKind CheckKind
) const {
402 AcquireLockAux(Call
, C
, Call
.getArgExpr(0), Call
.getArgSVal(0), true,
403 PthreadSemantics
, CheckKind
);
406 void PthreadLockChecker::TryXNULock(const CallEvent
&Call
, CheckerContext
&C
,
407 CheckerKind CheckKind
) const {
408 AcquireLockAux(Call
, C
, Call
.getArgExpr(0), Call
.getArgSVal(0), true,
409 PthreadSemantics
, CheckKind
);
412 void PthreadLockChecker::TryFuchsiaLock(const CallEvent
&Call
,
414 CheckerKind CheckKind
) const {
415 AcquireLockAux(Call
, C
, Call
.getArgExpr(0), Call
.getArgSVal(0), true,
416 PthreadSemantics
, CheckKind
);
419 void PthreadLockChecker::TryC11Lock(const CallEvent
&Call
, CheckerContext
&C
,
420 CheckerKind CheckKind
) const {
421 AcquireLockAux(Call
, C
, Call
.getArgExpr(0), Call
.getArgSVal(0), true,
422 PthreadSemantics
, CheckKind
);
425 void PthreadLockChecker::AcquireLockAux(const CallEvent
&Call
,
426 CheckerContext
&C
, const Expr
*MtxExpr
,
427 SVal MtxVal
, bool IsTryLock
,
428 enum LockingSemantics Semantics
,
429 CheckerKind CheckKind
) const {
430 if (!ChecksEnabled
[CheckKind
])
433 const MemRegion
*lockR
= MtxVal
.getAsRegion();
437 ProgramStateRef state
= C
.getState();
438 const SymbolRef
*sym
= state
->get
<DestroyRetVal
>(lockR
);
440 state
= resolvePossiblyDestroyedMutex(state
, lockR
, sym
);
442 if (const LockState
*LState
= state
->get
<LockMap
>(lockR
)) {
443 if (LState
->isLocked()) {
444 reportBug(C
, BT_doublelock
, MtxExpr
, CheckKind
,
445 "This lock has already been acquired");
447 } else if (LState
->isDestroyed()) {
448 reportBug(C
, BT_destroylock
, MtxExpr
, CheckKind
,
449 "This lock has already been destroyed");
454 ProgramStateRef lockSucc
= state
;
456 // Bifurcate the state, and allow a mode where the lock acquisition fails.
457 SVal RetVal
= Call
.getReturnValue();
458 if (auto DefinedRetVal
= RetVal
.getAs
<DefinedSVal
>()) {
459 ProgramStateRef lockFail
;
461 case PthreadSemantics
:
462 std::tie(lockFail
, lockSucc
) = state
->assume(*DefinedRetVal
);
465 std::tie(lockSucc
, lockFail
) = state
->assume(*DefinedRetVal
);
468 llvm_unreachable("Unknown tryLock locking semantics");
470 assert(lockFail
&& lockSucc
);
471 C
.addTransition(lockFail
);
473 // We might want to handle the case when the mutex lock function was inlined
474 // and returned an Unknown or Undefined value.
475 } else if (Semantics
== PthreadSemantics
) {
476 // Assume that the return value was 0.
477 SVal RetVal
= Call
.getReturnValue();
478 if (auto DefinedRetVal
= RetVal
.getAs
<DefinedSVal
>()) {
479 // FIXME: If the lock function was inlined and returned true,
480 // we need to behave sanely - at least generate sink.
481 lockSucc
= state
->assume(*DefinedRetVal
, false);
484 // We might want to handle the case when the mutex lock function was inlined
485 // and returned an Unknown or Undefined value.
487 // XNU locking semantics return void on non-try locks
488 assert((Semantics
== XNUSemantics
) && "Unknown locking semantics");
492 // Record that the lock was acquired.
493 lockSucc
= lockSucc
->add
<LockSet
>(lockR
);
494 lockSucc
= lockSucc
->set
<LockMap
>(lockR
, LockState::getLocked());
495 C
.addTransition(lockSucc
);
498 void PthreadLockChecker::ReleaseAnyLock(const CallEvent
&Call
,
500 CheckerKind CheckKind
) const {
501 ReleaseLockAux(Call
, C
, Call
.getArgExpr(0), Call
.getArgSVal(0), CheckKind
);
504 void PthreadLockChecker::ReleaseLockAux(const CallEvent
&Call
,
505 CheckerContext
&C
, const Expr
*MtxExpr
,
507 CheckerKind CheckKind
) const {
508 if (!ChecksEnabled
[CheckKind
])
511 const MemRegion
*lockR
= MtxVal
.getAsRegion();
515 ProgramStateRef state
= C
.getState();
516 const SymbolRef
*sym
= state
->get
<DestroyRetVal
>(lockR
);
518 state
= resolvePossiblyDestroyedMutex(state
, lockR
, sym
);
520 if (const LockState
*LState
= state
->get
<LockMap
>(lockR
)) {
521 if (LState
->isUnlocked()) {
522 reportBug(C
, BT_doubleunlock
, MtxExpr
, CheckKind
,
523 "This lock has already been unlocked");
525 } else if (LState
->isDestroyed()) {
526 reportBug(C
, BT_destroylock
, MtxExpr
, CheckKind
,
527 "This lock has already been destroyed");
532 LockSetTy LS
= state
->get
<LockSet
>();
535 const MemRegion
*firstLockR
= LS
.getHead();
536 if (firstLockR
!= lockR
) {
537 reportBug(C
, BT_lor
, MtxExpr
, CheckKind
,
538 "This was not the most recently acquired lock. Possible lock "
542 // Record that the lock was released.
543 state
= state
->set
<LockSet
>(LS
.getTail());
546 state
= state
->set
<LockMap
>(lockR
, LockState::getUnlocked());
547 C
.addTransition(state
);
550 void PthreadLockChecker::DestroyPthreadLock(const CallEvent
&Call
,
552 CheckerKind CheckKind
) const {
553 DestroyLockAux(Call
, C
, Call
.getArgExpr(0), Call
.getArgSVal(0),
554 PthreadSemantics
, CheckKind
);
557 void PthreadLockChecker::DestroyXNULock(const CallEvent
&Call
,
559 CheckerKind CheckKind
) const {
560 DestroyLockAux(Call
, C
, Call
.getArgExpr(0), Call
.getArgSVal(0), XNUSemantics
,
564 void PthreadLockChecker::DestroyLockAux(const CallEvent
&Call
,
565 CheckerContext
&C
, const Expr
*MtxExpr
,
567 enum LockingSemantics Semantics
,
568 CheckerKind CheckKind
) const {
569 if (!ChecksEnabled
[CheckKind
])
572 const MemRegion
*LockR
= MtxVal
.getAsRegion();
576 ProgramStateRef State
= C
.getState();
578 const SymbolRef
*sym
= State
->get
<DestroyRetVal
>(LockR
);
580 State
= resolvePossiblyDestroyedMutex(State
, LockR
, sym
);
582 const LockState
*LState
= State
->get
<LockMap
>(LockR
);
583 // Checking the return value of the destroy method only in the case of
585 if (Semantics
== PthreadSemantics
) {
586 if (!LState
|| LState
->isUnlocked()) {
587 SymbolRef sym
= Call
.getReturnValue().getAsSymbol();
589 State
= State
->remove
<LockMap
>(LockR
);
590 C
.addTransition(State
);
593 State
= State
->set
<DestroyRetVal
>(LockR
, sym
);
594 if (LState
&& LState
->isUnlocked())
595 State
= State
->set
<LockMap
>(
596 LockR
, LockState::getUnlockedAndPossiblyDestroyed());
598 State
= State
->set
<LockMap
>(
599 LockR
, LockState::getUntouchedAndPossiblyDestroyed());
600 C
.addTransition(State
);
604 if (!LState
|| LState
->isUnlocked()) {
605 State
= State
->set
<LockMap
>(LockR
, LockState::getDestroyed());
606 C
.addTransition(State
);
611 StringRef Message
= LState
->isLocked()
612 ? "This lock is still locked"
613 : "This lock has already been destroyed";
615 reportBug(C
, BT_destroylock
, MtxExpr
, CheckKind
, Message
);
618 void PthreadLockChecker::InitAnyLock(const CallEvent
&Call
, CheckerContext
&C
,
619 CheckerKind CheckKind
) const {
620 InitLockAux(Call
, C
, Call
.getArgExpr(0), Call
.getArgSVal(0), CheckKind
);
623 void PthreadLockChecker::InitLockAux(const CallEvent
&Call
, CheckerContext
&C
,
624 const Expr
*MtxExpr
, SVal MtxVal
,
625 CheckerKind CheckKind
) const {
626 if (!ChecksEnabled
[CheckKind
])
629 const MemRegion
*LockR
= MtxVal
.getAsRegion();
633 ProgramStateRef State
= C
.getState();
635 const SymbolRef
*sym
= State
->get
<DestroyRetVal
>(LockR
);
637 State
= resolvePossiblyDestroyedMutex(State
, LockR
, sym
);
639 const struct LockState
*LState
= State
->get
<LockMap
>(LockR
);
640 if (!LState
|| LState
->isDestroyed()) {
641 State
= State
->set
<LockMap
>(LockR
, LockState::getUnlocked());
642 C
.addTransition(State
);
646 StringRef Message
= LState
->isLocked()
647 ? "This lock is still being held"
648 : "This lock has already been initialized";
650 reportBug(C
, BT_initlock
, MtxExpr
, CheckKind
, Message
);
653 void PthreadLockChecker::reportBug(CheckerContext
&C
,
654 std::unique_ptr
<BugType
> BT
[],
655 const Expr
*MtxExpr
, CheckerKind CheckKind
,
656 StringRef Desc
) const {
657 ExplodedNode
*N
= C
.generateErrorNode();
660 initBugType(CheckKind
);
662 std::make_unique
<PathSensitiveBugReport
>(*BT
[CheckKind
], Desc
, N
);
663 Report
->addRange(MtxExpr
->getSourceRange());
664 C
.emitReport(std::move(Report
));
667 void PthreadLockChecker::checkDeadSymbols(SymbolReaper
&SymReaper
,
668 CheckerContext
&C
) const {
669 ProgramStateRef State
= C
.getState();
671 for (auto I
: State
->get
<DestroyRetVal
>()) {
672 // Once the return value symbol dies, no more checks can be performed
673 // against it. See if the return value was checked before this point.
674 // This would remove the symbol from the map as well.
675 if (SymReaper
.isDead(I
.second
))
676 State
= resolvePossiblyDestroyedMutex(State
, I
.first
, &I
.second
);
679 for (auto I
: State
->get
<LockMap
>()) {
680 // Stop tracking dead mutex regions as well.
681 if (!SymReaper
.isLiveRegion(I
.first
)) {
682 State
= State
->remove
<LockMap
>(I
.first
);
683 State
= State
->remove
<DestroyRetVal
>(I
.first
);
687 // TODO: We probably need to clean up the lock stack as well.
688 // It is tricky though: even if the mutex cannot be unlocked anymore,
689 // it can still participate in lock order reversal resolution.
691 C
.addTransition(State
);
694 ProgramStateRef
PthreadLockChecker::checkRegionChanges(
695 ProgramStateRef State
, const InvalidatedSymbols
*Symbols
,
696 ArrayRef
<const MemRegion
*> ExplicitRegions
,
697 ArrayRef
<const MemRegion
*> Regions
, const LocationContext
*LCtx
,
698 const CallEvent
*Call
) const {
700 bool IsLibraryFunction
= false;
701 if (Call
&& Call
->isGlobalCFunction()) {
702 // Avoid invalidating mutex state when a known supported function is called.
703 if (PThreadCallbacks
.lookup(*Call
) || FuchsiaCallbacks
.lookup(*Call
) ||
704 C11Callbacks
.lookup(*Call
))
707 if (Call
->isInSystemHeader())
708 IsLibraryFunction
= true;
711 for (auto R
: Regions
) {
712 // We assume that system library function wouldn't touch the mutex unless
713 // it takes the mutex explicitly as an argument.
714 // FIXME: This is a bit quadratic.
715 if (IsLibraryFunction
&& !llvm::is_contained(ExplicitRegions
, R
))
718 State
= State
->remove
<LockMap
>(R
);
719 State
= State
->remove
<DestroyRetVal
>(R
);
721 // TODO: We need to invalidate the lock stack as well. This is tricky
722 // to implement correctly and efficiently though, because the effects
723 // of mutex escapes on lock order may be fairly varied.
729 void ento::registerPthreadLockBase(CheckerManager
&mgr
) {
730 mgr
.registerChecker
<PthreadLockChecker
>();
733 bool ento::shouldRegisterPthreadLockBase(const CheckerManager
&mgr
) { return true; }
735 #define REGISTER_CHECKER(name) \
736 void ento::register##name(CheckerManager &mgr) { \
737 PthreadLockChecker *checker = mgr.getChecker<PthreadLockChecker>(); \
738 checker->ChecksEnabled[PthreadLockChecker::CK_##name] = true; \
739 checker->CheckNames[PthreadLockChecker::CK_##name] = \
740 mgr.getCurrentCheckerName(); \
743 bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
745 REGISTER_CHECKER(PthreadLockChecker
)
746 REGISTER_CHECKER(FuchsiaLockChecker
)
747 REGISTER_CHECKER(C11LockChecker
)