1 //===-- BlockInCriticalSectionChecker.cpp -----------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // Defines a checker for blocks in critical sections. This checker should find
11 // the calls to blocking functions (for example: sleep, getc, fgets, read,
12 // recv etc.) inside a critical section. When sleep(x) is called while a mutex
13 // is held, other threades cannot lock the same mutex. This might take some
14 // time, leading to bad performance or even deadlock.
16 //===----------------------------------------------------------------------===//
18 #include "ClangSACheckers.h"
19 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
20 #include "clang/StaticAnalyzer/Core/Checker.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
24 using namespace clang;
29 class BlockInCriticalSectionChecker : public Checker<check::PostCall,
32 CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn,
33 PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn,
34 MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock;
36 std::unique_ptr<BugType> BlockInCritSectionBugType;
38 void reportBlockInCritSection(SymbolRef FileDescSym,
39 const CallEvent &call,
40 CheckerContext &C) const;
43 BlockInCriticalSectionChecker();
45 bool isBlockingFunction(const CallEvent &Call) const;
46 bool isLockFunction(const CallEvent &Call) const;
47 bool isUnlockFunction(const CallEvent &Call) const;
49 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
53 /// Process blocking functions (sleep, getc, fgets, read, recv)
54 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
58 } // end anonymous namespace
60 REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
62 BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
63 : LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"),
64 FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"),
65 PthreadLockFn("pthread_mutex_lock"),
66 PthreadTryLockFn("pthread_mutex_trylock"),
67 PthreadUnlockFn("pthread_mutex_unlock"),
69 MtxTimedLock("mtx_timedlock"),
70 MtxTryLock("mtx_trylock"),
71 MtxUnlock("mtx_unlock") {
72 // Initialize the bug type.
73 BlockInCritSectionBugType.reset(
74 new BugType(this, "Call to blocking function in critical section",
78 bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const {
79 if (Call.isCalled(SleepFn)
80 || Call.isCalled(GetcFn)
81 || Call.isCalled(FgetsFn)
82 || Call.isCalled(ReadFn)
83 || Call.isCalled(RecvFn)) {
89 bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const {
90 if (Call.isCalled(LockFn)
91 || Call.isCalled(PthreadLockFn)
92 || Call.isCalled(PthreadTryLockFn)
93 || Call.isCalled(MtxLock)
94 || Call.isCalled(MtxTimedLock)
95 || Call.isCalled(MtxTryLock)) {
101 bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
102 if (Call.isCalled(UnlockFn)
103 || Call.isCalled(PthreadUnlockFn)
104 || Call.isCalled(MtxUnlock)) {
110 void BlockInCriticalSectionChecker::checkPreCall(const CallEvent &Call,
111 CheckerContext &C) const {
114 void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
115 CheckerContext &C) const {
116 if (!isBlockingFunction(Call)
117 && !isLockFunction(Call)
118 && !isUnlockFunction(Call))
121 ProgramStateRef State = C.getState();
122 unsigned mutexCount = State->get<MutexCounter>();
123 if (isUnlockFunction(Call) && mutexCount > 0) {
124 State = State->set<MutexCounter>(--mutexCount);
125 C.addTransition(State);
126 } else if (isLockFunction(Call)) {
127 State = State->set<MutexCounter>(++mutexCount);
128 C.addTransition(State);
129 } else if (mutexCount > 0) {
130 SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol();
131 reportBlockInCritSection(BlockDesc, Call, C);
135 void BlockInCriticalSectionChecker::reportBlockInCritSection(
136 SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
137 ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
142 llvm::raw_string_ostream os(msg);
143 os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName()
144 << "' inside of critical section";
145 auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType, os.str(), ErrNode);
146 R->addRange(Call.getSourceRange());
147 R->markInteresting(BlockDescSym);
148 C.emitReport(std::move(R));
151 void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
152 mgr.registerChecker<BlockInCriticalSectionChecker>();