zdtorok created this revision.
zdtorok added reviewers: zaks.anna, dcoughlin, xazax.hun.
zdtorok added a subscriber: cfe-commits.
zdtorok set the repository for this revision to rL LLVM.

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);
}
```

Repository:
  rL LLVM

http://reviews.llvm.org/D21506

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

Index: test/Analysis/block-in-critical-section.cpp
===================================================================
--- /dev/null
+++ test/Analysis/block-in-critical-section.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang --analyze -std=c++11 -Xclang -analyzer-checker -Xclang alpha.unix.BlockInCriticalSection %s
+
+void sleep(int x) {}
+
+namespace std {
+struct mutex {
+  void lock() {}
+  void unlock() {}
+};
+}
+
+void testBlockInCriticalSection() {
+  std::mutex m;
+  m.lock();
+  sleep(3); // expected-warning {{Block in a critical section}}
+  m.unlock();
+}
+
+void testBlockInCriticalSectionWithNestedMutexes() {
+  std::mutex m, n, k;
+  m.lock();
+  n.lock();
+  k.lock();
+  sleep(3); // expected-warning {{Block in a critical section}}
+  k.unlock();
+  sleep(5); // expected-warning {{Block in a critical section}}
+  n.unlock();
+  sleep(3); // expected-warning {{Block in a critical section}}
+  m.unlock();
+  sleep(3); // no-warning
+}
Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt
===================================================================
--- lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -8,6 +8,7 @@
   ArrayBoundChecker.cpp
   ArrayBoundCheckerV2.cpp
   BasicObjCFoundationChecks.cpp
+  BlockInCriticalSectionChecker.cpp
   BoolAssignmentChecker.cpp
   BuiltinFunctionChecker.cpp
   CStringChecker.cpp
Index: lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
===================================================================
--- /dev/null
+++ lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -0,0 +1,111 @@
+//===-- 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();
+
+  /// Process lock.
+  /// Process blocking functions (sleep, getc, fgets, read, recv)
+  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+
+  /// Process unlock.
+  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+
+};
+
+} // end anonymous namespace
+
+REGISTER_TRAIT_WITH_PROGRAMSTATE(Counter, 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, "Block in critical section", "Unix Stream API Error"));
+}
+
+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))
+    return;
+
+  ProgramStateRef State = C.getState();
+  unsigned mutexCount = State->get<Counter>();
+  if (Call.isCalled(LockFn)) {
+    State = State->set<Counter>(++mutexCount);
+    C.addTransition(State);
+  } else if (mutexCount > 0) {
+    SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol();
+    reportBlockInCritSection(BlockDesc, Call, C);
+  }
+}
+
+void BlockInCriticalSectionChecker::checkPreCall(const CallEvent &Call,
+                                                 CheckerContext &C) const {
+  if (!Call.isCalled(UnlockFn))
+    return;
+
+  ProgramStateRef State = C.getState();
+  unsigned mutexCount = State->get<Counter>();
+  State = State->set<Counter>(--mutexCount);
+  C.addTransition(State);
+}
+
+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,
+      "Block in a critical section", ErrNode);
+  R->addRange(Call.getSourceRange());
+  R->markInteresting(BlockDescSym);
+  C.emitReport(std::move(R));
+}
+
+void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
+  mgr.registerChecker<BlockInCriticalSectionChecker>();
+}
Index: include/clang/StaticAnalyzer/Checkers/Checkers.td
===================================================================
--- include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -402,6 +402,10 @@
   HelpText<"Check for misuses of stream APIs">,
   DescFile<"SimpleStreamChecker.cpp">;
 
+def BlockInCriticalSectionChecker : Checker<"BlockInCriticalSection">,
+  HelpText<"Check for blocks in critical section">,
+  DescFile<"BlockInCriticalSectionChecker.cpp">;
+
 } // end "alpha.unix"
 
 let ParentPackage = CString in {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to