r316892 - [analyzer] lock_guard and unique_lock extension for BlockInCriticalSection checker
Gabor Horvath via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 30 03:09:55 PDT 2017
Author: xazax
Date: Mon Oct 30 03:09:55 2017
New Revision: 316892
URL: http://llvm.org/viewvc/llvm-project?rev=316892&view=rev
Log:
[analyzer] lock_guard and unique_lock extension for BlockInCriticalSection checker
A patch by zdtorok (Zoltán Dániel Török)!
Differential Revision: https://reviews.llvm.org/D33729
Modified:
cfe/trunk/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
cfe/trunk/test/Analysis/block-in-critical-section.cpp
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp?rev=316892&r1=316891&r2=316892&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp Mon Oct 30 03:09:55 2017
@@ -26,15 +26,22 @@ using namespace ento;
namespace {
-class BlockInCriticalSectionChecker : public Checker<check::PostCall,
- check::PreCall> {
+class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
+
+ mutable IdentifierInfo *IILockGuard, *IIUniqueLock;
CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn,
PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn,
MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock;
+ StringRef ClassLockGuard, ClassUniqueLock;
+
+ mutable bool IdentifierInfoInitialized;
+
std::unique_ptr<BugType> BlockInCritSectionBugType;
+ void initIdentifierInfo(ASTContext &Ctx) const;
+
void reportBlockInCritSection(SymbolRef FileDescSym,
const CallEvent &call,
CheckerContext &C) const;
@@ -46,13 +53,10 @@ public:
bool isLockFunction(const CallEvent &Call) const;
bool isUnlockFunction(const CallEvent &Call) const;
- void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
-
/// Process unlock.
/// Process lock.
/// Process blocking functions (sleep, getc, fgets, read, recv)
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
-
};
} // end anonymous namespace
@@ -60,7 +64,8 @@ public:
REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
- : LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"),
+ : IILockGuard(nullptr), IIUniqueLock(nullptr),
+ LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"),
FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"),
PthreadLockFn("pthread_mutex_lock"),
PthreadTryLockFn("pthread_mutex_trylock"),
@@ -68,13 +73,29 @@ BlockInCriticalSectionChecker::BlockInCr
MtxLock("mtx_lock"),
MtxTimedLock("mtx_timedlock"),
MtxTryLock("mtx_trylock"),
- MtxUnlock("mtx_unlock") {
+ MtxUnlock("mtx_unlock"),
+ ClassLockGuard("lock_guard"),
+ ClassUniqueLock("unique_lock"),
+ IdentifierInfoInitialized(false) {
// Initialize the bug type.
BlockInCritSectionBugType.reset(
new BugType(this, "Call to blocking function in critical section",
"Blocking Error"));
}
+void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const {
+ if (!IdentifierInfoInitialized) {
+ /* In case of checking C code, or when the corresponding headers are not
+ * included, we might end up query the identifier table every time when this
+ * function is called instead of early returning it. To avoid this, a bool
+ * variable (IdentifierInfoInitialized) is used and the function will be run
+ * only once. */
+ IILockGuard = &Ctx.Idents.get(ClassLockGuard);
+ IIUniqueLock = &Ctx.Idents.get(ClassUniqueLock);
+ IdentifierInfoInitialized = true;
+ }
+}
+
bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const {
if (Call.isCalled(SleepFn)
|| Call.isCalled(GetcFn)
@@ -87,6 +108,12 @@ bool BlockInCriticalSectionChecker::isBl
}
bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const {
+ if (const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
+ auto IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier();
+ if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
+ return true;
+ }
+
if (Call.isCalled(LockFn)
|| Call.isCalled(PthreadLockFn)
|| Call.isCalled(PthreadTryLockFn)
@@ -99,6 +126,13 @@ bool BlockInCriticalSectionChecker::isLo
}
bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
+ if (const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
+ const auto *DRecordDecl = dyn_cast<CXXRecordDecl>(Dtor->getDecl()->getParent());
+ auto IdentifierInfo = DRecordDecl->getIdentifier();
+ if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
+ return true;
+ }
+
if (Call.isCalled(UnlockFn)
|| Call.isCalled(PthreadUnlockFn)
|| Call.isCalled(MtxUnlock)) {
@@ -107,12 +141,10 @@ bool BlockInCriticalSectionChecker::isUn
return false;
}
-void BlockInCriticalSectionChecker::checkPreCall(const CallEvent &Call,
- CheckerContext &C) const {
-}
-
void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
+ initIdentifierInfo(C.getASTContext());
+
if (!isBlockingFunction(Call)
&& !isLockFunction(Call)
&& !isUnlockFunction(Call))
Modified: cfe/trunk/test/Analysis/block-in-critical-section.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/block-in-critical-section.cpp?rev=316892&r1=316891&r2=316892&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/block-in-critical-section.cpp (original)
+++ cfe/trunk/test/Analysis/block-in-critical-section.cpp Mon Oct 30 03:09:55 2017
@@ -7,6 +7,20 @@ struct mutex {
void lock() {}
void unlock() {}
};
+template<typename T>
+struct lock_guard {
+ lock_guard<T>(std::mutex) {}
+ ~lock_guard<T>() {}
+};
+template<typename T>
+struct unique_lock {
+ unique_lock<T>(std::mutex) {}
+ ~unique_lock<T>() {}
+};
+template<typename T>
+struct not_real_lock {
+ not_real_lock<T>(std::mutex) {}
+};
}
void getc() {}
@@ -110,3 +124,31 @@ void testBlockInCriticalSectionUnexpecte
m.lock();
sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
}
+
+void testBlockInCriticalSectionLockGuard() {
+ std::mutex g_mutex;
+ std::not_real_lock<std::mutex> not_real_lock(g_mutex);
+ sleep(1); // no-warning
+
+ std::lock_guard<std::mutex> lock(g_mutex);
+ sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+}
+
+void testBlockInCriticalSectionLockGuardNested() {
+ testBlockInCriticalSectionLockGuard();
+ sleep(1); // no-warning
+}
+
+void testBlockInCriticalSectionUniqueLock() {
+ std::mutex g_mutex;
+ std::not_real_lock<std::mutex> not_real_lock(g_mutex);
+ sleep(1); // no-warning
+
+ std::unique_lock<std::mutex> lock(g_mutex);
+ sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+}
+
+void testBlockInCriticalSectionUniqueLockNested() {
+ testBlockInCriticalSectionUniqueLock();
+ sleep(1); // no-warning
+}
More information about the cfe-commits
mailing list