clang  6.0.0svn
BlockInCriticalSectionChecker.cpp
Go to the documentation of this file.
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"
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 
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 {
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 }
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
Symbolic value.
Definition: SymExpr.h:29
One of these records is kept for each identifier that is lexed.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:149
LineState State
IdentifierTable & Idents
Definition: ASTContext.h:537
SVal getReturnValue() const
Returns the return value of the call.
Definition: CallEvent.cpp:242
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
Definition: SVals.cpp:116
const IdentifierInfo * getCalleeIdentifier() const
Returns the name of the callee, if its name is a simple identifier.
Definition: CallEvent.h:335
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
virtual SourceRange getSourceRange() const
Returns a source range for the entire call, suitable for outputting in diagnostics.
Definition: CallEvent.h:266
#define false
Definition: stdbool.h:33
#define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)
Declares a program state trait for type Type called Name, and introduce a typedef named NameTy...
CHECKER * registerChecker()
Used to register checkers.
This class represents a description of a function call using the number of arguments and the name of ...
Definition: CallEvent.h:55
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
StringRef getName() const
Return the actual identifier string.
Dataflow Directional Tag Classes.
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:140
const ProgramStateRef & getState() const
Represents a C++ struct/union/class.
Definition: DeclCXX.h:299
bool isCalled(const CallDescription &CD) const
Returns true if the CallEvent is a call to a function that matches the CallDescription.
Definition: CallEvent.cpp:213