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