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