summaryrefslogtreecommitdiffstats
path: root/include/clang/Analysis/Analyses/ThreadSafety.h
blob: 18659aa4e5bbad4b8da11aeaba1ccb215dfcfe0b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
//===- ThreadSafety.h -------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//
// A intra-procedural analysis for thread safety (e.g. deadlocks and race
// conditions), based off of an annotation system.
//
// See http://clang.llvm.org/docs/LanguageExtensions.html#thread-safety-annotation-checking
// for more information.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_THREADSAFETY_H
#define LLVM_CLANG_ANALYSIS_ANALYSES_THREADSAFETY_H

#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/StringRef.h"

namespace clang {

class AnalysisDeclContext;
class FunctionDecl;
class NamedDecl;

namespace threadSafety {

class BeforeSet;

/// This enum distinguishes between different kinds of operations that may
/// need to be protected by locks. We use this enum in error handling.
enum ProtectedOperationKind {
  /// Dereferencing a variable (e.g. p in *p = 5;)
  POK_VarDereference,

  /// Reading or writing a variable (e.g. x in x = 5;)
  POK_VarAccess,

  /// Making a function call (e.g. fool())
  POK_FunctionCall,

  /// Passing a guarded variable by reference.
  POK_PassByRef,

  /// Passing a pt-guarded variable by reference.
  POK_PtPassByRef
};

/// This enum distinguishes between different kinds of lock actions. For
/// example, it is an error to write a variable protected by shared version of a
/// mutex.
enum LockKind {
  /// Shared/reader lock of a mutex.
  LK_Shared,

  /// Exclusive/writer lock of a mutex.
  LK_Exclusive,

  /// Can be either Shared or Exclusive.
  LK_Generic
};

/// This enum distinguishes between different ways to access (read or write) a
/// variable.
enum AccessKind {
  /// Reading a variable.
  AK_Read,

  /// Writing a variable.
  AK_Written
};

/// This enum distinguishes between different situations where we warn due to
/// inconsistent locking.
/// \enum SK_LockedSomeLoopIterations -- a mutex is locked for some but not all
/// loop iterations.
/// \enum SK_LockedSomePredecessors -- a mutex is locked in some but not all
/// predecessors of a CFGBlock.
/// \enum SK_LockedAtEndOfFunction -- a mutex is still locked at the end of a
/// function.
enum LockErrorKind {
  LEK_LockedSomeLoopIterations,
  LEK_LockedSomePredecessors,
  LEK_LockedAtEndOfFunction,
  LEK_NotLockedAtEndOfFunction
};

/// Handler class for thread safety warnings.
class ThreadSafetyHandler {
public:
  using Name = StringRef;

  ThreadSafetyHandler() = default;
  virtual ~ThreadSafetyHandler();

  /// Warn about lock expressions which fail to resolve to lockable objects.
  /// \param Kind -- the capability's name parameter (role, mutex, etc).
  /// \param Loc -- the SourceLocation of the unresolved expression.
  virtual void handleInvalidLockExp(StringRef Kind, SourceLocation Loc) {}

  /// Warn about unlock function calls that do not have a prior matching lock
  /// expression.
  /// \param Kind -- the capability's name parameter (role, mutex, etc).
  /// \param LockName -- A StringRef name for the lock expression, to be printed
  /// in the error message.
  /// \param Loc -- The SourceLocation of the Unlock
  virtual void handleUnmatchedUnlock(StringRef Kind, Name LockName,
                                     SourceLocation Loc) {}

  /// Warn about an unlock function call that attempts to unlock a lock with
  /// the incorrect lock kind. For instance, a shared lock being unlocked
  /// exclusively, or vice versa.
  /// \param LockName -- A StringRef name for the lock expression, to be printed
  /// in the error message.
  /// \param Kind -- the capability's name parameter (role, mutex, etc).
  /// \param Expected -- the kind of lock expected.
  /// \param Received -- the kind of lock received.
  /// \param LocLocked -- The SourceLocation of the Lock.
  /// \param LocUnlock -- The SourceLocation of the Unlock.
  virtual void handleIncorrectUnlockKind(StringRef Kind, Name LockName,
                                         LockKind Expected, LockKind Received,
                                         SourceLocation LocLocked,
                                         SourceLocation LocUnlock) {}

  /// Warn about lock function calls for locks which are already held.
  /// \param Kind -- the capability's name parameter (role, mutex, etc).
  /// \param LockName -- A StringRef name for the lock expression, to be printed
  /// in the error message.
  /// \param LocLocked -- The location of the first lock expression.
  /// \param LocDoubleLock -- The location of the second lock expression.
  virtual void handleDoubleLock(StringRef Kind, Name LockName,
                                SourceLocation LocLocked,
                                SourceLocation LocDoubleLock) {}

