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