[clang] f4c26d9 - [analyzer] Add FuchsiaLockChecker and C11LockChecker
Gabor Horvath via cfe-commits
cfe-commits at lists.llvm.org
Mon Jan 27 13:57:12 PST 2020
Author: Gabor Horvath
Date: 2020-01-27T13:55:56-08:00
New Revision: f4c26d993bdcb8e5250d774c0d2a6d91fee5ca00
URL: https://github.com/llvm/llvm-project/commit/f4c26d993bdcb8e5250d774c0d2a6d91fee5ca00
DIFF: https://github.com/llvm/llvm-project/commit/f4c26d993bdcb8e5250d774c0d2a6d91fee5ca00.diff
LOG: [analyzer] Add FuchsiaLockChecker and C11LockChecker
These are mostly trivial additions as both of them are reusing existing
PThreadLockChecker logic. I only needed to add the list of functions to
check and do some plumbing to make sure that we display the right
checker name in the diagnostic.
Differential Revision: https://reviews.llvm.org/D73376
Added:
clang/test/Analysis/c11lock.c
clang/test/Analysis/fuchsia_lock.c
Modified:
clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index fc1529f2ea1c..83d78e4f8994 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -227,6 +227,16 @@ def StackAddrAsyncEscapeChecker : Checker<"StackAddressAsyncEscape">,
Dependencies<[StackAddrEscapeBase]>,
Documentation<HasAlphaDocumentation>;
+def PthreadLockBase : Checker<"PthreadLockBase">,
+ HelpText<"Helper registering multiple checks.">,
+ Documentation<NotDocumented>,
+ Hidden;
+
+def C11LockChecker : Checker<"C11Lock">,
+ HelpText<"Simple lock -> unlock checker">,
+ Dependencies<[PthreadLockBase]>,
+ Documentation<HasAlphaDocumentation>;
+
} // end "alpha.core"
//===----------------------------------------------------------------------===//
@@ -431,6 +441,7 @@ def ChrootChecker : Checker<"Chroot">,
def PthreadLockChecker : Checker<"PthreadLock">,
HelpText<"Simple lock -> unlock checker">,
+ Dependencies<[PthreadLockBase]>,
Documentation<HasAlphaDocumentation>;
def StreamChecker : Checker<"Stream">,
@@ -1442,5 +1453,10 @@ def FuchsiaHandleChecker : Checker<"HandleChecker">,
HelpText<"A Checker that detect leaks related to Fuchsia handles">,
Documentation<HasDocumentation>;
+def FuchsiaLockChecker : Checker<"Lock">,
+ HelpText<"Check for the correct usage of locking APIs.">,
+ Dependencies<[PthreadLockBase]>,
+ Documentation<HasDocumentation>;
+
} // end fuchsia
diff --git a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index ed0fc0f98c2c..d3470dad0a43 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -6,8 +6,12 @@
//
//===----------------------------------------------------------------------===//
//
-// This defines PthreadLockChecker, a simple lock -> unlock checker.
-// Also handles XNU locks, which behave similarly enough to share code.
+// This file defines:
+// * PthreadLockChecker, a simple lock -> unlock checker.
+// Which also checks for XNU locks, which behave similarly enough to share
+// code.
+// * FuchsiaLocksChecker, which is also rather similar.
+// * C11LockChecker which also closely follows Pthread semantics.
//
//===----------------------------------------------------------------------===//
@@ -15,8 +19,8 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
using namespace clang;
using namespace ento;
@@ -46,9 +50,7 @@ struct LockState {
return LockState(UnlockedAndPossiblyDestroyed);
}
- bool operator==(const LockState &X) const {
- return K == X.K;
- }
+ bool operator==(const LockState &X) const { return K == X.K; }
bool isLocked() const { return K == Locked; }
bool isUnlocked() const { return K == Unlocked; }
@@ -60,98 +62,149 @@ struct LockState {
return K == UnlockedAndPossiblyDestroyed;
}
- void Profile(llvm::FoldingSetNodeID &ID) const {
- ID.AddInteger(K);
- }
+ void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
};
-class PthreadLockChecker
- : public Checker<check::PostCall, check::DeadSymbols,
- check::RegionChanges> {
- BugType BT_doublelock{this, "Double locking", "Lock checker"},
- BT_doubleunlock{this, "Double unlocking", "Lock checker"},
- BT_destroylock{this, "Use destroyed lock", "Lock checker"},
- BT_initlock{this, "Init invalid lock", "Lock checker"},
- BT_lor{this, "Lock order reversal", "Lock checker"};
-
- enum LockingSemantics {
- NotApplicable = 0,
- PthreadSemantics,
- XNUSemantics
+class PthreadLockChecker : public Checker<check::PostCall, check::DeadSymbols,
+ check::RegionChanges> {
+public:
+ enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics };
+ enum CheckerKind {
+ CK_PthreadLockChecker,
+ CK_FuchsiaLockChecker,
+ CK_C11LockChecker,
+ CK_NumCheckKinds
};
+ DefaultBool ChecksEnabled[CK_NumCheckKinds];
+ CheckerNameRef CheckNames[CK_NumCheckKinds];
+private:
typedef void (PthreadLockChecker::*FnCheck)(const CallEvent &Call,
- CheckerContext &C) const;
- CallDescriptionMap<FnCheck> Callbacks = {
- // Init.
- {{"pthread_mutex_init", 2}, &PthreadLockChecker::InitAnyLock},
- // TODO: pthread_rwlock_init(2 arguments).
- // TODO: lck_mtx_init(3 arguments).
- // TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex.
- // TODO: lck_rw_init(3 arguments).
- // TODO: lck_rw_alloc_init(2 arguments) => returns the mutex.
-
- // Acquire.
- {{"pthread_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
- {{"pthread_rwlock_rdlock", 1}, &PthreadLockChecker::AcquirePthreadLock},
- {{"pthread_rwlock_wrlock", 1}, &PthreadLockChecker::AcquirePthreadLock},
- {{"lck_mtx_lock", 1}, &PthreadLockChecker::AcquireXNULock},
- {{"lck_rw_lock_exclusive", 1}, &PthreadLockChecker::AcquireXNULock},
- {{"lck_rw_lock_shared", 1}, &PthreadLockChecker::AcquireXNULock},
-
- // Try.
- {{"pthread_mutex_trylock", 1}, &PthreadLockChecker::TryPthreadLock},
- {{"pthread_rwlock_tryrdlock", 1}, &PthreadLockChecker::TryPthreadLock},
- {{"pthread_rwlock_trywrlock", 1}, &PthreadLockChecker::TryPthreadLock},
- {{"lck_mtx_try_lock", 1}, &PthreadLockChecker::TryXNULock},
- {{"lck_rw_try_lock_exclusive", 1}, &PthreadLockChecker::TryXNULock},
- {{"lck_rw_try_lock_shared", 1}, &PthreadLockChecker::TryXNULock},
-
- // Release.
- {{"pthread_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
- {{"pthread_rwlock_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
- {{"lck_mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
- {{"lck_rw_unlock_exclusive", 1}, &PthreadLockChecker::ReleaseAnyLock},
- {{"lck_rw_unlock_shared", 1}, &PthreadLockChecker::ReleaseAnyLock},
- {{"lck_rw_done", 1}, &PthreadLockChecker::ReleaseAnyLock},
-
- // Destroy.
- {{"pthread_mutex_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock},
- {{"lck_mtx_destroy", 2}, &PthreadLockChecker::DestroyXNULock},
- // TODO: pthread_rwlock_destroy(1 argument).
- // TODO: lck_rw_destroy(2 arguments).
+ CheckerContext &C,
+ CheckerKind checkkind) const;
+ CallDescriptionMap<FnCheck> PThreadCallbacks = {
+ // Init.
+ {{"pthread_mutex_init", 2}, &PthreadLockChecker::InitAnyLock},
+ // TODO: pthread_rwlock_init(2 arguments).
+ // TODO: lck_mtx_init(3 arguments).
+ // TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex.
+ // TODO: lck_rw_init(3 arguments).
+ // TODO: lck_rw_alloc_init(2 arguments) => returns the mutex.
+
+ // Acquire.
+ {{"pthread_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+ {{"pthread_rwlock_rdlock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+ {{"pthread_rwlock_wrlock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+ {{"lck_mtx_lock", 1}, &PthreadLockChecker::AcquireXNULock},
+ {{"lck_rw_lock_exclusive", 1}, &PthreadLockChecker::AcquireXNULock},
+ {{"lck_rw_lock_shared", 1}, &PthreadLockChecker::AcquireXNULock},
+
+ // Try.
+ {{"pthread_mutex_trylock", 1}, &PthreadLockChecker::TryPthreadLock},
+ {{"pthread_rwlock_tryrdlock", 1}, &PthreadLockChecker::TryPthreadLock},
+ {{"pthread_rwlock_trywrlock", 1}, &PthreadLockChecker::TryPthreadLock},
+ {{"lck_mtx_try_lock", 1}, &PthreadLockChecker::TryXNULock},
+ {{"lck_rw_try_lock_exclusive", 1}, &PthreadLockChecker::TryXNULock},
+ {{"lck_rw_try_lock_shared", 1}, &PthreadLockChecker::TryXNULock},
+
+ // Release.
+ {{"pthread_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{"pthread_rwlock_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{"lck_mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{"lck_rw_unlock_exclusive", 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{"lck_rw_unlock_shared", 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{"lck_rw_done", 1}, &PthreadLockChecker::ReleaseAnyLock},
+
+ // Destroy.
+ {{"pthread_mutex_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock},
+ {{"lck_mtx_destroy", 2}, &PthreadLockChecker::DestroyXNULock},
+ // TODO: pthread_rwlock_destroy(1 argument).
+ // TODO: lck_rw_destroy(2 arguments).
+ };
+
+ CallDescriptionMap<FnCheck> FuchsiaCallbacks = {
+ // Init.
+ {{"spin_lock_init", 1}, &PthreadLockChecker::InitAnyLock},
+
+ // Acquire.
+ {{"spin_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+ {{"spin_lock_save", 3}, &PthreadLockChecker::AcquirePthreadLock},
+ {{"sync_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+ {{"sync_mutex_lock_with_waiter", 1},
+ &PthreadLockChecker::AcquirePthreadLock},
+
+ // Try.
+ {{"spin_trylock", 1}, &PthreadLockChecker::TryFuchsiaLock},
+ {{"sync_mutex_trylock", 1}, &PthreadLockChecker::TryFuchsiaLock},
+ {{"sync_mutex_timedlock", 2}, &PthreadLockChecker::TryFuchsiaLock},
+
+ // Release.
+ {{"spin_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{"spin_unlock_restore", 3}, &PthreadLockChecker::ReleaseAnyLock},
+ {{"sync_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+ };
+
+ CallDescriptionMap<FnCheck> C11Callbacks = {
+ // Init.
+ {{"mtx_init", 2}, &PthreadLockChecker::InitAnyLock},
+
+ // Acquire.
+ {{"mtx_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+
+ // Try.
+ {{"mtx_trylock", 1}, &PthreadLockChecker::TryC11Lock},
+ {{"mtx_timedlock", 2}, &PthreadLockChecker::TryC11Lock},
+
+ // Release.
+ {{"mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+
+ // Destroy
+ {{"mtx_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock},
};
ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state,
const MemRegion *lockR,
const SymbolRef *sym) const;
void reportUseDestroyedBug(const CallEvent &Call, CheckerContext &C,
- unsigned ArgNo) const;
+ unsigned ArgNo, CheckerKind checkKind) const;
// Init.
- void InitAnyLock(const CallEvent &Call, CheckerContext &C) const;
+ void InitAnyLock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
void InitLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
- SVal Lock) const;
+ SVal Lock, CheckerKind checkkind) const;
// Lock, Try-lock.
- void AcquirePthreadLock(const CallEvent &Call, CheckerContext &C) const;
- void AcquireXNULock(const CallEvent &Call, CheckerContext &C) const;
- void TryPthreadLock(const CallEvent &Call, CheckerContext &C) const;
- void TryXNULock(const CallEvent &Call, CheckerContext &C) const;
+ void AcquirePthreadLock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
+ void AcquireXNULock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
+ void TryPthreadLock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
+ void TryXNULock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
+ void TryFuchsiaLock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
+ void TryC11Lock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
void AcquireLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
- SVal lock, bool isTryLock,
- enum LockingSemantics semantics) const;
+ SVal lock, bool isTryLock, LockingSemantics semantics,
+ CheckerKind checkkind) const;
// Release.
- void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C) const;
+ void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
void ReleaseLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
- SVal lock) const;
+ SVal lock, CheckerKind checkkind) const;
// Destroy.
- void DestroyPthreadLock(const CallEvent &Call, CheckerContext &C) const;
- void DestroyXNULock(const CallEvent &Call, CheckerContext &C) const;
+ void DestroyPthreadLock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
+ void DestroyXNULock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
void DestroyLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
- SVal Lock, enum LockingSemantics semantics) const;
+ SVal Lock, LockingSemantics semantics,
+ CheckerKind checkkind) const;
public:
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
@@ -161,8 +214,30 @@ class PthreadLockChecker
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
const LocationContext *LCtx, const CallEvent *Call) const;
- void printState(raw_ostream &Out, ProgramStateRef State,
- const char *NL, const char *Sep) const override;
+ void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
+ const char *Sep) const override;
+
+private:
+ mutable std::unique_ptr<BugType> BT_doublelock[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_doubleunlock[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_destroylock[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_initlock[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_lor[CK_NumCheckKinds];
+
+ void initBugType(CheckerKind checkKind) const {
+ if (BT_doublelock[checkKind])
+ return;
+ BT_doublelock[checkKind].reset(
+ new BugType{CheckNames[checkKind], "Double locking", "Lock checker"});
+ BT_doubleunlock[checkKind].reset(
+ new BugType{CheckNames[checkKind], "Double unlocking", "Lock checker"});
+ BT_destroylock[checkKind].reset(new BugType{
+ CheckNames[checkKind], "Use destroyed lock", "Lock checker"});
+ BT_initlock[checkKind].reset(new BugType{
+ CheckNames[checkKind], "Init invalid lock", "Lock checker"});
+ BT_lor[checkKind].reset(new BugType{CheckNames[checkKind],
+ "Lock order reversal", "Lock checker"});
+ }
};
} // end anonymous namespace
@@ -184,11 +259,14 @@ void PthreadLockChecker::checkPostCall(const CallEvent &Call,
if (!Call.isGlobalCFunction())
return;
- if (const FnCheck *Callback = Callbacks.lookup(Call))
- (this->**Callback)(Call, C);
+ if (const FnCheck *Callback = PThreadCallbacks.lookup(Call))
+ (this->**Callback)(Call, C, CK_PthreadLockChecker);
+ else if (const FnCheck *Callback = FuchsiaCallbacks.lookup(Call))
+ (this->**Callback)(Call, C, CK_FuchsiaLockChecker);
+ else if (const FnCheck *Callback = C11Callbacks.lookup(Call))
+ (this->**Callback)(Call, C, CK_C11LockChecker);
}
-
// When a lock is destroyed, in some semantics(like PthreadSemantics) we are not
// sure if the destroy call has succeeded or failed, and the lock enters one of
// the 'possibly destroyed' state. There is a short time frame for the
@@ -248,7 +326,7 @@ void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State,
LockSetTy LS = State->get<LockSet>();
if (!LS.isEmpty()) {
Out << Sep << "Mutex lock order:" << NL;
- for (auto I: LS) {
+ for (auto I : LS) {
I->dumpToStream(Out);
Out << NL;
}
@@ -258,29 +336,52 @@ void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State,
}
void PthreadLockChecker::AcquirePthreadLock(const CallEvent &Call,
- CheckerContext &C) const {
- AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, PthreadSemantics);
+ CheckerContext &C,
+ CheckerKind checkKind) const {
+ AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, PthreadSemantics,
+ checkKind);
}
void PthreadLockChecker::AcquireXNULock(const CallEvent &Call,
- CheckerContext &C) const {
- AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, XNUSemantics);
+ CheckerContext &C,
+ CheckerKind checkKind) const {
+ AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, XNUSemantics,
+ checkKind);
}
void PthreadLockChecker::TryPthreadLock(const CallEvent &Call,
- CheckerContext &C) const {
- AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics);
+ CheckerContext &C,
+ CheckerKind checkKind) const {
+ AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
+ checkKind);
+}
+
+void PthreadLockChecker::TryXNULock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkKind) const {
+ AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
+ checkKind);
+}
+
+void PthreadLockChecker::TryFuchsiaLock(const CallEvent &Call,
+ CheckerContext &C,
+ CheckerKind checkKind) const {
+ AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
+ checkKind);
}
-void PthreadLockChecker::TryXNULock(const CallEvent &Call,
- CheckerContext &C) const {
- AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics);
+void PthreadLockChecker::TryC11Lock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkKind) const {
+ AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
+ checkKind);
}
void PthreadLockChecker::AcquireLockAux(const CallEvent &Call,
CheckerContext &C, unsigned ArgNo,
SVal lock, bool isTryLock,
- enum LockingSemantics semantics) const {
+ enum LockingSemantics semantics,
+ CheckerKind checkKind) const {
+ if (!ChecksEnabled[checkKind])
+ return;
const MemRegion *lockR = lock.getAsRegion();
if (!lockR)
@@ -296,13 +397,14 @@ void PthreadLockChecker::AcquireLockAux(const CallEvent &Call,
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
+ initBugType(checkKind);
auto report = std::make_unique<PathSensitiveBugReport>(
- BT_doublelock, "This lock has already been acquired", N);
+ *BT_doublelock[checkKind], "This lock has already been acquired", N);
report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
C.emitReport(std::move(report));
return;
} else if (LState->isDestroyed()) {
- reportUseDestroyedBug(Call, C, ArgNo);
+ reportUseDestroyedBug(Call, C, ArgNo, checkKind);
return;
}
}
@@ -352,13 +454,17 @@ void PthreadLockChecker::AcquireLockAux(const CallEvent &Call,
}
void PthreadLockChecker::ReleaseAnyLock(const CallEvent &Call,
- CheckerContext &C) const {
- ReleaseLockAux(Call, C, 0, Call.getArgSVal(0));
+ CheckerContext &C,
+ CheckerKind checkKind) const {
+ ReleaseLockAux(Call, C, 0, Call.getArgSVal(0), checkKind);
}
void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call,
CheckerContext &C, unsigned ArgNo,
- SVal lock) const {
+ SVal lock,
+ CheckerKind checkKind) const {
+ if (!ChecksEnabled[checkKind])
+ return;
const MemRegion *lockR = lock.getAsRegion();
if (!lockR)
@@ -374,13 +480,15 @@ void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call,
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
+ initBugType(checkKind);
auto Report = std::make_unique<PathSensitiveBugReport>(
- BT_doubleunlock, "This lock has already been unlocked", N);
+ *BT_doubleunlock[checkKind], "This lock has already been unlocked",
+ N);
Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
C.emitReport(std::move(Report));
return;
} else if (LState->isDestroyed()) {
- reportUseDestroyedBug(Call, C, ArgNo);
+ reportUseDestroyedBug(Call, C, ArgNo, checkKind);
return;
}
}
@@ -393,9 +501,12 @@ void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call,
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
+ initBugType(checkKind);
auto report = std::make_unique<PathSensitiveBugReport>(
- BT_lor, "This was not the most recently acquired lock. Possible "
- "lock order reversal", N);
+ *BT_lor[checkKind],
+ "This was not the most recently acquired lock. Possible "
+ "lock order reversal",
+ N);
report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
C.emitReport(std::move(report));
return;
@@ -409,19 +520,24 @@ void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call,
}
void PthreadLockChecker::DestroyPthreadLock(const CallEvent &Call,
- CheckerContext &C) const {
- DestroyLockAux(Call, C, 0, Call.getArgSVal(0), PthreadSemantics);
+ CheckerContext &C,
+ CheckerKind checkKind) const {
+ DestroyLockAux(Call, C, 0, Call.getArgSVal(0), PthreadSemantics, checkKind);
}
void PthreadLockChecker::DestroyXNULock(const CallEvent &Call,
- CheckerContext &C) const {
- DestroyLockAux(Call, C, 0, Call.getArgSVal(0), XNUSemantics);
+ CheckerContext &C,
+ CheckerKind checkKind) const {
+ DestroyLockAux(Call, C, 0, Call.getArgSVal(0), XNUSemantics, checkKind);
}
void PthreadLockChecker::DestroyLockAux(const CallEvent &Call,
CheckerContext &C, unsigned ArgNo,
SVal Lock,
- enum LockingSemantics semantics) const {
+ enum LockingSemantics semantics,
+ CheckerKind checkKind) const {
+ if (!ChecksEnabled[checkKind])
+ return;
const MemRegion *LockR = Lock.getAsRegion();
if (!LockR)
@@ -472,19 +588,23 @@ void PthreadLockChecker::DestroyLockAux(const CallEvent &Call,
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
- auto Report =
- std::make_unique<PathSensitiveBugReport>(BT_destroylock, Message, N);
+ initBugType(checkKind);
+ auto Report = std::make_unique<PathSensitiveBugReport>(
+ *BT_destroylock[checkKind], Message, N);
Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
C.emitReport(std::move(Report));
}
-void PthreadLockChecker::InitAnyLock(const CallEvent &Call,
- CheckerContext &C) const {
- InitLockAux(Call, C, 0, Call.getArgSVal(0));
+void PthreadLockChecker::InitAnyLock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkKind) const {
+ InitLockAux(Call, C, 0, Call.getArgSVal(0), checkKind);
}
void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C,
- unsigned ArgNo, SVal Lock) const {
+ unsigned ArgNo, SVal Lock,
+ CheckerKind checkKind) const {
+ if (!ChecksEnabled[checkKind])
+ return;
const MemRegion *LockR = Lock.getAsRegion();
if (!LockR)
@@ -514,20 +634,23 @@ void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C,
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
- auto Report =
- std::make_unique<PathSensitiveBugReport>(BT_initlock, Message, N);
+ initBugType(checkKind);
+ auto Report = std::make_unique<PathSensitiveBugReport>(
+ *BT_initlock[checkKind], Message, N);
Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
C.emitReport(std::move(Report));
}
void PthreadLockChecker::reportUseDestroyedBug(const CallEvent &Call,
CheckerContext &C,
- unsigned ArgNo) const {
+ unsigned ArgNo,
+ CheckerKind checkKind) const {
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
+ initBugType(checkKind);
auto Report = std::make_unique<PathSensitiveBugReport>(
- BT_destroylock, "This lock has already been destroyed", N);
+ *BT_destroylock[checkKind], "This lock has already been destroyed", N);
Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
C.emitReport(std::move(Report));
}
@@ -566,8 +689,9 @@ ProgramStateRef PthreadLockChecker::checkRegionChanges(
bool IsLibraryFunction = false;
if (Call && Call->isGlobalCFunction()) {
// Avoid invalidating mutex state when a known supported function is called.
- if (Callbacks.lookup(*Call))
- return State;
+ if (PThreadCallbacks.lookup(*Call) || FuchsiaCallbacks.lookup(*Call) ||
+ C11Callbacks.lookup(*Call))
+ return State;
if (Call->isInSystemHeader())
IsLibraryFunction = true;
@@ -593,10 +717,22 @@ ProgramStateRef PthreadLockChecker::checkRegionChanges(
return State;
}
-void ento::registerPthreadLockChecker(CheckerManager &mgr) {
+void ento::registerPthreadLockBase(CheckerManager &mgr) {
mgr.registerChecker<PthreadLockChecker>();
}
-bool ento::shouldRegisterPthreadLockChecker(const LangOptions &LO) {
- return true;
-}
+bool ento::shouldRegisterPthreadLockBase(const LangOptions &LO) { return true; }
+
+#define REGISTER_CHECKER(name) \
+ void ento::register##name(CheckerManager &mgr) { \
+ PthreadLockChecker *checker = mgr.getChecker<PthreadLockChecker>(); \
+ checker->ChecksEnabled[PthreadLockChecker::CK_##name] = true; \
+ checker->CheckNames[PthreadLockChecker::CK_##name] = \
+ mgr.getCurrentCheckerName(); \
+ } \
+ \
+ bool ento::shouldRegister##name(const LangOptions &LO) { return true; }
+
+REGISTER_CHECKER(PthreadLockChecker)
+REGISTER_CHECKER(FuchsiaLockChecker)
+REGISTER_CHECKER(C11LockChecker)
diff --git a/clang/test/Analysis/c11lock.c b/clang/test/Analysis/c11lock.c
new file mode 100644
index 000000000000..78e62982fea1
--- /dev/null
+++ b/clang/test/Analysis/c11lock.c
@@ -0,0 +1,90 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.C11Lock -verify %s
+
+typedef int mtx_t;
+struct timespec;
+
+enum {
+ // FIXME: The value if this enum is implementation defined. While all the
+ // implementations I am aware of using 0, the right solution would be to
+ // look this value up in the AST (and disable the check if it is not found).
+ thrd_success = 0,
+ thrd_error = 2
+};
+
+int mtx_init(mtx_t *mutex, int type);
+int mtx_lock(mtx_t *mutex);
+int mtx_timedlock(mtx_t *mutex,
+ const struct timespec *time_point);
+int mtx_trylock(mtx_t *mutex);
+int mtx_unlock(mtx_t *mutex);
+int mtx_destroy(mtx_t *mutex);
+
+mtx_t mtx1;
+mtx_t mtx2;
+
+void bad1(void)
+{
+ mtx_lock(&mtx1); // no-warning
+ mtx_lock(&mtx1); // expected-warning{{This lock has already been acquired}}
+}
+
+void bad2(void) {
+ mtx_t mtx;
+ mtx_init(&mtx, 0);
+ mtx_lock(&mtx);
+} // TODO: Warn for missing unlock?
+
+void bad3(void) {
+ mtx_t mtx;
+ mtx_init(&mtx, 0);
+} // TODO: Warn for missing destroy?
+
+void bad4(void) {
+ mtx_t mtx;
+ mtx_init(&mtx, 0);
+ mtx_lock(&mtx);
+ mtx_unlock(&mtx);
+} // TODO: warn for missing destroy?
+
+void bad5(void) {
+ mtx_lock(&mtx1);
+ mtx_unlock(&mtx1);
+ mtx_unlock(&mtx1); // expected-warning {{This lock has already been unlocked}}
+}
+
+void bad6() {
+ mtx_init(&mtx1, 0);
+ if (mtx_trylock(&mtx1) != thrd_success)
+ mtx_unlock(&mtx1); // expected-warning {{This lock has already been unlocked}}
+}
+
+void bad7(void) {
+ mtx_lock(&mtx1);
+ mtx_lock(&mtx2);
+ mtx_unlock(&mtx1); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}}
+ mtx_unlock(&mtx2);
+}
+
+void good() {
+ mtx_t mtx;
+ mtx_init(&mtx, 0);
+ mtx_lock(&mtx);
+ mtx_unlock(&mtx);
+ mtx_destroy(&mtx);
+}
+
+void good2() {
+ mtx_t mtx;
+ mtx_init(&mtx, 0);
+ if (mtx_trylock(&mtx) == thrd_success)
+ mtx_unlock(&mtx);
+ mtx_destroy(&mtx);
+}
+
+void good3() {
+ mtx_t mtx;
+ mtx_init(&mtx, 0);
+ if (mtx_timedlock(&mtx, 0) == thrd_success)
+ mtx_unlock(&mtx);
+ mtx_destroy(&mtx);
+}
diff --git a/clang/test/Analysis/fuchsia_lock.c b/clang/test/Analysis/fuchsia_lock.c
new file mode 100644
index 000000000000..9f2a7ec09fa3
--- /dev/null
+++ b/clang/test/Analysis/fuchsia_lock.c
@@ -0,0 +1,104 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=fuchsia.Lock -verify %s
+
+typedef int spin_lock_t;
+typedef int zx_status_t;
+typedef int zx_time_t;
+
+void spin_lock(spin_lock_t *lock);
+int spin_trylock(spin_lock_t *lock);
+void spin_unlock(spin_lock_t *lock);
+void spin_lock_init(spin_lock_t *lock);
+
+void spin_lock_save(spin_lock_t *lock, void *statep,
+ int flags);
+void spin_unlock_restore(spin_lock_t *lock, void *old_state,
+ int flags);
+
+spin_lock_t mtx1;
+spin_lock_t mtx2;
+
+void bad1(void)
+{
+ spin_lock(&mtx1); // no-warning
+ spin_lock(&mtx1); // expected-warning{{This lock has already been acquired}}
+}
+
+void bad2(void) {
+ spin_lock(&mtx1);
+ spin_unlock(&mtx1);
+ spin_unlock(&mtx1); // expected-warning {{This lock has already been unlocked}}
+}
+
+void bad3() {
+ spin_lock_init(&mtx1);
+ if (spin_trylock(&mtx1) != 0)
+ spin_unlock(&mtx1); // expected-warning {{This lock has already been unlocked}}
+}
+
+void bad4(void) {
+ spin_lock(&mtx1);
+ spin_lock(&mtx2);
+ spin_unlock(&mtx1); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}}
+ spin_unlock(&mtx2);
+}
+
+void good() {
+ spin_lock_t mtx;
+ spin_lock_init(&mtx);
+ spin_lock_save(&mtx, 0, 0);
+ spin_unlock_restore(&mtx, 0, 0);
+}
+
+void good2() {
+ spin_lock_t mtx;
+ spin_lock_init(&mtx);
+ if (spin_trylock(&mtx) == 0)
+ spin_unlock(&mtx);
+}
+
+typedef int sync_mutex_t;
+void sync_mutex_lock(sync_mutex_t* mutex);
+void sync_mutex_lock_with_waiter(sync_mutex_t* mutex);
+zx_status_t sync_mutex_timedlock(sync_mutex_t* mutex, zx_time_t deadline);
+zx_status_t sync_mutex_trylock(sync_mutex_t* mutex);
+void sync_mutex_unlock(sync_mutex_t* mutex);
+
+sync_mutex_t smtx1;
+sync_mutex_t smtx2;
+
+void bad11(void)
+{
+ sync_mutex_lock(&smtx1); // no-warning
+ sync_mutex_lock(&smtx1); // expected-warning{{This lock has already been acquired}}
+}
+
+void bad12(void) {
+ sync_mutex_lock_with_waiter(&smtx1);
+ sync_mutex_unlock(&smtx1);
+ sync_mutex_unlock(&smtx1); // expected-warning {{This lock has already been unlocked}}
+}
+
+void bad13() {
+ sync_mutex_unlock(&smtx1);
+ if (sync_mutex_trylock(&smtx1) != 0)
+ sync_mutex_unlock(&smtx1); // expected-warning {{This lock has already been unlocked}}
+}
+
+void bad14(void) {
+ sync_mutex_lock(&smtx1);
+ sync_mutex_lock(&smtx2);
+ sync_mutex_unlock(&smtx1); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}}
+ sync_mutex_unlock(&smtx2);
+}
+
+void good11() {
+ sync_mutex_t mtx;
+ if (sync_mutex_trylock(&mtx) == 0)
+ sync_mutex_unlock(&mtx);
+}
+
+void good12() {
+ sync_mutex_t mtx;
+ if (sync_mutex_timedlock(&mtx, 0) == 0)
+ sync_mutex_unlock(&mtx);
+}
More information about the cfe-commits
mailing list