10b57cec5SDimitry Andric //===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
95ffd83dbSDimitry Andric // This file defines:
105ffd83dbSDimitry Andric // * PthreadLockChecker, a simple lock -> unlock checker.
115ffd83dbSDimitry Andric // Which also checks for XNU locks, which behave similarly enough to share
125ffd83dbSDimitry Andric // code.
135ffd83dbSDimitry Andric // * FuchsiaLocksChecker, which is also rather similar.
145ffd83dbSDimitry Andric // * C11LockChecker which also closely follows Pthread semantics.
155ffd83dbSDimitry Andric //
165ffd83dbSDimitry Andric // TODO: Path notes.
170b57cec5SDimitry Andric //
180b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
190b57cec5SDimitry Andric
200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
230b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
24349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
255ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
260b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
270b57cec5SDimitry Andric
280b57cec5SDimitry Andric using namespace clang;
290b57cec5SDimitry Andric using namespace ento;
300b57cec5SDimitry Andric
310b57cec5SDimitry Andric namespace {
320b57cec5SDimitry Andric
330b57cec5SDimitry Andric struct LockState {
340b57cec5SDimitry Andric enum Kind {
350b57cec5SDimitry Andric Destroyed,
360b57cec5SDimitry Andric Locked,
370b57cec5SDimitry Andric Unlocked,
380b57cec5SDimitry Andric UntouchedAndPossiblyDestroyed,
390b57cec5SDimitry Andric UnlockedAndPossiblyDestroyed
400b57cec5SDimitry Andric } K;
410b57cec5SDimitry Andric
420b57cec5SDimitry Andric private:
LockState__anonba8256140111::LockState430b57cec5SDimitry Andric LockState(Kind K) : K(K) {}
440b57cec5SDimitry Andric
450b57cec5SDimitry Andric public:
getLocked__anonba8256140111::LockState460b57cec5SDimitry Andric static LockState getLocked() { return LockState(Locked); }
getUnlocked__anonba8256140111::LockState470b57cec5SDimitry Andric static LockState getUnlocked() { return LockState(Unlocked); }
getDestroyed__anonba8256140111::LockState480b57cec5SDimitry Andric static LockState getDestroyed() { return LockState(Destroyed); }
getUntouchedAndPossiblyDestroyed__anonba8256140111::LockState490b57cec5SDimitry Andric static LockState getUntouchedAndPossiblyDestroyed() {
500b57cec5SDimitry Andric return LockState(UntouchedAndPossiblyDestroyed);
510b57cec5SDimitry Andric }
getUnlockedAndPossiblyDestroyed__anonba8256140111::LockState520b57cec5SDimitry Andric static LockState getUnlockedAndPossiblyDestroyed() {
530b57cec5SDimitry Andric return LockState(UnlockedAndPossiblyDestroyed);
540b57cec5SDimitry Andric }
550b57cec5SDimitry Andric
operator ==__anonba8256140111::LockState565ffd83dbSDimitry Andric bool operator==(const LockState &X) const { return K == X.K; }
570b57cec5SDimitry Andric
isLocked__anonba8256140111::LockState580b57cec5SDimitry Andric bool isLocked() const { return K == Locked; }
isUnlocked__anonba8256140111::LockState590b57cec5SDimitry Andric bool isUnlocked() const { return K == Unlocked; }
isDestroyed__anonba8256140111::LockState600b57cec5SDimitry Andric bool isDestroyed() const { return K == Destroyed; }
isUntouchedAndPossiblyDestroyed__anonba8256140111::LockState610b57cec5SDimitry Andric bool isUntouchedAndPossiblyDestroyed() const {
620b57cec5SDimitry Andric return K == UntouchedAndPossiblyDestroyed;
630b57cec5SDimitry Andric }
isUnlockedAndPossiblyDestroyed__anonba8256140111::LockState640b57cec5SDimitry Andric bool isUnlockedAndPossiblyDestroyed() const {
650b57cec5SDimitry Andric return K == UnlockedAndPossiblyDestroyed;
660b57cec5SDimitry Andric }
670b57cec5SDimitry Andric
Profile__anonba8256140111::LockState685ffd83dbSDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
690b57cec5SDimitry Andric };
700b57cec5SDimitry Andric
715ffd83dbSDimitry Andric class PthreadLockChecker : public Checker<check::PostCall, check::DeadSymbols,
725ffd83dbSDimitry Andric check::RegionChanges> {
730b57cec5SDimitry Andric public:
745ffd83dbSDimitry Andric enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics };
755ffd83dbSDimitry Andric enum CheckerKind {
765ffd83dbSDimitry Andric CK_PthreadLockChecker,
775ffd83dbSDimitry Andric CK_FuchsiaLockChecker,
785ffd83dbSDimitry Andric CK_C11LockChecker,
795ffd83dbSDimitry Andric CK_NumCheckKinds
805ffd83dbSDimitry Andric };
8181ad6265SDimitry Andric bool ChecksEnabled[CK_NumCheckKinds] = {false};
825ffd83dbSDimitry Andric CheckerNameRef CheckNames[CK_NumCheckKinds];
830b57cec5SDimitry Andric
845ffd83dbSDimitry Andric private:
855ffd83dbSDimitry Andric typedef void (PthreadLockChecker::*FnCheck)(const CallEvent &Call,
865ffd83dbSDimitry Andric CheckerContext &C,
87e8d8bef9SDimitry Andric CheckerKind CheckKind) const;
885ffd83dbSDimitry Andric CallDescriptionMap<FnCheck> PThreadCallbacks = {
895ffd83dbSDimitry Andric // Init.
90*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"pthread_mutex_init"}, 2},
91*0fca6ea1SDimitry Andric &PthreadLockChecker::InitAnyLock},
925ffd83dbSDimitry Andric // TODO: pthread_rwlock_init(2 arguments).
935ffd83dbSDimitry Andric // TODO: lck_mtx_init(3 arguments).
945ffd83dbSDimitry Andric // TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex.
955ffd83dbSDimitry Andric // TODO: lck_rw_init(3 arguments).
965ffd83dbSDimitry Andric // TODO: lck_rw_alloc_init(2 arguments) => returns the mutex.
970b57cec5SDimitry Andric
985ffd83dbSDimitry Andric // Acquire.
99*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"pthread_mutex_lock"}, 1},
100*0fca6ea1SDimitry Andric &PthreadLockChecker::AcquirePthreadLock},
101*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"pthread_rwlock_rdlock"}, 1},
102*0fca6ea1SDimitry Andric &PthreadLockChecker::AcquirePthreadLock},
103*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"pthread_rwlock_wrlock"}, 1},
104*0fca6ea1SDimitry Andric &PthreadLockChecker::AcquirePthreadLock},
105*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"lck_mtx_lock"}, 1},
106*0fca6ea1SDimitry Andric &PthreadLockChecker::AcquireXNULock},
107*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"lck_rw_lock_exclusive"}, 1},
108*0fca6ea1SDimitry Andric &PthreadLockChecker::AcquireXNULock},
109*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"lck_rw_lock_shared"}, 1},
110*0fca6ea1SDimitry Andric &PthreadLockChecker::AcquireXNULock},
1115ffd83dbSDimitry Andric
1125ffd83dbSDimitry Andric // Try.
113*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"pthread_mutex_trylock"}, 1},
114*0fca6ea1SDimitry Andric &PthreadLockChecker::TryPthreadLock},
115*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"pthread_rwlock_tryrdlock"}, 1},
116*0fca6ea1SDimitry Andric &PthreadLockChecker::TryPthreadLock},
117*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"pthread_rwlock_trywrlock"}, 1},
118*0fca6ea1SDimitry Andric &PthreadLockChecker::TryPthreadLock},
119*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"lck_mtx_try_lock"}, 1},
120*0fca6ea1SDimitry Andric &PthreadLockChecker::TryXNULock},
121*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"lck_rw_try_lock_exclusive"}, 1},
122*0fca6ea1SDimitry Andric &PthreadLockChecker::TryXNULock},
123*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"lck_rw_try_lock_shared"}, 1},
124*0fca6ea1SDimitry Andric &PthreadLockChecker::TryXNULock},
1255ffd83dbSDimitry Andric
1265ffd83dbSDimitry Andric // Release.
127*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"pthread_mutex_unlock"}, 1},
128*0fca6ea1SDimitry Andric &PthreadLockChecker::ReleaseAnyLock},
129*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"pthread_rwlock_unlock"}, 1},
130*0fca6ea1SDimitry Andric &PthreadLockChecker::ReleaseAnyLock},
131*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"lck_mtx_unlock"}, 1},
132*0fca6ea1SDimitry Andric &PthreadLockChecker::ReleaseAnyLock},
133*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"lck_rw_unlock_exclusive"}, 1},
134*0fca6ea1SDimitry Andric &PthreadLockChecker::ReleaseAnyLock},
135*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"lck_rw_unlock_shared"}, 1},
136*0fca6ea1SDimitry Andric &PthreadLockChecker::ReleaseAnyLock},
137*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"lck_rw_done"}, 1},
138*0fca6ea1SDimitry Andric &PthreadLockChecker::ReleaseAnyLock},
1395ffd83dbSDimitry Andric
1405ffd83dbSDimitry Andric // Destroy.
141*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"pthread_mutex_destroy"}, 1},
142*0fca6ea1SDimitry Andric &PthreadLockChecker::DestroyPthreadLock},
143*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"lck_mtx_destroy"}, 2},
144*0fca6ea1SDimitry Andric &PthreadLockChecker::DestroyXNULock},
1455ffd83dbSDimitry Andric // TODO: pthread_rwlock_destroy(1 argument).
1465ffd83dbSDimitry Andric // TODO: lck_rw_destroy(2 arguments).
1475ffd83dbSDimitry Andric };
1485ffd83dbSDimitry Andric
1495ffd83dbSDimitry Andric CallDescriptionMap<FnCheck> FuchsiaCallbacks = {
1505ffd83dbSDimitry Andric // Init.
151*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"spin_lock_init"}, 1},
152*0fca6ea1SDimitry Andric &PthreadLockChecker::InitAnyLock},
1535ffd83dbSDimitry Andric
1545ffd83dbSDimitry Andric // Acquire.
155*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"spin_lock"}, 1},
156*0fca6ea1SDimitry Andric &PthreadLockChecker::AcquirePthreadLock},
157*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"spin_lock_save"}, 3},
158*0fca6ea1SDimitry Andric &PthreadLockChecker::AcquirePthreadLock},
159*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"sync_mutex_lock"}, 1},
160*0fca6ea1SDimitry Andric &PthreadLockChecker::AcquirePthreadLock},
161*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"sync_mutex_lock_with_waiter"}, 1},
1625ffd83dbSDimitry Andric &PthreadLockChecker::AcquirePthreadLock},
1635ffd83dbSDimitry Andric
1645ffd83dbSDimitry Andric // Try.
165*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"spin_trylock"}, 1},
166*0fca6ea1SDimitry Andric &PthreadLockChecker::TryFuchsiaLock},
167*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"sync_mutex_trylock"}, 1},
168*0fca6ea1SDimitry Andric &PthreadLockChecker::TryFuchsiaLock},
169*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"sync_mutex_timedlock"}, 2},
170*0fca6ea1SDimitry Andric &PthreadLockChecker::TryFuchsiaLock},
1715ffd83dbSDimitry Andric
1725ffd83dbSDimitry Andric // Release.
173*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"spin_unlock"}, 1},
174*0fca6ea1SDimitry Andric &PthreadLockChecker::ReleaseAnyLock},
175*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"spin_unlock_restore"}, 3},
176*0fca6ea1SDimitry Andric &PthreadLockChecker::ReleaseAnyLock},
177*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"sync_mutex_unlock"}, 1},
178*0fca6ea1SDimitry Andric &PthreadLockChecker::ReleaseAnyLock},
1795ffd83dbSDimitry Andric };
1805ffd83dbSDimitry Andric
1815ffd83dbSDimitry Andric CallDescriptionMap<FnCheck> C11Callbacks = {
1825ffd83dbSDimitry Andric // Init.
183*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"mtx_init"}, 2}, &PthreadLockChecker::InitAnyLock},
1845ffd83dbSDimitry Andric
1855ffd83dbSDimitry Andric // Acquire.
186*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"mtx_lock"}, 1},
187*0fca6ea1SDimitry Andric &PthreadLockChecker::AcquirePthreadLock},
1885ffd83dbSDimitry Andric
1895ffd83dbSDimitry Andric // Try.
190*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"mtx_trylock"}, 1}, &PthreadLockChecker::TryC11Lock},
191*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"mtx_timedlock"}, 2}, &PthreadLockChecker::TryC11Lock},
1925ffd83dbSDimitry Andric
1935ffd83dbSDimitry Andric // Release.
194*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"mtx_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
1955ffd83dbSDimitry Andric
1965ffd83dbSDimitry Andric // Destroy
197*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"mtx_destroy"}, 1},
198*0fca6ea1SDimitry Andric &PthreadLockChecker::DestroyPthreadLock},
1995ffd83dbSDimitry Andric };
2005ffd83dbSDimitry Andric
2010b57cec5SDimitry Andric ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state,
2020b57cec5SDimitry Andric const MemRegion *lockR,
2030b57cec5SDimitry Andric const SymbolRef *sym) const;
204e8d8bef9SDimitry Andric void reportBug(CheckerContext &C, std::unique_ptr<BugType> BT[],
205e8d8bef9SDimitry Andric const Expr *MtxExpr, CheckerKind CheckKind,
206e8d8bef9SDimitry Andric StringRef Desc) const;
2075ffd83dbSDimitry Andric
2085ffd83dbSDimitry Andric // Init.
2095ffd83dbSDimitry Andric void InitAnyLock(const CallEvent &Call, CheckerContext &C,
210e8d8bef9SDimitry Andric CheckerKind CheckKind) const;
211e8d8bef9SDimitry Andric void InitLockAux(const CallEvent &Call, CheckerContext &C,
212e8d8bef9SDimitry Andric const Expr *MtxExpr, SVal MtxVal,
213e8d8bef9SDimitry Andric CheckerKind CheckKind) const;
2145ffd83dbSDimitry Andric
2155ffd83dbSDimitry Andric // Lock, Try-lock.
2165ffd83dbSDimitry Andric void AcquirePthreadLock(const CallEvent &Call, CheckerContext &C,
217e8d8bef9SDimitry Andric CheckerKind CheckKind) const;
2185ffd83dbSDimitry Andric void AcquireXNULock(const CallEvent &Call, CheckerContext &C,
219e8d8bef9SDimitry Andric CheckerKind CheckKind) const;
2205ffd83dbSDimitry Andric void TryPthreadLock(const CallEvent &Call, CheckerContext &C,
221e8d8bef9SDimitry Andric CheckerKind CheckKind) const;
2225ffd83dbSDimitry Andric void TryXNULock(const CallEvent &Call, CheckerContext &C,
223e8d8bef9SDimitry Andric CheckerKind CheckKind) const;
2245ffd83dbSDimitry Andric void TryFuchsiaLock(const CallEvent &Call, CheckerContext &C,
225e8d8bef9SDimitry Andric CheckerKind CheckKind) const;
2265ffd83dbSDimitry Andric void TryC11Lock(const CallEvent &Call, CheckerContext &C,
227e8d8bef9SDimitry Andric CheckerKind CheckKind) const;
228e8d8bef9SDimitry Andric void AcquireLockAux(const CallEvent &Call, CheckerContext &C,
229e8d8bef9SDimitry Andric const Expr *MtxExpr, SVal MtxVal, bool IsTryLock,
230e8d8bef9SDimitry Andric LockingSemantics Semantics, CheckerKind CheckKind) const;
2315ffd83dbSDimitry Andric
2325ffd83dbSDimitry Andric // Release.
2335ffd83dbSDimitry Andric void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C,
234e8d8bef9SDimitry Andric CheckerKind CheckKind) const;
235e8d8bef9SDimitry Andric void ReleaseLockAux(const CallEvent &Call, CheckerContext &C,
236e8d8bef9SDimitry Andric const Expr *MtxExpr, SVal MtxVal,
237e8d8bef9SDimitry Andric CheckerKind CheckKind) const;
2385ffd83dbSDimitry Andric
2395ffd83dbSDimitry Andric // Destroy.
2405ffd83dbSDimitry Andric void DestroyPthreadLock(const CallEvent &Call, CheckerContext &C,
241e8d8bef9SDimitry Andric CheckerKind CheckKind) const;
2425ffd83dbSDimitry Andric void DestroyXNULock(const CallEvent &Call, CheckerContext &C,
243e8d8bef9SDimitry Andric CheckerKind CheckKind) const;
244e8d8bef9SDimitry Andric void DestroyLockAux(const CallEvent &Call, CheckerContext &C,
245e8d8bef9SDimitry Andric const Expr *MtxExpr, SVal MtxVal,
246e8d8bef9SDimitry Andric LockingSemantics Semantics, CheckerKind CheckKind) const;
2475ffd83dbSDimitry Andric
2485ffd83dbSDimitry Andric public:
2495ffd83dbSDimitry Andric void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
2505ffd83dbSDimitry Andric void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
2515ffd83dbSDimitry Andric ProgramStateRef
2525ffd83dbSDimitry Andric checkRegionChanges(ProgramStateRef State, const InvalidatedSymbols *Symbols,
2535ffd83dbSDimitry Andric ArrayRef<const MemRegion *> ExplicitRegions,
2545ffd83dbSDimitry Andric ArrayRef<const MemRegion *> Regions,
2555ffd83dbSDimitry Andric const LocationContext *LCtx, const CallEvent *Call) const;
2565ffd83dbSDimitry Andric void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
2575ffd83dbSDimitry Andric const char *Sep) const override;
2585ffd83dbSDimitry Andric
2595ffd83dbSDimitry Andric private:
2605ffd83dbSDimitry Andric mutable std::unique_ptr<BugType> BT_doublelock[CK_NumCheckKinds];
2615ffd83dbSDimitry Andric mutable std::unique_ptr<BugType> BT_doubleunlock[CK_NumCheckKinds];
2625ffd83dbSDimitry Andric mutable std::unique_ptr<BugType> BT_destroylock[CK_NumCheckKinds];
2635ffd83dbSDimitry Andric mutable std::unique_ptr<BugType> BT_initlock[CK_NumCheckKinds];
2645ffd83dbSDimitry Andric mutable std::unique_ptr<BugType> BT_lor[CK_NumCheckKinds];
2655ffd83dbSDimitry Andric
initBugType(CheckerKind CheckKind) const266e8d8bef9SDimitry Andric void initBugType(CheckerKind CheckKind) const {
267e8d8bef9SDimitry Andric if (BT_doublelock[CheckKind])
2685ffd83dbSDimitry Andric return;
269e8d8bef9SDimitry Andric BT_doublelock[CheckKind].reset(
270e8d8bef9SDimitry Andric new BugType{CheckNames[CheckKind], "Double locking", "Lock checker"});
271e8d8bef9SDimitry Andric BT_doubleunlock[CheckKind].reset(
272e8d8bef9SDimitry Andric new BugType{CheckNames[CheckKind], "Double unlocking", "Lock checker"});
273e8d8bef9SDimitry Andric BT_destroylock[CheckKind].reset(new BugType{
274e8d8bef9SDimitry Andric CheckNames[CheckKind], "Use destroyed lock", "Lock checker"});
275e8d8bef9SDimitry Andric BT_initlock[CheckKind].reset(new BugType{
276e8d8bef9SDimitry Andric CheckNames[CheckKind], "Init invalid lock", "Lock checker"});
277e8d8bef9SDimitry Andric BT_lor[CheckKind].reset(new BugType{CheckNames[CheckKind],
2785ffd83dbSDimitry Andric "Lock order reversal", "Lock checker"});
2795ffd83dbSDimitry Andric }
2800b57cec5SDimitry Andric };
2810b57cec5SDimitry Andric } // end anonymous namespace
2820b57cec5SDimitry Andric
2830b57cec5SDimitry Andric // A stack of locks for tracking lock-unlock order.
REGISTER_LIST_WITH_PROGRAMSTATE(LockSet,const MemRegion *)2840b57cec5SDimitry Andric REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *)
2850b57cec5SDimitry Andric
2860b57cec5SDimitry Andric // An entry for tracking lock states.
2870b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState)
2880b57cec5SDimitry Andric
2890b57cec5SDimitry Andric // Return values for unresolved calls to pthread_mutex_destroy().
2900b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(DestroyRetVal, const MemRegion *, SymbolRef)
2910b57cec5SDimitry Andric
2925ffd83dbSDimitry Andric void PthreadLockChecker::checkPostCall(const CallEvent &Call,
2930b57cec5SDimitry Andric CheckerContext &C) const {
2945ffd83dbSDimitry Andric // FIXME: Try to handle cases when the implementation was inlined rather
2955ffd83dbSDimitry Andric // than just giving up.
296*0fca6ea1SDimitry Andric if (C.wasInlined)
2970b57cec5SDimitry Andric return;
2980b57cec5SDimitry Andric
2995ffd83dbSDimitry Andric if (const FnCheck *Callback = PThreadCallbacks.lookup(Call))
3005ffd83dbSDimitry Andric (this->**Callback)(Call, C, CK_PthreadLockChecker);
3015ffd83dbSDimitry Andric else if (const FnCheck *Callback = FuchsiaCallbacks.lookup(Call))
3025ffd83dbSDimitry Andric (this->**Callback)(Call, C, CK_FuchsiaLockChecker);
3035ffd83dbSDimitry Andric else if (const FnCheck *Callback = C11Callbacks.lookup(Call))
3045ffd83dbSDimitry Andric (this->**Callback)(Call, C, CK_C11LockChecker);
3050b57cec5SDimitry Andric }
3060b57cec5SDimitry Andric
3070b57cec5SDimitry Andric // When a lock is destroyed, in some semantics(like PthreadSemantics) we are not
3080b57cec5SDimitry Andric // sure if the destroy call has succeeded or failed, and the lock enters one of
3090b57cec5SDimitry Andric // the 'possibly destroyed' state. There is a short time frame for the
3100b57cec5SDimitry Andric // programmer to check the return value to see if the lock was successfully
3110b57cec5SDimitry Andric // destroyed. Before we model the next operation over that lock, we call this
3120b57cec5SDimitry Andric // function to see if the return value was checked by now and set the lock state
3130b57cec5SDimitry Andric // - either to destroyed state or back to its previous state.
3140b57cec5SDimitry Andric
3150b57cec5SDimitry Andric // In PthreadSemantics, pthread_mutex_destroy() returns zero if the lock is
3160b57cec5SDimitry Andric // successfully destroyed and it returns a non-zero value otherwise.
resolvePossiblyDestroyedMutex(ProgramStateRef state,const MemRegion * lockR,const SymbolRef * sym) const3170b57cec5SDimitry Andric ProgramStateRef PthreadLockChecker::resolvePossiblyDestroyedMutex(
3180b57cec5SDimitry Andric ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const {
3190b57cec5SDimitry Andric const LockState *lstate = state->get<LockMap>(lockR);
3200b57cec5SDimitry Andric // Existence in DestroyRetVal ensures existence in LockMap.
3210b57cec5SDimitry Andric // Existence in Destroyed also ensures that the lock state for lockR is either
3220b57cec5SDimitry Andric // UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed.
32306c3fb27SDimitry Andric assert(lstate);
3240b57cec5SDimitry Andric assert(lstate->isUntouchedAndPossiblyDestroyed() ||
3250b57cec5SDimitry Andric lstate->isUnlockedAndPossiblyDestroyed());
3260b57cec5SDimitry Andric
3270b57cec5SDimitry Andric ConstraintManager &CMgr = state->getConstraintManager();
3280b57cec5SDimitry Andric ConditionTruthVal retZero = CMgr.isNull(state, *sym);
3290b57cec5SDimitry Andric if (retZero.isConstrainedFalse()) {
3300b57cec5SDimitry Andric if (lstate->isUntouchedAndPossiblyDestroyed())
3310b57cec5SDimitry Andric state = state->remove<LockMap>(lockR);
3320b57cec5SDimitry Andric else if (lstate->isUnlockedAndPossiblyDestroyed())
3330b57cec5SDimitry Andric state = state->set<LockMap>(lockR, LockState::getUnlocked());
3340b57cec5SDimitry Andric } else
3350b57cec5SDimitry Andric state = state->set<LockMap>(lockR, LockState::getDestroyed());
3360b57cec5SDimitry Andric
3370b57cec5SDimitry Andric // Removing the map entry (lockR, sym) from DestroyRetVal as the lock state is
3380b57cec5SDimitry Andric // now resolved.
3390b57cec5SDimitry Andric state = state->remove<DestroyRetVal>(lockR);
3400b57cec5SDimitry Andric return state;
3410b57cec5SDimitry Andric }
3420b57cec5SDimitry Andric
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const3430b57cec5SDimitry Andric void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State,
3440b57cec5SDimitry Andric const char *NL, const char *Sep) const {
3450b57cec5SDimitry Andric LockMapTy LM = State->get<LockMap>();
3460b57cec5SDimitry Andric if (!LM.isEmpty()) {
3470b57cec5SDimitry Andric Out << Sep << "Mutex states:" << NL;
3480b57cec5SDimitry Andric for (auto I : LM) {
3490b57cec5SDimitry Andric I.first->dumpToStream(Out);
3500b57cec5SDimitry Andric if (I.second.isLocked())
3510b57cec5SDimitry Andric Out << ": locked";
3520b57cec5SDimitry Andric else if (I.second.isUnlocked())
3530b57cec5SDimitry Andric Out << ": unlocked";
3540b57cec5SDimitry Andric else if (I.second.isDestroyed())
3550b57cec5SDimitry Andric Out << ": destroyed";
3560b57cec5SDimitry Andric else if (I.second.isUntouchedAndPossiblyDestroyed())
3570b57cec5SDimitry Andric Out << ": not tracked, possibly destroyed";
3580b57cec5SDimitry Andric else if (I.second.isUnlockedAndPossiblyDestroyed())
3590b57cec5SDimitry Andric Out << ": unlocked, possibly destroyed";
3600b57cec5SDimitry Andric Out << NL;
3610b57cec5SDimitry Andric }
3620b57cec5SDimitry Andric }
3630b57cec5SDimitry Andric
3640b57cec5SDimitry Andric LockSetTy LS = State->get<LockSet>();
3650b57cec5SDimitry Andric if (!LS.isEmpty()) {
3660b57cec5SDimitry Andric Out << Sep << "Mutex lock order:" << NL;
3670b57cec5SDimitry Andric for (auto I : LS) {
3680b57cec5SDimitry Andric I->dumpToStream(Out);
3690b57cec5SDimitry Andric Out << NL;
3700b57cec5SDimitry Andric }
3710b57cec5SDimitry Andric }
3720b57cec5SDimitry Andric
373fe6060f1SDimitry Andric DestroyRetValTy DRV = State->get<DestroyRetVal>();
374fe6060f1SDimitry Andric if (!DRV.isEmpty()) {
375fe6060f1SDimitry Andric Out << Sep << "Mutexes in unresolved possibly destroyed state:" << NL;
376fe6060f1SDimitry Andric for (auto I : DRV) {
377fe6060f1SDimitry Andric I.first->dumpToStream(Out);
378fe6060f1SDimitry Andric Out << ": ";
379fe6060f1SDimitry Andric I.second->dumpToStream(Out);
380fe6060f1SDimitry Andric Out << NL;
381fe6060f1SDimitry Andric }
382fe6060f1SDimitry Andric }
3830b57cec5SDimitry Andric }
3840b57cec5SDimitry Andric
AcquirePthreadLock(const CallEvent & Call,CheckerContext & C,CheckerKind CheckKind) const3855ffd83dbSDimitry Andric void PthreadLockChecker::AcquirePthreadLock(const CallEvent &Call,
3865ffd83dbSDimitry Andric CheckerContext &C,
387e8d8bef9SDimitry Andric CheckerKind CheckKind) const {
388e8d8bef9SDimitry Andric AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false,
389e8d8bef9SDimitry Andric PthreadSemantics, CheckKind);
3905ffd83dbSDimitry Andric }
3915ffd83dbSDimitry Andric
AcquireXNULock(const CallEvent & Call,CheckerContext & C,CheckerKind CheckKind) const3925ffd83dbSDimitry Andric void PthreadLockChecker::AcquireXNULock(const CallEvent &Call,
3935ffd83dbSDimitry Andric CheckerContext &C,
394e8d8bef9SDimitry Andric CheckerKind CheckKind) const {
395e8d8bef9SDimitry Andric AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false,
396e8d8bef9SDimitry Andric XNUSemantics, CheckKind);
3975ffd83dbSDimitry Andric }
3985ffd83dbSDimitry Andric
TryPthreadLock(const CallEvent & Call,CheckerContext & C,CheckerKind CheckKind) const3995ffd83dbSDimitry Andric void PthreadLockChecker::TryPthreadLock(const CallEvent &Call,
4005ffd83dbSDimitry Andric CheckerContext &C,
401e8d8bef9SDimitry Andric CheckerKind CheckKind) const {
402e8d8bef9SDimitry Andric AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
403e8d8bef9SDimitry Andric PthreadSemantics, CheckKind);
4045ffd83dbSDimitry Andric }
4055ffd83dbSDimitry Andric
TryXNULock(const CallEvent & Call,CheckerContext & C,CheckerKind CheckKind) const4065ffd83dbSDimitry Andric void PthreadLockChecker::TryXNULock(const CallEvent &Call, CheckerContext &C,
407e8d8bef9SDimitry Andric CheckerKind CheckKind) const {
408e8d8bef9SDimitry Andric AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
409e8d8bef9SDimitry Andric PthreadSemantics, CheckKind);
4105ffd83dbSDimitry Andric }
4115ffd83dbSDimitry Andric
TryFuchsiaLock(const CallEvent & Call,CheckerContext & C,CheckerKind CheckKind) const4125ffd83dbSDimitry Andric void PthreadLockChecker::TryFuchsiaLock(const CallEvent &Call,
4135ffd83dbSDimitry Andric CheckerContext &C,
414e8d8bef9SDimitry Andric CheckerKind CheckKind) const {
415e8d8bef9SDimitry Andric AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
416e8d8bef9SDimitry Andric PthreadSemantics, CheckKind);
4175ffd83dbSDimitry Andric }
4185ffd83dbSDimitry Andric
TryC11Lock(const CallEvent & Call,CheckerContext & C,CheckerKind CheckKind) const4195ffd83dbSDimitry Andric void PthreadLockChecker::TryC11Lock(const CallEvent &Call, CheckerContext &C,
420e8d8bef9SDimitry Andric CheckerKind CheckKind) const {
421e8d8bef9SDimitry Andric AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
422e8d8bef9SDimitry Andric PthreadSemantics, CheckKind);
4235ffd83dbSDimitry Andric }
4245ffd83dbSDimitry Andric
AcquireLockAux(const CallEvent & Call,CheckerContext & C,const Expr * MtxExpr,SVal MtxVal,bool IsTryLock,enum LockingSemantics Semantics,CheckerKind CheckKind) const4255ffd83dbSDimitry Andric void PthreadLockChecker::AcquireLockAux(const CallEvent &Call,
426e8d8bef9SDimitry Andric CheckerContext &C, const Expr *MtxExpr,
427e8d8bef9SDimitry Andric SVal MtxVal, bool IsTryLock,
428e8d8bef9SDimitry Andric enum LockingSemantics Semantics,
429e8d8bef9SDimitry Andric CheckerKind CheckKind) const {
430e8d8bef9SDimitry Andric if (!ChecksEnabled[CheckKind])
4315ffd83dbSDimitry Andric return;
4320b57cec5SDimitry Andric
433e8d8bef9SDimitry Andric const MemRegion *lockR = MtxVal.getAsRegion();
4340b57cec5SDimitry Andric if (!lockR)
4350b57cec5SDimitry Andric return;
4360b57cec5SDimitry Andric
4370b57cec5SDimitry Andric ProgramStateRef state = C.getState();
4380b57cec5SDimitry Andric const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
4390b57cec5SDimitry Andric if (sym)
4400b57cec5SDimitry Andric state = resolvePossiblyDestroyedMutex(state, lockR, sym);
4410b57cec5SDimitry Andric
4420b57cec5SDimitry Andric if (const LockState *LState = state->get<LockMap>(lockR)) {
4430b57cec5SDimitry Andric if (LState->isLocked()) {
444e8d8bef9SDimitry Andric reportBug(C, BT_doublelock, MtxExpr, CheckKind,
445e8d8bef9SDimitry Andric "This lock has already been acquired");
4460b57cec5SDimitry Andric return;
4470b57cec5SDimitry Andric } else if (LState->isDestroyed()) {
448e8d8bef9SDimitry Andric reportBug(C, BT_destroylock, MtxExpr, CheckKind,
449e8d8bef9SDimitry Andric "This lock has already been destroyed");
4500b57cec5SDimitry Andric return;
4510b57cec5SDimitry Andric }
4520b57cec5SDimitry Andric }
4530b57cec5SDimitry Andric
4540b57cec5SDimitry Andric ProgramStateRef lockSucc = state;
455e8d8bef9SDimitry Andric if (IsTryLock) {
4560b57cec5SDimitry Andric // Bifurcate the state, and allow a mode where the lock acquisition fails.
4575ffd83dbSDimitry Andric SVal RetVal = Call.getReturnValue();
4585ffd83dbSDimitry Andric if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
4590b57cec5SDimitry Andric ProgramStateRef lockFail;
460e8d8bef9SDimitry Andric switch (Semantics) {
4610b57cec5SDimitry Andric case PthreadSemantics:
4625ffd83dbSDimitry Andric std::tie(lockFail, lockSucc) = state->assume(*DefinedRetVal);
4630b57cec5SDimitry Andric break;
4640b57cec5SDimitry Andric case XNUSemantics:
4655ffd83dbSDimitry Andric std::tie(lockSucc, lockFail) = state->assume(*DefinedRetVal);
4660b57cec5SDimitry Andric break;
4670b57cec5SDimitry Andric default:
4680b57cec5SDimitry Andric llvm_unreachable("Unknown tryLock locking semantics");
4690b57cec5SDimitry Andric }
4700b57cec5SDimitry Andric assert(lockFail && lockSucc);
4710b57cec5SDimitry Andric C.addTransition(lockFail);
4725ffd83dbSDimitry Andric }
4735ffd83dbSDimitry Andric // We might want to handle the case when the mutex lock function was inlined
4745ffd83dbSDimitry Andric // and returned an Unknown or Undefined value.
475e8d8bef9SDimitry Andric } else if (Semantics == PthreadSemantics) {
4760b57cec5SDimitry Andric // Assume that the return value was 0.
4775ffd83dbSDimitry Andric SVal RetVal = Call.getReturnValue();
4785ffd83dbSDimitry Andric if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
4795ffd83dbSDimitry Andric // FIXME: If the lock function was inlined and returned true,
4805ffd83dbSDimitry Andric // we need to behave sanely - at least generate sink.
4815ffd83dbSDimitry Andric lockSucc = state->assume(*DefinedRetVal, false);
4820b57cec5SDimitry Andric assert(lockSucc);
4835ffd83dbSDimitry Andric }
4845ffd83dbSDimitry Andric // We might want to handle the case when the mutex lock function was inlined
4855ffd83dbSDimitry Andric // and returned an Unknown or Undefined value.
4860b57cec5SDimitry Andric } else {
4870b57cec5SDimitry Andric // XNU locking semantics return void on non-try locks
488e8d8bef9SDimitry Andric assert((Semantics == XNUSemantics) && "Unknown locking semantics");
4890b57cec5SDimitry Andric lockSucc = state;
4900b57cec5SDimitry Andric }
4910b57cec5SDimitry Andric
4920b57cec5SDimitry Andric // Record that the lock was acquired.
4930b57cec5SDimitry Andric lockSucc = lockSucc->add<LockSet>(lockR);
4940b57cec5SDimitry Andric lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked());
4950b57cec5SDimitry Andric C.addTransition(lockSucc);
4960b57cec5SDimitry Andric }
4970b57cec5SDimitry Andric
ReleaseAnyLock(const CallEvent & Call,CheckerContext & C,CheckerKind CheckKind) const4985ffd83dbSDimitry Andric void PthreadLockChecker::ReleaseAnyLock(const CallEvent &Call,
4995ffd83dbSDimitry Andric CheckerContext &C,
500e8d8bef9SDimitry Andric CheckerKind CheckKind) const {
501e8d8bef9SDimitry Andric ReleaseLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), CheckKind);
5025ffd83dbSDimitry Andric }
5035ffd83dbSDimitry Andric
ReleaseLockAux(const CallEvent & Call,CheckerContext & C,const Expr * MtxExpr,SVal MtxVal,CheckerKind CheckKind) const5045ffd83dbSDimitry Andric void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call,
505e8d8bef9SDimitry Andric CheckerContext &C, const Expr *MtxExpr,
506e8d8bef9SDimitry Andric SVal MtxVal,
507e8d8bef9SDimitry Andric CheckerKind CheckKind) const {
508e8d8bef9SDimitry Andric if (!ChecksEnabled[CheckKind])
5095ffd83dbSDimitry Andric return;
5100b57cec5SDimitry Andric
511e8d8bef9SDimitry Andric const MemRegion *lockR = MtxVal.getAsRegion();
5120b57cec5SDimitry Andric if (!lockR)
5130b57cec5SDimitry Andric return;
5140b57cec5SDimitry Andric
5150b57cec5SDimitry Andric ProgramStateRef state = C.getState();
5160b57cec5SDimitry Andric const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
5170b57cec5SDimitry Andric if (sym)
5180b57cec5SDimitry Andric state = resolvePossiblyDestroyedMutex(state, lockR, sym);
5190b57cec5SDimitry Andric
5200b57cec5SDimitry Andric if (const LockState *LState = state->get<LockMap>(lockR)) {
5210b57cec5SDimitry Andric if (LState->isUnlocked()) {
522e8d8bef9SDimitry Andric reportBug(C, BT_doubleunlock, MtxExpr, CheckKind,
523e8d8bef9SDimitry Andric "This lock has already been unlocked");
5240b57cec5SDimitry Andric return;
5250b57cec5SDimitry Andric } else if (LState->isDestroyed()) {
526e8d8bef9SDimitry Andric reportBug(C, BT_destroylock, MtxExpr, CheckKind,
527e8d8bef9SDimitry Andric "This lock has already been destroyed");
5280b57cec5SDimitry Andric return;
5290b57cec5SDimitry Andric }
5300b57cec5SDimitry Andric }
5310b57cec5SDimitry Andric
5320b57cec5SDimitry Andric LockSetTy LS = state->get<LockSet>();
5330b57cec5SDimitry Andric
5340b57cec5SDimitry Andric if (!LS.isEmpty()) {
5350b57cec5SDimitry Andric const MemRegion *firstLockR = LS.getHead();
5360b57cec5SDimitry Andric if (firstLockR != lockR) {
537e8d8bef9SDimitry Andric reportBug(C, BT_lor, MtxExpr, CheckKind,
538e8d8bef9SDimitry Andric "This was not the most recently acquired lock. Possible lock "
539e8d8bef9SDimitry Andric "order reversal");
5400b57cec5SDimitry Andric return;
5410b57cec5SDimitry Andric }
5420b57cec5SDimitry Andric // Record that the lock was released.
5430b57cec5SDimitry Andric state = state->set<LockSet>(LS.getTail());
5440b57cec5SDimitry Andric }
5450b57cec5SDimitry Andric
5460b57cec5SDimitry Andric state = state->set<LockMap>(lockR, LockState::getUnlocked());
5470b57cec5SDimitry Andric C.addTransition(state);
5480b57cec5SDimitry Andric }
5490b57cec5SDimitry Andric
DestroyPthreadLock(const CallEvent & Call,CheckerContext & C,CheckerKind CheckKind) const5505ffd83dbSDimitry Andric void PthreadLockChecker::DestroyPthreadLock(const CallEvent &Call,
5515ffd83dbSDimitry Andric CheckerContext &C,
552e8d8bef9SDimitry Andric CheckerKind CheckKind) const {
553e8d8bef9SDimitry Andric DestroyLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0),
554e8d8bef9SDimitry Andric PthreadSemantics, CheckKind);
5555ffd83dbSDimitry Andric }
5565ffd83dbSDimitry Andric
DestroyXNULock(const CallEvent & Call,CheckerContext & C,CheckerKind CheckKind) const5575ffd83dbSDimitry Andric void PthreadLockChecker::DestroyXNULock(const CallEvent &Call,
5585ffd83dbSDimitry Andric CheckerContext &C,
559e8d8bef9SDimitry Andric CheckerKind CheckKind) const {
560e8d8bef9SDimitry Andric DestroyLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), XNUSemantics,
561e8d8bef9SDimitry Andric CheckKind);
5625ffd83dbSDimitry Andric }
5635ffd83dbSDimitry Andric
DestroyLockAux(const CallEvent & Call,CheckerContext & C,const Expr * MtxExpr,SVal MtxVal,enum LockingSemantics Semantics,CheckerKind CheckKind) const5645ffd83dbSDimitry Andric void PthreadLockChecker::DestroyLockAux(const CallEvent &Call,
565e8d8bef9SDimitry Andric CheckerContext &C, const Expr *MtxExpr,
566e8d8bef9SDimitry Andric SVal MtxVal,
567e8d8bef9SDimitry Andric enum LockingSemantics Semantics,
568e8d8bef9SDimitry Andric CheckerKind CheckKind) const {
569e8d8bef9SDimitry Andric if (!ChecksEnabled[CheckKind])
5705ffd83dbSDimitry Andric return;
5710b57cec5SDimitry Andric
572e8d8bef9SDimitry Andric const MemRegion *LockR = MtxVal.getAsRegion();
5730b57cec5SDimitry Andric if (!LockR)
5740b57cec5SDimitry Andric return;
5750b57cec5SDimitry Andric
5760b57cec5SDimitry Andric ProgramStateRef State = C.getState();
5770b57cec5SDimitry Andric
5780b57cec5SDimitry Andric const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
5790b57cec5SDimitry Andric if (sym)
5800b57cec5SDimitry Andric State = resolvePossiblyDestroyedMutex(State, LockR, sym);
5810b57cec5SDimitry Andric
5820b57cec5SDimitry Andric const LockState *LState = State->get<LockMap>(LockR);
5830b57cec5SDimitry Andric // Checking the return value of the destroy method only in the case of
5840b57cec5SDimitry Andric // PthreadSemantics
585e8d8bef9SDimitry Andric if (Semantics == PthreadSemantics) {
5860b57cec5SDimitry Andric if (!LState || LState->isUnlocked()) {
5875ffd83dbSDimitry Andric SymbolRef sym = Call.getReturnValue().getAsSymbol();
5880b57cec5SDimitry Andric if (!sym) {
5890b57cec5SDimitry Andric State = State->remove<LockMap>(LockR);
5900b57cec5SDimitry Andric C.addTransition(State);
5910b57cec5SDimitry Andric return;
5920b57cec5SDimitry Andric }
5930b57cec5SDimitry Andric State = State->set<DestroyRetVal>(LockR, sym);
5940b57cec5SDimitry Andric if (LState && LState->isUnlocked())
5950b57cec5SDimitry Andric State = State->set<LockMap>(
5960b57cec5SDimitry Andric LockR, LockState::getUnlockedAndPossiblyDestroyed());
5970b57cec5SDimitry Andric else
5980b57cec5SDimitry Andric State = State->set<LockMap>(
5990b57cec5SDimitry Andric LockR, LockState::getUntouchedAndPossiblyDestroyed());
6000b57cec5SDimitry Andric C.addTransition(State);
6010b57cec5SDimitry Andric return;
6020b57cec5SDimitry Andric }
6030b57cec5SDimitry Andric } else {
6040b57cec5SDimitry Andric if (!LState || LState->isUnlocked()) {
6050b57cec5SDimitry Andric State = State->set<LockMap>(LockR, LockState::getDestroyed());
6060b57cec5SDimitry Andric C.addTransition(State);
6070b57cec5SDimitry Andric return;
6080b57cec5SDimitry Andric }
6090b57cec5SDimitry Andric }
6100b57cec5SDimitry Andric
611e8d8bef9SDimitry Andric StringRef Message = LState->isLocked()
612e8d8bef9SDimitry Andric ? "This lock is still locked"
613e8d8bef9SDimitry Andric : "This lock has already been destroyed";
6140b57cec5SDimitry Andric
615e8d8bef9SDimitry Andric reportBug(C, BT_destroylock, MtxExpr, CheckKind, Message);
6160b57cec5SDimitry Andric }
6170b57cec5SDimitry Andric
InitAnyLock(const CallEvent & Call,CheckerContext & C,CheckerKind CheckKind) const6185ffd83dbSDimitry Andric void PthreadLockChecker::InitAnyLock(const CallEvent &Call, CheckerContext &C,
619e8d8bef9SDimitry Andric CheckerKind CheckKind) const {
620e8d8bef9SDimitry Andric InitLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), CheckKind);
6215ffd83dbSDimitry Andric }
6225ffd83dbSDimitry Andric
InitLockAux(const CallEvent & Call,CheckerContext & C,const Expr * MtxExpr,SVal MtxVal,CheckerKind CheckKind) const6235ffd83dbSDimitry Andric void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C,
624e8d8bef9SDimitry Andric const Expr *MtxExpr, SVal MtxVal,
625e8d8bef9SDimitry Andric CheckerKind CheckKind) const {
626e8d8bef9SDimitry Andric if (!ChecksEnabled[CheckKind])
6275ffd83dbSDimitry Andric return;
6280b57cec5SDimitry Andric
629e8d8bef9SDimitry Andric const MemRegion *LockR = MtxVal.getAsRegion();
6300b57cec5SDimitry Andric if (!LockR)
6310b57cec5SDimitry Andric return;
6320b57cec5SDimitry Andric
6330b57cec5SDimitry Andric ProgramStateRef State = C.getState();
6340b57cec5SDimitry Andric
6350b57cec5SDimitry Andric const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
6360b57cec5SDimitry Andric if (sym)
6370b57cec5SDimitry Andric State = resolvePossiblyDestroyedMutex(State, LockR, sym);
6380b57cec5SDimitry Andric
6390b57cec5SDimitry Andric const struct LockState *LState = State->get<LockMap>(LockR);
6400b57cec5SDimitry Andric if (!LState || LState->isDestroyed()) {
6410b57cec5SDimitry Andric State = State->set<LockMap>(LockR, LockState::getUnlocked());
6420b57cec5SDimitry Andric C.addTransition(State);
6430b57cec5SDimitry Andric return;
6440b57cec5SDimitry Andric }
6450b57cec5SDimitry Andric
646e8d8bef9SDimitry Andric StringRef Message = LState->isLocked()
647e8d8bef9SDimitry Andric ? "This lock is still being held"
648e8d8bef9SDimitry Andric : "This lock has already been initialized";
6490b57cec5SDimitry Andric
650e8d8bef9SDimitry Andric reportBug(C, BT_initlock, MtxExpr, CheckKind, Message);
6510b57cec5SDimitry Andric }
6520b57cec5SDimitry Andric
reportBug(CheckerContext & C,std::unique_ptr<BugType> BT[],const Expr * MtxExpr,CheckerKind CheckKind,StringRef Desc) const653e8d8bef9SDimitry Andric void PthreadLockChecker::reportBug(CheckerContext &C,
654e8d8bef9SDimitry Andric std::unique_ptr<BugType> BT[],
655e8d8bef9SDimitry Andric const Expr *MtxExpr, CheckerKind CheckKind,
656e8d8bef9SDimitry Andric StringRef Desc) const {
6570b57cec5SDimitry Andric ExplodedNode *N = C.generateErrorNode();
6580b57cec5SDimitry Andric if (!N)
6590b57cec5SDimitry Andric return;
660e8d8bef9SDimitry Andric initBugType(CheckKind);
661e8d8bef9SDimitry Andric auto Report =
662e8d8bef9SDimitry Andric std::make_unique<PathSensitiveBugReport>(*BT[CheckKind], Desc, N);
663e8d8bef9SDimitry Andric Report->addRange(MtxExpr->getSourceRange());
6640b57cec5SDimitry Andric C.emitReport(std::move(Report));
6650b57cec5SDimitry Andric }
6660b57cec5SDimitry Andric
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const6670b57cec5SDimitry Andric void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper,
6680b57cec5SDimitry Andric CheckerContext &C) const {
6690b57cec5SDimitry Andric ProgramStateRef State = C.getState();
6700b57cec5SDimitry Andric
6715ffd83dbSDimitry Andric for (auto I : State->get<DestroyRetVal>()) {
6725ffd83dbSDimitry Andric // Once the return value symbol dies, no more checks can be performed
6735ffd83dbSDimitry Andric // against it. See if the return value was checked before this point.
6745ffd83dbSDimitry Andric // This would remove the symbol from the map as well.
6755ffd83dbSDimitry Andric if (SymReaper.isDead(I.second))
6765ffd83dbSDimitry Andric State = resolvePossiblyDestroyedMutex(State, I.first, &I.second);
6770b57cec5SDimitry Andric }
6785ffd83dbSDimitry Andric
6795ffd83dbSDimitry Andric for (auto I : State->get<LockMap>()) {
6805ffd83dbSDimitry Andric // Stop tracking dead mutex regions as well.
681fe6060f1SDimitry Andric if (!SymReaper.isLiveRegion(I.first)) {
6825ffd83dbSDimitry Andric State = State->remove<LockMap>(I.first);
683fe6060f1SDimitry Andric State = State->remove<DestroyRetVal>(I.first);
684fe6060f1SDimitry Andric }
6855ffd83dbSDimitry Andric }
6865ffd83dbSDimitry Andric
6875ffd83dbSDimitry Andric // TODO: We probably need to clean up the lock stack as well.
6885ffd83dbSDimitry Andric // It is tricky though: even if the mutex cannot be unlocked anymore,
6895ffd83dbSDimitry Andric // it can still participate in lock order reversal resolution.
6905ffd83dbSDimitry Andric
6910b57cec5SDimitry Andric C.addTransition(State);
6920b57cec5SDimitry Andric }
6930b57cec5SDimitry Andric
checkRegionChanges(ProgramStateRef State,const InvalidatedSymbols * Symbols,ArrayRef<const MemRegion * > ExplicitRegions,ArrayRef<const MemRegion * > Regions,const LocationContext * LCtx,const CallEvent * Call) const6945ffd83dbSDimitry Andric ProgramStateRef PthreadLockChecker::checkRegionChanges(
6955ffd83dbSDimitry Andric ProgramStateRef State, const InvalidatedSymbols *Symbols,
6965ffd83dbSDimitry Andric ArrayRef<const MemRegion *> ExplicitRegions,
6975ffd83dbSDimitry Andric ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
6985ffd83dbSDimitry Andric const CallEvent *Call) const {
6995ffd83dbSDimitry Andric
7005ffd83dbSDimitry Andric bool IsLibraryFunction = false;
7015ffd83dbSDimitry Andric if (Call && Call->isGlobalCFunction()) {
7025ffd83dbSDimitry Andric // Avoid invalidating mutex state when a known supported function is called.
7035ffd83dbSDimitry Andric if (PThreadCallbacks.lookup(*Call) || FuchsiaCallbacks.lookup(*Call) ||
7045ffd83dbSDimitry Andric C11Callbacks.lookup(*Call))
7055ffd83dbSDimitry Andric return State;
7065ffd83dbSDimitry Andric
7075ffd83dbSDimitry Andric if (Call->isInSystemHeader())
7085ffd83dbSDimitry Andric IsLibraryFunction = true;
7095ffd83dbSDimitry Andric }
7105ffd83dbSDimitry Andric
7115ffd83dbSDimitry Andric for (auto R : Regions) {
7125ffd83dbSDimitry Andric // We assume that system library function wouldn't touch the mutex unless
7135ffd83dbSDimitry Andric // it takes the mutex explicitly as an argument.
7145ffd83dbSDimitry Andric // FIXME: This is a bit quadratic.
715349cc55cSDimitry Andric if (IsLibraryFunction && !llvm::is_contained(ExplicitRegions, R))
7165ffd83dbSDimitry Andric continue;
7175ffd83dbSDimitry Andric
7185ffd83dbSDimitry Andric State = State->remove<LockMap>(R);
7195ffd83dbSDimitry Andric State = State->remove<DestroyRetVal>(R);
7205ffd83dbSDimitry Andric
7215ffd83dbSDimitry Andric // TODO: We need to invalidate the lock stack as well. This is tricky
7225ffd83dbSDimitry Andric // to implement correctly and efficiently though, because the effects
7235ffd83dbSDimitry Andric // of mutex escapes on lock order may be fairly varied.
7245ffd83dbSDimitry Andric }
7255ffd83dbSDimitry Andric
7265ffd83dbSDimitry Andric return State;
7275ffd83dbSDimitry Andric }
7285ffd83dbSDimitry Andric
registerPthreadLockBase(CheckerManager & mgr)7295ffd83dbSDimitry Andric void ento::registerPthreadLockBase(CheckerManager &mgr) {
7300b57cec5SDimitry Andric mgr.registerChecker<PthreadLockChecker>();
7310b57cec5SDimitry Andric }
7320b57cec5SDimitry Andric
shouldRegisterPthreadLockBase(const CheckerManager & mgr)7335ffd83dbSDimitry Andric bool ento::shouldRegisterPthreadLockBase(const CheckerManager &mgr) { return true; }
7345ffd83dbSDimitry Andric
7355ffd83dbSDimitry Andric #define REGISTER_CHECKER(name) \
7365ffd83dbSDimitry Andric void ento::register##name(CheckerManager &mgr) { \
7375ffd83dbSDimitry Andric PthreadLockChecker *checker = mgr.getChecker<PthreadLockChecker>(); \
7385ffd83dbSDimitry Andric checker->ChecksEnabled[PthreadLockChecker::CK_##name] = true; \
7395ffd83dbSDimitry Andric checker->CheckNames[PthreadLockChecker::CK_##name] = \
7405ffd83dbSDimitry Andric mgr.getCurrentCheckerName(); \
7415ffd83dbSDimitry Andric } \
7425ffd83dbSDimitry Andric \
7435ffd83dbSDimitry Andric bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
7445ffd83dbSDimitry Andric
7455ffd83dbSDimitry Andric REGISTER_CHECKER(PthreadLockChecker)
7465ffd83dbSDimitry Andric REGISTER_CHECKER(FuchsiaLockChecker)
7475ffd83dbSDimitry Andric REGISTER_CHECKER(C11LockChecker)
748