r282011 - [analyzer] Add a checker that detects blocks in critical sections

Anna Zaks via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 20 13:28:50 PDT 2016


Author: zaks
Date: Tue Sep 20 15:28:50 2016
New Revision: 282011

URL: http://llvm.org/viewvc/llvm-project?rev=282011&view=rev
Log:
[analyzer] Add a checker that detects blocks in critical sections

This checker should find the calls to blocking functions (for example: sleep, getc, fgets,read,recv etc.) inside a critical section. When sleep(x) is called while a mutex is held, other threads cannot lock the same mutex. This might take some time, leading to bad performance or even deadlock.

Example:

mutex_t m;

void f() {
  sleep(1000); // Error: sleep() while m is locked! [f() is called from foobar() while m is locked]
  // do some work
}

void foobar() {
  lock(m);
  f();
  unlock(m);
}

A patch by zdtorok (Zoltán Dániel Török)!

Differential Revision: https://reviews.llvm.org/D21506

Added:
    cfe/trunk/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
    cfe/trunk/test/Analysis/block-in-critical-section.cpp
Modified:
    cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td
    cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt

Modified: cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td?rev=282011&r1=282010&r2=282011&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td Tue Sep 20 15:28:50 2016
@@ -436,6 +436,10 @@ def SimpleStreamChecker : Checker<"Simpl
   HelpText<"Check for misuses of stream APIs">,
   DescFile<"SimpleStreamChecker.cpp">;
 
+def BlockInCriticalSectionChecker : Checker<"BlockInCriticalSection">,
+  HelpText<"Check for calls to blocking functions inside a critical section">,
+  DescFile<"BlockInCriticalSectionChecker.cpp">;
+
 } // end "alpha.unix"
 
 let ParentPackage = CString in {

Added: cfe/trunk/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp?rev=282011&view=auto
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp (added)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp Tue Sep 20 15:28:50 2016
@@ -0,0 +1,109 @@
+//===-- BlockInCriticalSectionChecker.cpp -----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines a checker for blocks in critical sections. This checker should find
+// the calls to blocking functions (for example: sleep, getc, fgets, read,
+// recv etc.) inside a critical section. When sleep(x) is called while a mutex
+// is held, other threades cannot lock the same mutex. This might take some
+// time, leading to bad performance or even deadlock.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class BlockInCriticalSectionChecker : public Checker<check::PostCall,
+                                                     check::PreCall> {
+
+  CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn;
+
+  std::unique_ptr<BugType> BlockInCritSectionBugType;
+
+  void reportBlockInCritSection(SymbolRef FileDescSym,
+                                const CallEvent &call,
+                                CheckerContext &C) const;
+
+public:
+  BlockInCriticalSectionChecker();
+
+  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
+
+REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
+
+BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
+    : LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"),
+      FgetsFn("fgets"), ReadFn("read"), RecvFn("recv") {
+  // Initialize the bug type.
+  BlockInCritSectionBugType.reset(
+      new BugType(this, "Call to blocking function in critical section",
+                        "Blocking Error"));
+}
+
+void BlockInCriticalSectionChecker::checkPreCall(const CallEvent &Call,
+                                                 CheckerContext &C) const {
+}
+
+void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
+                                                  CheckerContext &C) const {
+  if (!Call.isCalled(LockFn)
+      && !Call.isCalled(SleepFn)
+      && !Call.isCalled(GetcFn)
+      && !Call.isCalled(FgetsFn)
+      && !Call.isCalled(ReadFn)
+      && !Call.isCalled(RecvFn)
+      && !Call.isCalled(UnlockFn))
+    return;
+
+  ProgramStateRef State = C.getState();
+  unsigned mutexCount = State->get<MutexCounter>();
+  if (Call.isCalled(UnlockFn) && mutexCount > 0) {
+    State = State->set<MutexCounter>(--mutexCount);
+    C.addTransition(State);
+  } else if (Call.isCalled(LockFn)) {
+    State = State->set<MutexCounter>(++mutexCount);
+    C.addTransition(State);
+  } else if (mutexCount > 0) {
+    SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol();
+    reportBlockInCritSection(BlockDesc, Call, C);
+  }
+}
+
+void BlockInCriticalSectionChecker::reportBlockInCritSection(
+    SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
+  ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
+  if (!ErrNode)
+    return;
+
+  auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType,
+      "A blocking function %s is called inside a critical section.", ErrNode);
+  R->addRange(Call.getSourceRange());
+  R->markInteresting(BlockDescSym);
+  C.emitReport(std::move(R));
+}
+
+void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
+  mgr.registerChecker<BlockInCriticalSectionChecker>();
+}

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt?rev=282011&r1=282010&r2=282011&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt Tue Sep 20 15:28:50 2016
@@ -9,6 +9,7 @@ add_clang_library(clangStaticAnalyzerChe
   ArrayBoundChecker.cpp
   ArrayBoundCheckerV2.cpp
   BasicObjCFoundationChecks.cpp
+  BlockInCriticalSectionChecker.cpp
   BoolAssignmentChecker.cpp
   BuiltinFunctionChecker.cpp
   CStringChecker.cpp

Added: 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=282011&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/block-in-critical-section.cpp (added)
+++ cfe/trunk/test/Analysis/block-in-critical-section.cpp Tue Sep 20 15:28:50 2016
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.unix.BlockInCriticalSection -std=c++11 -verify %s
+
+void sleep(int x) {}
+
+namespace std {
+struct mutex {
+  void lock() {}
+  void unlock() {}
+};
+}
+
+void testBlockInCriticalSection() {
+  std::mutex m;
+  m.lock();
+  sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}}
+  m.unlock();
+}
+
+void testBlockInCriticalSectionWithNestedMutexes() {
+  std::mutex m, n, k;
+  m.lock();
+  n.lock();
+  k.lock();
+  sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}}
+  k.unlock();
+  sleep(5); // expected-warning {{A blocking function %s is called inside a critical section}}
+  n.unlock();
+  sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}}
+  m.unlock();
+  sleep(3); // no-warning
+}
+
+void f() {
+  sleep(1000); // expected-warning {{A blocking function %s is called inside a critical section}}
+}
+
+void testBlockInCriticalSectionInterProcedural() {
+  std::mutex m;
+  m.lock();
+  f();
+  m.unlock();
+}
+
+void testBlockInCriticalSectionUnexpectedUnlock() {
+  std::mutex m;
+  m.unlock();
+  sleep(1); // no-warning
+  m.lock();
+  sleep(1); // expected-warning {{A blocking function %s is called inside a critical section}}
+}




More information about the cfe-commits mailing list