  /// Warn about situations where a mutex is sometimes held and sometimes not.
  /// The three situations are:
  /// 1. a mutex is locked on an "if" branch but not the "else" branch,
  /// 2, or a mutex is only held at the start of some loop iterations,
  /// 3. or when a mutex is locked but not unlocked inside a function.
  /// \param Kind -- the capability's name parameter (role, mutex, etc).
  /// \param LockName -- A StringRef name for the lock expression, to be printed
  /// in the error message.
  /// \param LocLocked -- The location of the lock expression where the mutex is
  ///               locked
  /// \param LocEndOfScope -- The location of the end of the scope where the
  ///               mutex is no longer held
  /// \param LEK -- which of the three above cases we should warn for
  virtual void handleMutexHeldEndOfScope(StringRef Kind, Name LockName,
                                         SourceLocation LocLocked,
                                         SourceLocation LocEndOfScope,
                                         LockErrorKind LEK) {}

  /// Warn when a mutex is held exclusively and shared at the same point. For
  /// example, if a mutex is locked exclusively during an if branch and shared
  /// during the else branch.
  /// \param Kind -- the capability's name parameter (role, mutex, etc).
  /// \param LockName -- A StringRef name for the lock expression, to be printed
  /// in the error message.
  /// \param Loc1 -- The location of the first lock expression.
  /// \param Loc2 -- The location of the second lock expression.
  virtual void handleExclusiveAndShared(StringRef Kind, Name LockName,
                                        SourceLocation Loc1,
                                        SourceLocation Loc2) {}

  /// Warn when a protected operation occurs while no locks are held.
  /// \param Kind -- the capability's name parameter (role, mutex, etc).
  /// \param D -- The decl for the protected variable or function
  /// \param POK -- The kind of protected operation (e.g. variable access)
  /// \param AK -- The kind of access (i.e. read or write) that occurred
  /// \param Loc -- The location of the protected operation.
  virtual void handleNoMutexHeld(StringRef Kind, const NamedDecl *D,
                                 ProtectedOperationKind POK, AccessKind AK,
                                 SourceLocation Loc) {}

  /// Warn when a protected operation occurs while the specific mutex protecting
  /// the operation is not locked.
  /// \param Kind -- the capability's name parameter (role, mutex, etc).
  /// \param D -- The decl for the protected variable or function
  /// \param POK -- The kind of protected operation (e.g. variable access)
  /// \param LockName -- A StringRef name for the lock expression, to be printed
  /// in the error message.
  /// \param LK -- The kind of access (i.e. read or write) that occurred
  /// \param Loc -- The location of the protected operation.
  virtual void handleMutexNotHeld(StringRef Kind, const NamedDecl *D,
                                  ProtectedOperationKind POK, Name LockName,
                                  LockKind LK, SourceLocation Loc,
                                  Name *PossibleMatch = nullptr) {}

  /// Warn when acquiring a lock that the negative capability is not held.
  /// \param Kind -- the capability's name parameter (role, mutex, etc).
  /// \param LockName -- The name for the lock expression, to be printed in the
  /// diagnostic.
  /// \param Neg -- The name of the negative capability to be printed in the
  /// diagnostic.
  /// \param Loc -- The location of the protected operation.
  virtual void handleNegativeNotHeld(StringRef Kind, Name LockName, Name Neg,
                                     SourceLocation Loc) {}

  /// Warn when a function is called while an excluded mutex is locked. For
  /// example, the mutex may be locked inside the function.
  /// \param Kind -- the capability's name parameter (role, mutex, etc).
  /// \param FunName -- The name of the function
  /// \param LockName -- A StringRef name for the lock expression, to be printed
  /// in the error message.
  /// \param Loc -- The location of the function call.
  virtual void handleFunExcludesLock(StringRef Kind, Name FunName,
                                     Name LockName, SourceLocation Loc) {}

  /// Warn that L1 cannot be acquired before L2.
  virtual void handleLockAcquiredBefore(StringRef Kind, Name L1Name,
                                        Name L2Name, SourceLocation Loc) {}

  /// Warn that there is a cycle in acquired_before/after dependencies.
  virtual void handleBeforeAfterCycle(Name L1Name, SourceLocation Loc) {}

  /// Called by the analysis when starting analysis of a function.
  /// Used to issue suggestions for changes to annotations.
  virtual void enterFunction(const FunctionDecl *FD) {}

  /// Called by the analysis when finishing analysis of a function.
  virtual void leaveFunction(const FunctionDecl *FD) {}

  bool issueBetaWarnings() { return IssueBetaWarnings; }
  void setIssueBetaWarnings(bool b) { IssueBetaWarnings = b; }

private:
  bool IssueBetaWarnings = false;
};

/// Check a function's CFG for thread-safety violations.
///
/// We traverse the blocks in the CFG, compute the set of mutexes that are held
/// at the end of each block, and issue warnings for thread safety violations.
/// Each block in the CFG is traversed exactly once.
void runThreadSafetyAnalysis(AnalysisDeclContext &AC,
                             ThreadSafetyHandler &Handler,
                             BeforeSet **Bset);

void threadSafetyCleanup(BeforeSet *Cache);

/// Helper function that returns a LockKind required for the given level
/// of access.
LockKind getLockKindFromAccessKind(AccessKind AK);

} // namespace threadSafety
} // namespace clang

#endif // LLVM_CLANG_ANALYSIS_ANALYSES_THREADSAFETY_H