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 "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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> {
31 mutable IdentifierInfo *IILockGuard, *IIUniqueLock;
33 CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn,
34 PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn,
35 MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock;
37 StringRef ClassLockGuard, ClassUniqueLock;
39 mutable bool IdentifierInfoInitialized;
41 std::unique_ptr<BugType> BlockInCritSectionBugType;
43 void initIdentifierInfo(ASTContext &Ctx) const;
45 void reportBlockInCritSection(SymbolRef FileDescSym,
46 const CallEvent &call,
47 CheckerContext &C) const;
50 BlockInCriticalSectionChecker();
52 bool isBlockingFunction(const CallEvent &Call) const;
53 bool isLockFunction(const CallEvent &Call) const;
54 bool isUnlockFunction(const CallEvent &Call) const;
58 /// Process blocking functions (sleep, getc, fgets, read, recv)
59 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
62 } // end anonymous namespace
64 REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
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"),
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",
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
93 IILockGuard = &Ctx.Idents.get(ClassLockGuard);
94 IIUniqueLock = &Ctx.Idents.get(ClassUniqueLock);
95 IdentifierInfoInitialized = true;
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)) {
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)
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)) {
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)
136 if (Call.isCalled(UnlockFn)
137 || Call.isCalled(PthreadUnlockFn)
138 || Call.isCalled(MtxUnlock)) {
144 void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
145 CheckerContext &C) const {
146 initIdentifierInfo(C.getASTContext());
148 if (!isBlockingFunction(Call)
149 && !isLockFunction(Call)
150 && !isUnlockFunction(Call))
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);
167 void BlockInCriticalSectionChecker::reportBlockInCritSection(
168 SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
169 ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
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));
183 void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
184 mgr.registerChecker<BlockInCriticalSectionChecker>();