]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
Merge llvm, clang, lld, lldb, compiler-rt and libc++ r307894, and update
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / clang / lib / StaticAnalyzer / Checkers / BlockInCriticalSectionChecker.cpp
1 //===-- BlockInCriticalSectionChecker.cpp -----------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
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.
15 //
16 //===----------------------------------------------------------------------===//
17
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"
23
24 using namespace clang;
25 using namespace ento;
26
27 namespace {
28
29 class BlockInCriticalSectionChecker : public Checker<check::PostCall,
30                                                      check::PreCall> {
31
32   CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn,
33                   PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn,
34                   MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock;
35
36   std::unique_ptr<BugType> BlockInCritSectionBugType;
37
38   void reportBlockInCritSection(SymbolRef FileDescSym,
39                                 const CallEvent &call,
40                                 CheckerContext &C) const;
41
42 public:
43   BlockInCriticalSectionChecker();
44
45   bool isBlockingFunction(const CallEvent &Call) const;
46   bool isLockFunction(const CallEvent &Call) const;
47   bool isUnlockFunction(const CallEvent &Call) const;
48
49   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
50
51   /// Process unlock.
52   /// Process lock.
53   /// Process blocking functions (sleep, getc, fgets, read, recv)
54   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
55
56 };
57
58 } // end anonymous namespace
59
60 REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
61
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"),
68       MtxLock("mtx_lock"),
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",
75                         "Blocking Error"));
76 }
77
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)) {
84     return true;
85   }
86   return false;
87 }
88
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)) {
96     return true;
97   }
98   return false;
99 }
100
101 bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
102   if (Call.isCalled(UnlockFn)
103        || Call.isCalled(PthreadUnlockFn)
104        || Call.isCalled(MtxUnlock)) {
105     return true;
106   }
107   return false;
108 }
109
110 void BlockInCriticalSectionChecker::checkPreCall(const CallEvent &Call,
111                                                  CheckerContext &C) const {
112 }
113
114 void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
115                                                   CheckerContext &C) const {
116   if (!isBlockingFunction(Call)
117       && !isLockFunction(Call)
118       && !isUnlockFunction(Call))
119     return;
120
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);
132   }
133 }
134
135 void BlockInCriticalSectionChecker::reportBlockInCritSection(
136     SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
137   ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
138   if (!ErrNode)
139     return;
140
141   std::string msg;
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));
149 }
150
151 void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
152   mgr.registerChecker<BlockInCriticalSectionChecker>();
153 }