]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
MFV r333668:
[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
31   mutable IdentifierInfo *IILockGuard, *IIUniqueLock;
32
33   CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn,
34                   PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn,
35                   MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock;
36
37   StringRef ClassLockGuard, ClassUniqueLock;
38
39   mutable bool IdentifierInfoInitialized;
40
41   std::unique_ptr<BugType> BlockInCritSectionBugType;
42
43   void initIdentifierInfo(ASTContext &Ctx) const;
44
45   void reportBlockInCritSection(SymbolRef FileDescSym,
46                                 const CallEvent &call,
47                                 CheckerContext &C) const;
48
49 public:
50   BlockInCriticalSectionChecker();
51
52   bool isBlockingFunction(const CallEvent &Call) const;
53   bool isLockFunction(const CallEvent &Call) const;
54   bool isUnlockFunction(const CallEvent &Call) const;
55
56   /// Process unlock.
57   /// Process lock.
58   /// Process blocking functions (sleep, getc, fgets, read, recv)
59   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
60 };
61
62 } // end anonymous namespace
63
64 REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
65
66 BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
67     : IILockGuard(nullptr), IIUniqueLock(nullptr),
68       LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"),
69       FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"),
70       PthreadLockFn("pthread_mutex_lock"),
71       PthreadTryLockFn("pthread_mutex_trylock"),
72       PthreadUnlockFn("pthread_mutex_unlock"),
73       MtxLock("mtx_lock"),
74       MtxTimedLock("mtx_timedlock"),
75       MtxTryLock("mtx_trylock"),
76       MtxUnlock("mtx_unlock"),
77       ClassLockGuard("lock_guard"),
78       ClassUniqueLock("unique_lock"),
79       IdentifierInfoInitialized(false) {
80   // Initialize the bug type.
81   BlockInCritSectionBugType.reset(
82       new BugType(this, "Call to blocking function in critical section",
83                         "Blocking Error"));
84 }
85
86 void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const {
87   if (!IdentifierInfoInitialized) {
88     /* In case of checking C code, or when the corresponding headers are not
89      * included, we might end up query the identifier table every time when this
90      * function is called instead of early returning it. To avoid this, a bool
91      * variable (IdentifierInfoInitialized) is used and the function will be run
92      * only once. */
93     IILockGuard  = &Ctx.Idents.get(ClassLockGuard);
94     IIUniqueLock = &Ctx.Idents.get(ClassUniqueLock);
95     IdentifierInfoInitialized = true;
96   }
97 }
98
99 bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const {
100   if (Call.isCalled(SleepFn)
101       || Call.isCalled(GetcFn)
102       || Call.isCalled(FgetsFn)
103       || Call.isCalled(ReadFn)
104       || Call.isCalled(RecvFn)) {
105     return true;
106   }
107   return false;
108 }
109
110 bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const {
111   if (const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
112     auto IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier();
113     if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
114       return true;
115   }
116
117   if (Call.isCalled(LockFn)
118       || Call.isCalled(PthreadLockFn)
119       || Call.isCalled(PthreadTryLockFn)
120       || Call.isCalled(MtxLock)
121       || Call.isCalled(MtxTimedLock)
122       || Call.isCalled(MtxTryLock)) {
123     return true;
124   }
125   return false;
126 }
127
128 bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
129   if (const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
130     const auto *DRecordDecl = dyn_cast<CXXRecordDecl>(Dtor->getDecl()->getParent());
131     auto IdentifierInfo = DRecordDecl->getIdentifier();
132     if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
133       return true;
134   }
135
136   if (Call.isCalled(UnlockFn)
137        || Call.isCalled(PthreadUnlockFn)
138        || Call.isCalled(MtxUnlock)) {
139     return true;
140   }
141   return false;
142 }
143
144 void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
145                                                   CheckerContext &C) const {
146   initIdentifierInfo(C.getASTContext());
147
148   if (!isBlockingFunction(Call)
149       && !isLockFunction(Call)
150       && !isUnlockFunction(Call))
151     return;
152
153   ProgramStateRef State = C.getState();
154   unsigned mutexCount = State->get<MutexCounter>();
155   if (isUnlockFunction(Call) && mutexCount > 0) {
156     State = State->set<MutexCounter>(--mutexCount);
157     C.addTransition(State);
158   } else if (isLockFunction(Call)) {
159     State = State->set<MutexCounter>(++mutexCount);
160     C.addTransition(State);
161   } else if (mutexCount > 0) {
162     SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol();
163     reportBlockInCritSection(BlockDesc, Call, C);
164   }
165 }
166
167 void BlockInCriticalSectionChecker::reportBlockInCritSection(
168     SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
169   ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
170   if (!ErrNode)
171     return;
172
173   std::string msg;
174   llvm::raw_string_ostream os(msg);
175   os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName()
176      << "' inside of critical section";
177   auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType, os.str(), ErrNode);
178   R->addRange(Call.getSourceRange());
179   R->markInteresting(BlockDescSym);
180   C.emitReport(std::move(R));
181 }
182
183 void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
184   mgr.registerChecker<BlockInCriticalSectionChecker>();
185 }