1 //===-- BlockInCriticalSectionChecker.cpp -----------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // Defines a checker for blocks in critical sections. This checker should find
10 // the calls to blocking functions (for example: sleep, getc, fgets, read,
11 // recv etc.) inside a critical section. When sleep(x) is called while a mutex
12 // is held, other threades cannot lock the same mutex. This might take some
13 // time, leading to bad performance or even deadlock.
15 //===----------------------------------------------------------------------===//
17 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19 #include "clang/StaticAnalyzer/Core/Checker.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
23 using namespace clang;
28 class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
30 mutable IdentifierInfo *IILockGuard, *IIUniqueLock;
32 CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn,
33 PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn,
34 MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock;
36 StringRef ClassLockGuard, ClassUniqueLock;
38 mutable bool IdentifierInfoInitialized;
40 std::unique_ptr<BugType> BlockInCritSectionBugType;
42 void initIdentifierInfo(ASTContext &Ctx) const;
44 void reportBlockInCritSection(SymbolRef FileDescSym,
45 const CallEvent &call,
46 CheckerContext &C) const;
49 BlockInCriticalSectionChecker();
51 bool isBlockingFunction(const CallEvent &Call) const;
52 bool isLockFunction(const CallEvent &Call) const;
53 bool isUnlockFunction(const CallEvent &Call) const;
57 /// Process blocking functions (sleep, getc, fgets, read, recv)
58 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
61 } // end anonymous namespace
63 REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
65 BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
66 : IILockGuard(nullptr), IIUniqueLock(nullptr),
67 LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"),
68 FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"),
69 PthreadLockFn("pthread_mutex_lock"),
70 PthreadTryLockFn("pthread_mutex_trylock"),
71 PthreadUnlockFn("pthread_mutex_unlock"),
73 MtxTimedLock("mtx_timedlock"),
74 MtxTryLock("mtx_trylock"),
75 MtxUnlock("mtx_unlock"),
76 ClassLockGuard("lock_guard"),
77 ClassUniqueLock("unique_lock"),
78 IdentifierInfoInitialized(false) {
79 // Initialize the bug type.
80 BlockInCritSectionBugType.reset(
81 new BugType(this, "Call to blocking function in critical section",
85 void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const {
86 if (!IdentifierInfoInitialized) {
87 /* In case of checking C code, or when the corresponding headers are not
88 * included, we might end up query the identifier table every time when this
89 * function is called instead of early returning it. To avoid this, a bool
90 * variable (IdentifierInfoInitialized) is used and the function will be run
92 IILockGuard = &Ctx.Idents.get(ClassLockGuard);
93 IIUniqueLock = &Ctx.Idents.get(ClassUniqueLock);
94 IdentifierInfoInitialized = true;
98 bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const {
99 if (Call.isCalled(SleepFn)
100 || Call.isCalled(GetcFn)
101 || Call.isCalled(FgetsFn)
102 || Call.isCalled(ReadFn)
103 || Call.isCalled(RecvFn)) {
109 bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const {
110 if (const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
111 auto IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier();
112 if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
116 if (Call.isCalled(LockFn)
117 || Call.isCalled(PthreadLockFn)
118 || Call.isCalled(PthreadTryLockFn)
119 || Call.isCalled(MtxLock)
120 || Call.isCalled(MtxTimedLock)
121 || Call.isCalled(MtxTryLock)) {
127 bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
128 if (const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
129 const auto *DRecordDecl = cast<CXXRecordDecl>(Dtor->getDecl()->getParent());
130 auto IdentifierInfo = DRecordDecl->getIdentifier();
131 if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
135 if (Call.isCalled(UnlockFn)
136 || Call.isCalled(PthreadUnlockFn)
137 || Call.isCalled(MtxUnlock)) {
143 void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
144 CheckerContext &C) const {
145 initIdentifierInfo(C.getASTContext());
147 if (!isBlockingFunction(Call)
148 && !isLockFunction(Call)
149 && !isUnlockFunction(Call))
152 ProgramStateRef State = C.getState();
153 unsigned mutexCount = State->get<MutexCounter>();
154 if (isUnlockFunction(Call) && mutexCount > 0) {
155 State = State->set<MutexCounter>(--mutexCount);
156 C.addTransition(State);
157 } else if (isLockFunction(Call)) {
158 State = State->set<MutexCounter>(++mutexCount);
159 C.addTransition(State);
160 } else if (mutexCount > 0) {
161 SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol();
162 reportBlockInCritSection(BlockDesc, Call, C);
166 void BlockInCriticalSectionChecker::reportBlockInCritSection(
167 SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
168 ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
173 llvm::raw_string_ostream os(msg);
174 os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName()
175 << "' inside of critical section";
176 auto R = std::make_unique<PathSensitiveBugReport>(*BlockInCritSectionBugType,
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>();
187 bool ento::shouldRegisterBlockInCriticalSectionChecker(const LangOptions &LO) {