clang  5.0.0
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  check::PreCall> {
31 
32  CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn,
33  PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn,
34  MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock;
35 
36  std::unique_ptr<BugType> BlockInCritSectionBugType;
37 
38  void reportBlockInCritSection(SymbolRef FileDescSym,
39  const CallEvent &call,
40  CheckerContext &C) const;
41 
42 public:
43  BlockInCriticalSectionChecker();
44 
45  bool isBlockingFunction(const CallEvent &Call) const;
46  bool isLockFunction(const CallEvent &Call) const;
47  bool isUnlockFunction(const CallEvent &Call) const;
48 
49  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
50 
51  /// Process unlock.
52  /// Process lock.
53  /// Process blocking functions (sleep, getc, fgets, read, recv)
54  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
55 
56 };
57 
58 } // end anonymous namespace
59 
60 REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
61 
62 BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
63  : LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"),
64  FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"),
65  PthreadLockFn("pthread_mutex_lock"),
66  PthreadTryLockFn("pthread_mutex_trylock"),
67  PthreadUnlockFn("pthread_mutex_unlock"),
68  MtxLock("mtx_lock"),
69  MtxTimedLock("mtx_timedlock"),
70  MtxTryLock("mtx_trylock"),
71  MtxUnlock("mtx_unlock") {
72  // Initialize the bug type.
73  BlockInCritSectionBugType.reset(
74  new BugType(this, "Call to blocking function in critical section",
75  "Blocking Error"));
76 }
77 
78 bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const {
79  if (Call.isCalled(SleepFn)
80  || Call.isCalled(GetcFn)
81  || Call.isCalled(FgetsFn)
82  || Call.isCalled(ReadFn)
83  || Call.isCalled(RecvFn)) {
84  return true;
85  }
86  return false;
87 }
88 
89 bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const {
90  if (Call.isCalled(LockFn)
91  || Call.isCalled(PthreadLockFn)
92  || Call.isCalled(PthreadTryLockFn)
93  || Call.isCalled(MtxLock)
94  || Call.isCalled(MtxTimedLock)
95  || Call.isCalled(MtxTryLock)) {
96  return true;
97  }
98  return false;
99 }
100 
101 bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
102  if (Call.isCalled(UnlockFn)
103  || Call.isCalled(PthreadUnlockFn)
104  || Call.isCalled(MtxUnlock)) {
105  return true;
106  }
107  return false;
108 }
109 
110 void BlockInCriticalSectionChecker::checkPreCall(const CallEvent &Call,
111  CheckerContext &C) const {
112 }
113 
114 void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
115  CheckerContext &C) const {
116  if (!isBlockingFunction(Call)
117  && !isLockFunction(Call)
118  && !isUnlockFunction(Call))
119  return;
120 
122  unsigned mutexCount = State->get<MutexCounter>();
123  if (isUnlockFunction(Call) && mutexCount > 0) {
124  State = State->set<MutexCounter>(--mutexCount);
125  C.addTransition(State);
126  } else if (isLockFunction(Call)) {
127  State = State->set<MutexCounter>(++mutexCount);
128  C.addTransition(State);
129  } else if (mutexCount > 0) {
130  SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol();
131  reportBlockInCritSection(BlockDesc, Call, C);
132  }
133 }
134 
135 void BlockInCriticalSectionChecker::reportBlockInCritSection(
136  SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
138  if (!ErrNode)
139  return;
140 
141  std::string msg;
142  llvm::raw_string_ostream os(msg);
143  os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName()
144  << "' inside of critical section";
145  auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType, os.str(), ErrNode);
146  R->addRange(Call.getSourceRange());
147  R->markInteresting(BlockDescSym);
148  C.emitReport(std::move(R));
149 }
150 
151 void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
152  mgr.registerChecker<BlockInCriticalSectionChecker>();
153 }
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
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
Symbolic value.
Definition: SymExpr.h:29
LineState State
StringRef getName() const
Return the actual identifier string.
const ProgramStateRef & getState() const
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.
#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
virtual SourceRange getSourceRange() const
Returns a source range for the entire call, suitable for outputting in diagnostics.
Definition: CallEvent.h:266
const IdentifierInfo * getCalleeIdentifier() const
Returns the name of the callee, if its name is a simple identifier.
Definition: CallEvent.h:335
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:140
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
Definition: SVals.cpp:116
SVal getReturnValue() const
Returns the return value of the call.
Definition: CallEvent.cpp:240