Coverage Report

Created: 2022-01-18 06:27

/Users/buildslave/jenkins/workspace/coverage/llvm-project/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/CallDescription.h"
21
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
22
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.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
5
      IdentifierInfoInitialized(false) {
80
  // Initialize the bug type.
81
5
  BlockInCritSectionBugType.reset(
82
5
      new BugType(this, "Call to blocking function in critical section",
83
5
                        "Blocking Error"));
84
5
}
85
86
109
void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const {
87
109
  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
5
    IILockGuard  = &Ctx.Idents.get(ClassLockGuard);
94
5
    IIUniqueLock = &Ctx.Idents.get(ClassUniqueLock);
95
5
    IdentifierInfoInitialized = true;
96
5
  }
97
109
}
98
99
109
bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const {
100
109
  return matchesAny(Call, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn);
101
109
}
102
103
123
bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const {
104
123
  if (const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
105
18
    auto IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier();
106
18
    if (IdentifierInfo == IILockGuard || 
IdentifierInfo == IIUniqueLock16
)
107
4
      return true;
108
18
  }
109
110
119
  return matchesAny(Call, LockFn, PthreadLockFn, PthreadTryLockFn, MtxLock,
111
119
                    MtxTimedLock, MtxTryLock);
112
123
}
113
114
122
bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
115
122
  if (const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
116
4
    const auto *DRecordDecl = cast<CXXRecordDecl>(Dtor->getDecl()->getParent());
117
4
    auto IdentifierInfo = DRecordDecl->getIdentifier();
118
4
    if (IdentifierInfo == IILockGuard || 
IdentifierInfo == IIUniqueLock2
)
119
4
      return true;
120
4
  }
121
122
118
  return matchesAny(Call, UnlockFn, PthreadUnlockFn, MtxUnlock);
123
122
}
124
125
void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
126
109
                                                  CheckerContext &C) const {
127
109
  initIdentifierInfo(C.getASTContext());
128
129
109
  if (!isBlockingFunction(Call)
130
109
      && 
!isLockFunction(Call)66
131
109
      && 
!isUnlockFunction(Call)53
)
132
40
    return;
133
134
69
  ProgramStateRef State = C.getState();
135
69
  unsigned mutexCount = State->get<MutexCounter>();
136
69
  if (isUnlockFunction(Call) && 
mutexCount > 013
) {
137
12
    State = State->set<MutexCounter>(--mutexCount);
138
12
    C.addTransition(State);
139
57
  } else if (isLockFunction(Call)) {
140
13
    State = State->set<MutexCounter>(++mutexCount);
141
13
    C.addTransition(State);
142
44
  } else if (mutexCount > 0) {
143
37
    SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol();
144
37
    reportBlockInCritSection(BlockDesc, Call, C);
145
37
  }
146
69
}
147
148
void BlockInCriticalSectionChecker::reportBlockInCritSection(
149
37
    SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
150
37
  ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
151
37
  if (!ErrNode)
152
0
    return;
153
154
37
  std::string msg;
155
37
  llvm::raw_string_ostream os(msg);
156
37
  os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName()
157
37
     << "' inside of critical section";
158
37
  auto R = std::make_unique<PathSensitiveBugReport>(*BlockInCritSectionBugType,
159
37
                                                    os.str(), ErrNode);
160
37
  R->addRange(Call.getSourceRange());
161
37
  R->markInteresting(BlockDescSym);
162
37
  C.emitReport(std::move(R));
163
37
}
164
165
5
void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
166
5
  mgr.registerChecker<BlockInCriticalSectionChecker>();
167
5
}
168
169
10
bool ento::shouldRegisterBlockInCriticalSectionChecker(const CheckerManager &mgr) {
170
10
  return true;
171
10
}