clang 10.0.0git

BlockInCriticalSectionChecker.cpp
Go to the documentation of this file.
1 //===-- BlockInCriticalSectionChecker.cpp -----------------------*- C++ -*-===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
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.
14 //
15 //===----------------------------------------------------------------------===//
16
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"
22
23 using namespace clang;
24 using namespace ento;
25
26 namespace {
27
28 class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
29
30 mutable IdentifierInfo *IILockGuard, *IIUniqueLock;
31
32 CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn,
33 PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn,
34 MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock;
35
36 StringRef ClassLockGuard, ClassUniqueLock;
37
38 mutable bool IdentifierInfoInitialized;
39
40 std::unique_ptr<BugType> BlockInCritSectionBugType;
41
42 void initIdentifierInfo(ASTContext &Ctx) const;
43
44 void reportBlockInCritSection(SymbolRef FileDescSym,
45 const CallEvent &call,
46 CheckerContext &C) const;
47
48 public:
49 BlockInCriticalSectionChecker();
50
51 bool isBlockingFunction(const CallEvent &Call) const;
52 bool isLockFunction(const CallEvent &Call) const;
53 bool isUnlockFunction(const CallEvent &Call) const;
54
55 /// Process unlock.
56 /// Process lock.
57 /// Process blocking functions (sleep, getc, fgets, read, recv)
58 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
59 };
60
61 } // end anonymous namespace
62
63 REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
64
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"),
72 MtxLock("mtx_lock"),
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",
82 "Blocking Error"));
83 }
84
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
91 * only once. */
92 IILockGuard = &Ctx.Idents.get(ClassLockGuard);
93 IIUniqueLock = &Ctx.Idents.get(ClassUniqueLock);
94 IdentifierInfoInitialized = true;
95 }
96 }
97
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)) {
104 return true;
105 }
106 return false;
107 }
108
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)
113 return true;
114 }
115
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)) {
122 return true;
123 }
124 return false;
125 }
126
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)
132 return true;
133 }
134
135 if (Call.isCalled(UnlockFn)
136 || Call.isCalled(PthreadUnlockFn)
137 || Call.isCalled(MtxUnlock)) {
138 return true;
139 }
140 return false;
141 }
142
143 void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
144 CheckerContext &C) const {
145 initIdentifierInfo(C.getASTContext());
146
147 if (!isBlockingFunction(Call)
148 && !isLockFunction(Call)
149 && !isUnlockFunction(Call))
150 return;
151
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);
163 }
164 }
165
166 void BlockInCriticalSectionChecker::reportBlockInCritSection(
167 SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
168 ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
169 if (!ErrNode)
170 return;
171
172 std::string msg;
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,
177 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 }
186
187 bool ento::shouldRegisterBlockInCriticalSectionChecker(const LangOptions &LO) {
188 return true;
189 }
ento
clang::ento::ProgramStateRef
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Definition: ProgramState_Fwd.h:37
BugType.h
clang::IdentifierInfo
One of these records is kept for each identifier that is lexed.
Definition: IdentifierTable.h:57
clang::ento::SymbolRef
const SymExpr * SymbolRef
Definition: SymExpr.h:110
clang::ASTContext
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:168
State
LineState State
Definition: UnwrappedLineFormatter.cpp:932
Checker.h
clang::LangOptions
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:53
clang::ASTContext::Idents
IdentifierTable & Idents
Definition: ASTContext.h:580
clang::CallDescription
This class represents a description of a function call using the number of arguments and the name of ...
Definition: CallEvent.h:1058
BuiltinCheckerRegistration.h
false
#define false
Definition: stdbool.h:17
REGISTER_TRAIT_WITH_PROGRAMSTATE
#define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)
Declares a program state trait for type Type called Name, and introduce a type named NameTy...
Definition: ProgramStateTrait.h:33
clang::IdentifierTable::get
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
Definition: IdentifierTable.h:529
clang
Dataflow Directional Tag Classes.
Definition: CFGReachabilityAnalysis.h:21
AttributeLangSupport::C
Definition: SemaDeclAttr.cpp:45
CallEvent.h
CallEvent
CheckerContext.h