Coverage Report

Created: 2019-07-24 05:18

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
Line
Count
Source (jump to first uncovered line)
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
6
      IdentifierInfoInitialized(false) {
79
6
  // Initialize the bug type.
80
6
  BlockInCritSectionBugType.reset(
81
6
      new BugType(this, "Call to blocking function in critical section",
82
6
                        "Blocking Error"));
83
6
}
84
85
112
void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const {
86
112
  if (!IdentifierInfoInitialized) {
87
6
    /* In case of checking C code, or when the corresponding headers are not
88
6
     * included, we might end up query the identifier table every time when this
89
6
     * function is called instead of early returning it. To avoid this, a bool
90
6
     * variable (IdentifierInfoInitialized) is used and the function will be run
91
6
     * only once. */
92
6
    IILockGuard  = &Ctx.Idents.get(ClassLockGuard);
93
6
    IIUniqueLock = &Ctx.Idents.get(ClassUniqueLock);
94
6
    IdentifierInfoInitialized = true;
95
6
  }
96
112
}
97
98
112
bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const {
99
112
  if (Call.isCalled(SleepFn)
100
112
      || 
Call.isCalled(GetcFn)93
101
112
      || 
Call.isCalled(FgetsFn)87
102
112
      || 
Call.isCalled(ReadFn)81
103
112
      || 
Call.isCalled(RecvFn)75
) {
104
43
    return true;
105
43
  }
106
69
  return false;
107
69
}
108
109
126
bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const {
110
126
  if (const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
111
18
    auto IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier();
112
18
    if (IdentifierInfo == IILockGuard || 
IdentifierInfo == IIUniqueLock16
)
113
4
      return true;
114
122
  }
115
122
116
122
  if (Call.isCalled(LockFn)
117
122
      || 
Call.isCalled(PthreadLockFn)110
118
122
      || 
Call.isCalled(PthreadTryLockFn)108
119
122
      || 
Call.isCalled(MtxLock)106
120
122
      || 
Call.isCalled(MtxTimedLock)104
121
122
      || 
Call.isCalled(MtxTryLock)102
) {
122
22
    return true;
123
22
  }
124
100
  return false;
125
100
}
126
127
125
bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
128
125
  if (const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
129
4
    const auto *DRecordDecl = dyn_cast<CXXRecordDecl>(Dtor->getDecl()->getParent());
130
4
    auto IdentifierInfo = DRecordDecl->getIdentifier();
131
4
    if (IdentifierInfo == IILockGuard || 
IdentifierInfo == IIUniqueLock2
)
132
4
      return true;
133
121
  }
134
121
135
121
  if (Call.isCalled(UnlockFn)
136
121
       || 
Call.isCalled(PthreadUnlockFn)109
137
121
       || 
Call.isCalled(MtxUnlock)105
) {
138
22
    return true;
139
22
  }
140
99
  return false;
141
99
}
142
143
void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
144
112
                                                  CheckerContext &C) const {
145
112
  initIdentifierInfo(C.getASTContext());
146
112
147
112
  if (!isBlockingFunction(Call)
148
112
      && 
!isLockFunction(Call)69
149
112
      && 
!isUnlockFunction(Call)56
)
150
43
    return;
151
69
152
69
  ProgramStateRef State = C.getState();
153
69
  unsigned mutexCount = State->get<MutexCounter>();
154
69
  if (isUnlockFunction(Call) && 
mutexCount > 013
) {
155
12
    State = State->set<MutexCounter>(--mutexCount);
156
12
    C.addTransition(State);
157
57
  } else if (isLockFunction(Call)) {
158
13
    State = State->set<MutexCounter>(++mutexCount);
159
13
    C.addTransition(State);
160
44
  } else if (mutexCount > 0) {
161
37
    SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol();
162
37
    reportBlockInCritSection(BlockDesc, Call, C);
163
37
  }
164
69
}
165
166
void BlockInCriticalSectionChecker::reportBlockInCritSection(
167
37
    SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
168
37
  ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
169
37
  if (!ErrNode)
170
0
    return;
171
37
172
37
  std::string msg;
173
37
  llvm::raw_string_ostream os(msg);
174
37
  os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName()
175
37
     << "' inside of critical section";
176
37
  auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType, os.str(), ErrNode);
177
37
  R->addRange(Call.getSourceRange());
178
37
  R->markInteresting(BlockDescSym);
179
37
  C.emitReport(std::move(R));
180
37
}
181
182
6
void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
183
6
  mgr.registerChecker<BlockInCriticalSectionChecker>();
184
6
}
185
186
6
bool ento::shouldRegisterBlockInCriticalSectionChecker(const LangOptions &LO) {
187
6
  return true;
188
6
}