xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //===-- BlockInCriticalSectionChecker.cpp -----------------------*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // Defines a checker for blocks in critical sections. This checker should find
100b57cec5SDimitry Andric // the calls to blocking functions (for example: sleep, getc, fgets, read,
110b57cec5SDimitry Andric // recv etc.) inside a critical section. When sleep(x) is called while a mutex
120b57cec5SDimitry Andric // is held, other threades cannot lock the same mutex. This might take some
130b57cec5SDimitry Andric // time, leading to bad performance or even deadlock.
140b57cec5SDimitry Andric //
150b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
160b57cec5SDimitry Andric 
170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
20349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
23*0fca6ea1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
24*0fca6ea1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
25*0fca6ea1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
26*0fca6ea1SDimitry Andric #include "llvm/ADT/STLExtras.h"
27*0fca6ea1SDimitry Andric #include "llvm/ADT/SmallString.h"
28*0fca6ea1SDimitry Andric #include "llvm/ADT/StringExtras.h"
29*0fca6ea1SDimitry Andric 
30*0fca6ea1SDimitry Andric #include <iterator>
31*0fca6ea1SDimitry Andric #include <utility>
32*0fca6ea1SDimitry Andric #include <variant>
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric using namespace clang;
350b57cec5SDimitry Andric using namespace ento;
360b57cec5SDimitry Andric 
370b57cec5SDimitry Andric namespace {
38*0fca6ea1SDimitry Andric 
39*0fca6ea1SDimitry Andric struct CritSectionMarker {
40*0fca6ea1SDimitry Andric   const Expr *LockExpr{};
41*0fca6ea1SDimitry Andric   const MemRegion *LockReg{};
42*0fca6ea1SDimitry Andric 
43*0fca6ea1SDimitry Andric   void Profile(llvm::FoldingSetNodeID &ID) const {
44*0fca6ea1SDimitry Andric     ID.Add(LockExpr);
45*0fca6ea1SDimitry Andric     ID.Add(LockReg);
46*0fca6ea1SDimitry Andric   }
47*0fca6ea1SDimitry Andric 
48*0fca6ea1SDimitry Andric   [[nodiscard]] constexpr bool
49*0fca6ea1SDimitry Andric   operator==(const CritSectionMarker &Other) const noexcept {
50*0fca6ea1SDimitry Andric     return LockExpr == Other.LockExpr && LockReg == Other.LockReg;
51*0fca6ea1SDimitry Andric   }
52*0fca6ea1SDimitry Andric   [[nodiscard]] constexpr bool
53*0fca6ea1SDimitry Andric   operator!=(const CritSectionMarker &Other) const noexcept {
54*0fca6ea1SDimitry Andric     return !(*this == Other);
55*0fca6ea1SDimitry Andric   }
56*0fca6ea1SDimitry Andric };
57*0fca6ea1SDimitry Andric 
58*0fca6ea1SDimitry Andric class CallDescriptionBasedMatcher {
59*0fca6ea1SDimitry Andric   CallDescription LockFn;
60*0fca6ea1SDimitry Andric   CallDescription UnlockFn;
61*0fca6ea1SDimitry Andric 
62*0fca6ea1SDimitry Andric public:
63*0fca6ea1SDimitry Andric   CallDescriptionBasedMatcher(CallDescription &&LockFn,
64*0fca6ea1SDimitry Andric                               CallDescription &&UnlockFn)
65*0fca6ea1SDimitry Andric       : LockFn(std::move(LockFn)), UnlockFn(std::move(UnlockFn)) {}
66*0fca6ea1SDimitry Andric   [[nodiscard]] bool matches(const CallEvent &Call, bool IsLock) const {
67*0fca6ea1SDimitry Andric     if (IsLock) {
68*0fca6ea1SDimitry Andric       return LockFn.matches(Call);
69*0fca6ea1SDimitry Andric     }
70*0fca6ea1SDimitry Andric     return UnlockFn.matches(Call);
71*0fca6ea1SDimitry Andric   }
72*0fca6ea1SDimitry Andric };
73*0fca6ea1SDimitry Andric 
74*0fca6ea1SDimitry Andric class FirstArgMutexDescriptor : public CallDescriptionBasedMatcher {
75*0fca6ea1SDimitry Andric public:
76*0fca6ea1SDimitry Andric   FirstArgMutexDescriptor(CallDescription &&LockFn, CallDescription &&UnlockFn)
77*0fca6ea1SDimitry Andric       : CallDescriptionBasedMatcher(std::move(LockFn), std::move(UnlockFn)) {}
78*0fca6ea1SDimitry Andric 
79*0fca6ea1SDimitry Andric   [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call, bool) const {
80*0fca6ea1SDimitry Andric     return Call.getArgSVal(0).getAsRegion();
81*0fca6ea1SDimitry Andric   }
82*0fca6ea1SDimitry Andric };
83*0fca6ea1SDimitry Andric 
84*0fca6ea1SDimitry Andric class MemberMutexDescriptor : public CallDescriptionBasedMatcher {
85*0fca6ea1SDimitry Andric public:
86*0fca6ea1SDimitry Andric   MemberMutexDescriptor(CallDescription &&LockFn, CallDescription &&UnlockFn)
87*0fca6ea1SDimitry Andric       : CallDescriptionBasedMatcher(std::move(LockFn), std::move(UnlockFn)) {}
88*0fca6ea1SDimitry Andric 
89*0fca6ea1SDimitry Andric   [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call, bool) const {
90*0fca6ea1SDimitry Andric     return cast<CXXMemberCall>(Call).getCXXThisVal().getAsRegion();
91*0fca6ea1SDimitry Andric   }
92*0fca6ea1SDimitry Andric };
93*0fca6ea1SDimitry Andric 
94*0fca6ea1SDimitry Andric class RAIIMutexDescriptor {
95*0fca6ea1SDimitry Andric   mutable const IdentifierInfo *Guard{};
96*0fca6ea1SDimitry Andric   mutable bool IdentifierInfoInitialized{};
97*0fca6ea1SDimitry Andric   mutable llvm::SmallString<32> GuardName{};
98*0fca6ea1SDimitry Andric 
99*0fca6ea1SDimitry Andric   void initIdentifierInfo(const CallEvent &Call) const {
100*0fca6ea1SDimitry Andric     if (!IdentifierInfoInitialized) {
101*0fca6ea1SDimitry Andric       // In case of checking C code, or when the corresponding headers are not
102*0fca6ea1SDimitry Andric       // included, we might end up query the identifier table every time when
103*0fca6ea1SDimitry Andric       // this function is called instead of early returning it. To avoid this, a
104*0fca6ea1SDimitry Andric       // bool variable (IdentifierInfoInitialized) is used and the function will
105*0fca6ea1SDimitry Andric       // be run only once.
106*0fca6ea1SDimitry Andric       const auto &ASTCtx = Call.getState()->getStateManager().getContext();
107*0fca6ea1SDimitry Andric       Guard = &ASTCtx.Idents.get(GuardName);
108*0fca6ea1SDimitry Andric     }
109*0fca6ea1SDimitry Andric   }
110*0fca6ea1SDimitry Andric 
111*0fca6ea1SDimitry Andric   template <typename T> bool matchesImpl(const CallEvent &Call) const {
112*0fca6ea1SDimitry Andric     const T *C = dyn_cast<T>(&Call);
113*0fca6ea1SDimitry Andric     if (!C)
114*0fca6ea1SDimitry Andric       return false;
115*0fca6ea1SDimitry Andric     const IdentifierInfo *II =
116*0fca6ea1SDimitry Andric         cast<CXXRecordDecl>(C->getDecl()->getParent())->getIdentifier();
117*0fca6ea1SDimitry Andric     return II == Guard;
118*0fca6ea1SDimitry Andric   }
119*0fca6ea1SDimitry Andric 
120*0fca6ea1SDimitry Andric public:
121*0fca6ea1SDimitry Andric   RAIIMutexDescriptor(StringRef GuardName) : GuardName(GuardName) {}
122*0fca6ea1SDimitry Andric   [[nodiscard]] bool matches(const CallEvent &Call, bool IsLock) const {
123*0fca6ea1SDimitry Andric     initIdentifierInfo(Call);
124*0fca6ea1SDimitry Andric     if (IsLock) {
125*0fca6ea1SDimitry Andric       return matchesImpl<CXXConstructorCall>(Call);
126*0fca6ea1SDimitry Andric     }
127*0fca6ea1SDimitry Andric     return matchesImpl<CXXDestructorCall>(Call);
128*0fca6ea1SDimitry Andric   }
129*0fca6ea1SDimitry Andric   [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call,
130*0fca6ea1SDimitry Andric                                            bool IsLock) const {
131*0fca6ea1SDimitry Andric     const MemRegion *LockRegion = nullptr;
132*0fca6ea1SDimitry Andric     if (IsLock) {
133*0fca6ea1SDimitry Andric       if (std::optional<SVal> Object = Call.getReturnValueUnderConstruction()) {
134*0fca6ea1SDimitry Andric         LockRegion = Object->getAsRegion();
135*0fca6ea1SDimitry Andric       }
136*0fca6ea1SDimitry Andric     } else {
137*0fca6ea1SDimitry Andric       LockRegion = cast<CXXDestructorCall>(Call).getCXXThisVal().getAsRegion();
138*0fca6ea1SDimitry Andric     }
139*0fca6ea1SDimitry Andric     return LockRegion;
140*0fca6ea1SDimitry Andric   }
141*0fca6ea1SDimitry Andric };
142*0fca6ea1SDimitry Andric 
143*0fca6ea1SDimitry Andric using MutexDescriptor =
144*0fca6ea1SDimitry Andric     std::variant<FirstArgMutexDescriptor, MemberMutexDescriptor,
145*0fca6ea1SDimitry Andric                  RAIIMutexDescriptor>;
146*0fca6ea1SDimitry Andric 
1470b57cec5SDimitry Andric class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
148*0fca6ea1SDimitry Andric private:
149*0fca6ea1SDimitry Andric   const std::array<MutexDescriptor, 8> MutexDescriptors{
150*0fca6ea1SDimitry Andric       MemberMutexDescriptor({/*MatchAs=*/CDM::CXXMethod,
151*0fca6ea1SDimitry Andric                              /*QualifiedName=*/{"std", "mutex", "lock"},
152*0fca6ea1SDimitry Andric                              /*RequiredArgs=*/0},
153*0fca6ea1SDimitry Andric                             {CDM::CXXMethod, {"std", "mutex", "unlock"}, 0}),
154*0fca6ea1SDimitry Andric       FirstArgMutexDescriptor({CDM::CLibrary, {"pthread_mutex_lock"}, 1},
155*0fca6ea1SDimitry Andric                               {CDM::CLibrary, {"pthread_mutex_unlock"}, 1}),
156*0fca6ea1SDimitry Andric       FirstArgMutexDescriptor({CDM::CLibrary, {"mtx_lock"}, 1},
157*0fca6ea1SDimitry Andric                               {CDM::CLibrary, {"mtx_unlock"}, 1}),
158*0fca6ea1SDimitry Andric       FirstArgMutexDescriptor({CDM::CLibrary, {"pthread_mutex_trylock"}, 1},
159*0fca6ea1SDimitry Andric                               {CDM::CLibrary, {"pthread_mutex_unlock"}, 1}),
160*0fca6ea1SDimitry Andric       FirstArgMutexDescriptor({CDM::CLibrary, {"mtx_trylock"}, 1},
161*0fca6ea1SDimitry Andric                               {CDM::CLibrary, {"mtx_unlock"}, 1}),
162*0fca6ea1SDimitry Andric       FirstArgMutexDescriptor({CDM::CLibrary, {"mtx_timedlock"}, 1},
163*0fca6ea1SDimitry Andric                               {CDM::CLibrary, {"mtx_unlock"}, 1}),
164*0fca6ea1SDimitry Andric       RAIIMutexDescriptor("lock_guard"),
165*0fca6ea1SDimitry Andric       RAIIMutexDescriptor("unique_lock")};
1660b57cec5SDimitry Andric 
167*0fca6ea1SDimitry Andric   const CallDescriptionSet BlockingFunctions{{CDM::CLibrary, {"sleep"}},
168*0fca6ea1SDimitry Andric                                              {CDM::CLibrary, {"getc"}},
169*0fca6ea1SDimitry Andric                                              {CDM::CLibrary, {"fgets"}},
170*0fca6ea1SDimitry Andric                                              {CDM::CLibrary, {"read"}},
171*0fca6ea1SDimitry Andric                                              {CDM::CLibrary, {"recv"}}};
1720b57cec5SDimitry Andric 
173647cbc5dSDimitry Andric   const BugType BlockInCritSectionBugType{
174647cbc5dSDimitry Andric       this, "Call to blocking function in critical section", "Blocking Error"};
1750b57cec5SDimitry Andric 
176*0fca6ea1SDimitry Andric   void reportBlockInCritSection(const CallEvent &call, CheckerContext &C) const;
1770b57cec5SDimitry Andric 
178*0fca6ea1SDimitry Andric   [[nodiscard]] const NoteTag *createCritSectionNote(CritSectionMarker M,
179*0fca6ea1SDimitry Andric                                                      CheckerContext &C) const;
180*0fca6ea1SDimitry Andric 
181*0fca6ea1SDimitry Andric   [[nodiscard]] std::optional<MutexDescriptor>
182*0fca6ea1SDimitry Andric   checkDescriptorMatch(const CallEvent &Call, CheckerContext &C,
183*0fca6ea1SDimitry Andric                        bool IsLock) const;
184*0fca6ea1SDimitry Andric 
185*0fca6ea1SDimitry Andric   void handleLock(const MutexDescriptor &Mutex, const CallEvent &Call,
186*0fca6ea1SDimitry Andric                   CheckerContext &C) const;
187*0fca6ea1SDimitry Andric 
188*0fca6ea1SDimitry Andric   void handleUnlock(const MutexDescriptor &Mutex, const CallEvent &Call,
189*0fca6ea1SDimitry Andric                     CheckerContext &C) const;
190*0fca6ea1SDimitry Andric 
191*0fca6ea1SDimitry Andric   [[nodiscard]] bool isBlockingInCritSection(const CallEvent &Call,
1920b57cec5SDimitry Andric                                              CheckerContext &C) const;
1930b57cec5SDimitry Andric 
1940b57cec5SDimitry Andric public:
1950b57cec5SDimitry Andric   /// Process unlock.
1960b57cec5SDimitry Andric   /// Process lock.
1970b57cec5SDimitry Andric   /// Process blocking functions (sleep, getc, fgets, read, recv)
1980b57cec5SDimitry Andric   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
1990b57cec5SDimitry Andric };
2000b57cec5SDimitry Andric 
2010b57cec5SDimitry Andric } // end anonymous namespace
2020b57cec5SDimitry Andric 
203*0fca6ea1SDimitry Andric REGISTER_LIST_WITH_PROGRAMSTATE(ActiveCritSections, CritSectionMarker)
2040b57cec5SDimitry Andric 
205*0fca6ea1SDimitry Andric // Iterator traits for ImmutableList data structure
206*0fca6ea1SDimitry Andric // that enable the use of STL algorithms.
207*0fca6ea1SDimitry Andric // TODO: Move these to llvm::ImmutableList when overhauling immutable data
208*0fca6ea1SDimitry Andric // structures for proper iterator concept support.
209*0fca6ea1SDimitry Andric template <>
210*0fca6ea1SDimitry Andric struct std::iterator_traits<
211*0fca6ea1SDimitry Andric     typename llvm::ImmutableList<CritSectionMarker>::iterator> {
212*0fca6ea1SDimitry Andric   using iterator_category = std::forward_iterator_tag;
213*0fca6ea1SDimitry Andric   using value_type = CritSectionMarker;
214*0fca6ea1SDimitry Andric   using difference_type = std::ptrdiff_t;
215*0fca6ea1SDimitry Andric   using reference = CritSectionMarker &;
216*0fca6ea1SDimitry Andric   using pointer = CritSectionMarker *;
217*0fca6ea1SDimitry Andric };
218*0fca6ea1SDimitry Andric 
219*0fca6ea1SDimitry Andric std::optional<MutexDescriptor>
220*0fca6ea1SDimitry Andric BlockInCriticalSectionChecker::checkDescriptorMatch(const CallEvent &Call,
221*0fca6ea1SDimitry Andric                                                     CheckerContext &C,
222*0fca6ea1SDimitry Andric                                                     bool IsLock) const {
223*0fca6ea1SDimitry Andric   const auto Descriptor =
224*0fca6ea1SDimitry Andric       llvm::find_if(MutexDescriptors, [&Call, IsLock](auto &&Descriptor) {
225*0fca6ea1SDimitry Andric         return std::visit(
226*0fca6ea1SDimitry Andric             [&Call, IsLock](auto &&DescriptorImpl) {
227*0fca6ea1SDimitry Andric               return DescriptorImpl.matches(Call, IsLock);
228*0fca6ea1SDimitry Andric             },
229*0fca6ea1SDimitry Andric             Descriptor);
230*0fca6ea1SDimitry Andric       });
231*0fca6ea1SDimitry Andric   if (Descriptor != MutexDescriptors.end())
232*0fca6ea1SDimitry Andric     return *Descriptor;
233*0fca6ea1SDimitry Andric   return std::nullopt;
2340b57cec5SDimitry Andric }
2350b57cec5SDimitry Andric 
236*0fca6ea1SDimitry Andric static const MemRegion *getRegion(const CallEvent &Call,
237*0fca6ea1SDimitry Andric                                   const MutexDescriptor &Descriptor,
238*0fca6ea1SDimitry Andric                                   bool IsLock) {
239*0fca6ea1SDimitry Andric   return std::visit(
240*0fca6ea1SDimitry Andric       [&Call, IsLock](auto &&Descriptor) {
241*0fca6ea1SDimitry Andric         return Descriptor.getRegion(Call, IsLock);
242*0fca6ea1SDimitry Andric       },
243*0fca6ea1SDimitry Andric       Descriptor);
2440b57cec5SDimitry Andric }
2450b57cec5SDimitry Andric 
246*0fca6ea1SDimitry Andric void BlockInCriticalSectionChecker::handleLock(
247*0fca6ea1SDimitry Andric     const MutexDescriptor &LockDescriptor, const CallEvent &Call,
248*0fca6ea1SDimitry Andric     CheckerContext &C) const {
249*0fca6ea1SDimitry Andric   const MemRegion *MutexRegion =
250*0fca6ea1SDimitry Andric       getRegion(Call, LockDescriptor, /*IsLock=*/true);
251*0fca6ea1SDimitry Andric   if (!MutexRegion)
252*0fca6ea1SDimitry Andric     return;
253*0fca6ea1SDimitry Andric 
254*0fca6ea1SDimitry Andric   const CritSectionMarker MarkToAdd{Call.getOriginExpr(), MutexRegion};
255*0fca6ea1SDimitry Andric   ProgramStateRef StateWithLockEvent =
256*0fca6ea1SDimitry Andric       C.getState()->add<ActiveCritSections>(MarkToAdd);
257*0fca6ea1SDimitry Andric   C.addTransition(StateWithLockEvent, createCritSectionNote(MarkToAdd, C));
2580b57cec5SDimitry Andric }
2590b57cec5SDimitry Andric 
260*0fca6ea1SDimitry Andric void BlockInCriticalSectionChecker::handleUnlock(
261*0fca6ea1SDimitry Andric     const MutexDescriptor &UnlockDescriptor, const CallEvent &Call,
262*0fca6ea1SDimitry Andric     CheckerContext &C) const {
263*0fca6ea1SDimitry Andric   const MemRegion *MutexRegion =
264*0fca6ea1SDimitry Andric       getRegion(Call, UnlockDescriptor, /*IsLock=*/false);
265*0fca6ea1SDimitry Andric   if (!MutexRegion)
266*0fca6ea1SDimitry Andric     return;
267*0fca6ea1SDimitry Andric 
268*0fca6ea1SDimitry Andric   ProgramStateRef State = C.getState();
269*0fca6ea1SDimitry Andric   const auto ActiveSections = State->get<ActiveCritSections>();
270*0fca6ea1SDimitry Andric   const auto MostRecentLock =
271*0fca6ea1SDimitry Andric       llvm::find_if(ActiveSections, [MutexRegion](auto &&Marker) {
272*0fca6ea1SDimitry Andric         return Marker.LockReg == MutexRegion;
273*0fca6ea1SDimitry Andric       });
274*0fca6ea1SDimitry Andric   if (MostRecentLock == ActiveSections.end())
275*0fca6ea1SDimitry Andric     return;
276*0fca6ea1SDimitry Andric 
277*0fca6ea1SDimitry Andric   // Build a new ImmutableList without this element.
278*0fca6ea1SDimitry Andric   auto &Factory = State->get_context<ActiveCritSections>();
279*0fca6ea1SDimitry Andric   llvm::ImmutableList<CritSectionMarker> NewList = Factory.getEmptyList();
280*0fca6ea1SDimitry Andric   for (auto It = ActiveSections.begin(), End = ActiveSections.end(); It != End;
281*0fca6ea1SDimitry Andric        ++It) {
282*0fca6ea1SDimitry Andric     if (It != MostRecentLock)
283*0fca6ea1SDimitry Andric       NewList = Factory.add(*It, NewList);
2840b57cec5SDimitry Andric   }
2850b57cec5SDimitry Andric 
286*0fca6ea1SDimitry Andric   State = State->set<ActiveCritSections>(NewList);
287*0fca6ea1SDimitry Andric   C.addTransition(State);
2880b57cec5SDimitry Andric }
2890b57cec5SDimitry Andric 
290*0fca6ea1SDimitry Andric bool BlockInCriticalSectionChecker::isBlockingInCritSection(
291*0fca6ea1SDimitry Andric     const CallEvent &Call, CheckerContext &C) const {
292*0fca6ea1SDimitry Andric   return BlockingFunctions.contains(Call) &&
293*0fca6ea1SDimitry Andric          !C.getState()->get<ActiveCritSections>().isEmpty();
2940b57cec5SDimitry Andric }
2950b57cec5SDimitry Andric 
2960b57cec5SDimitry Andric void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
2970b57cec5SDimitry Andric                                                   CheckerContext &C) const {
298*0fca6ea1SDimitry Andric   if (isBlockingInCritSection(Call, C)) {
299*0fca6ea1SDimitry Andric     reportBlockInCritSection(Call, C);
300*0fca6ea1SDimitry Andric   } else if (std::optional<MutexDescriptor> LockDesc =
301*0fca6ea1SDimitry Andric                  checkDescriptorMatch(Call, C, /*IsLock=*/true)) {
302*0fca6ea1SDimitry Andric     handleLock(*LockDesc, Call, C);
303*0fca6ea1SDimitry Andric   } else if (std::optional<MutexDescriptor> UnlockDesc =
304*0fca6ea1SDimitry Andric                  checkDescriptorMatch(Call, C, /*IsLock=*/false)) {
305*0fca6ea1SDimitry Andric     handleUnlock(*UnlockDesc, Call, C);
3060b57cec5SDimitry Andric   }
3070b57cec5SDimitry Andric }
3080b57cec5SDimitry Andric 
3090b57cec5SDimitry Andric void BlockInCriticalSectionChecker::reportBlockInCritSection(
310*0fca6ea1SDimitry Andric     const CallEvent &Call, CheckerContext &C) const {
311*0fca6ea1SDimitry Andric   ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState());
3120b57cec5SDimitry Andric   if (!ErrNode)
3130b57cec5SDimitry Andric     return;
3140b57cec5SDimitry Andric 
3150b57cec5SDimitry Andric   std::string msg;
3160b57cec5SDimitry Andric   llvm::raw_string_ostream os(msg);
3170b57cec5SDimitry Andric   os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName()
3180b57cec5SDimitry Andric      << "' inside of critical section";
319647cbc5dSDimitry Andric   auto R = std::make_unique<PathSensitiveBugReport>(BlockInCritSectionBugType,
320a7dea167SDimitry Andric                                                     os.str(), ErrNode);
3210b57cec5SDimitry Andric   R->addRange(Call.getSourceRange());
322*0fca6ea1SDimitry Andric   R->markInteresting(Call.getReturnValue());
3230b57cec5SDimitry Andric   C.emitReport(std::move(R));
3240b57cec5SDimitry Andric }
3250b57cec5SDimitry Andric 
326*0fca6ea1SDimitry Andric const NoteTag *
327*0fca6ea1SDimitry Andric BlockInCriticalSectionChecker::createCritSectionNote(CritSectionMarker M,
328*0fca6ea1SDimitry Andric                                                      CheckerContext &C) const {
329*0fca6ea1SDimitry Andric   const BugType *BT = &this->BlockInCritSectionBugType;
330*0fca6ea1SDimitry Andric   return C.getNoteTag([M, BT](PathSensitiveBugReport &BR,
331*0fca6ea1SDimitry Andric                               llvm::raw_ostream &OS) {
332*0fca6ea1SDimitry Andric     if (&BR.getBugType() != BT)
333*0fca6ea1SDimitry Andric       return;
334*0fca6ea1SDimitry Andric 
335*0fca6ea1SDimitry Andric     // Get the lock events for the mutex of the current line's lock event.
336*0fca6ea1SDimitry Andric     const auto CritSectionBegins =
337*0fca6ea1SDimitry Andric         BR.getErrorNode()->getState()->get<ActiveCritSections>();
338*0fca6ea1SDimitry Andric     llvm::SmallVector<CritSectionMarker, 4> LocksForMutex;
339*0fca6ea1SDimitry Andric     llvm::copy_if(
340*0fca6ea1SDimitry Andric         CritSectionBegins, std::back_inserter(LocksForMutex),
341*0fca6ea1SDimitry Andric         [M](const auto &Marker) { return Marker.LockReg == M.LockReg; });
342*0fca6ea1SDimitry Andric     if (LocksForMutex.empty())
343*0fca6ea1SDimitry Andric       return;
344*0fca6ea1SDimitry Andric 
345*0fca6ea1SDimitry Andric     // As the ImmutableList builds the locks by prepending them, we
346*0fca6ea1SDimitry Andric     // reverse the list to get the correct order.
347*0fca6ea1SDimitry Andric     std::reverse(LocksForMutex.begin(), LocksForMutex.end());
348*0fca6ea1SDimitry Andric 
349*0fca6ea1SDimitry Andric     // Find the index of the lock expression in the list of all locks for a
350*0fca6ea1SDimitry Andric     // given mutex (in acquisition order).
351*0fca6ea1SDimitry Andric     const auto Position =
352*0fca6ea1SDimitry Andric         llvm::find_if(std::as_const(LocksForMutex), [M](const auto &Marker) {
353*0fca6ea1SDimitry Andric           return Marker.LockExpr == M.LockExpr;
354*0fca6ea1SDimitry Andric         });
355*0fca6ea1SDimitry Andric     if (Position == LocksForMutex.end())
356*0fca6ea1SDimitry Andric       return;
357*0fca6ea1SDimitry Andric 
358*0fca6ea1SDimitry Andric     // If there is only one lock event, we don't need to specify how many times
359*0fca6ea1SDimitry Andric     // the critical section was entered.
360*0fca6ea1SDimitry Andric     if (LocksForMutex.size() == 1) {
361*0fca6ea1SDimitry Andric       OS << "Entering critical section here";
362*0fca6ea1SDimitry Andric       return;
363*0fca6ea1SDimitry Andric     }
364*0fca6ea1SDimitry Andric 
365*0fca6ea1SDimitry Andric     const auto IndexOfLock =
366*0fca6ea1SDimitry Andric         std::distance(std::as_const(LocksForMutex).begin(), Position);
367*0fca6ea1SDimitry Andric 
368*0fca6ea1SDimitry Andric     const auto OrdinalOfLock = IndexOfLock + 1;
369*0fca6ea1SDimitry Andric     OS << "Entering critical section for the " << OrdinalOfLock
370*0fca6ea1SDimitry Andric        << llvm::getOrdinalSuffix(OrdinalOfLock) << " time here";
371*0fca6ea1SDimitry Andric   });
372*0fca6ea1SDimitry Andric }
373*0fca6ea1SDimitry Andric 
3740b57cec5SDimitry Andric void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
3750b57cec5SDimitry Andric   mgr.registerChecker<BlockInCriticalSectionChecker>();
3760b57cec5SDimitry Andric }
3770b57cec5SDimitry Andric 
378*0fca6ea1SDimitry Andric bool ento::shouldRegisterBlockInCriticalSectionChecker(
379*0fca6ea1SDimitry Andric     const CheckerManager &mgr) {
3800b57cec5SDimitry Andric   return true;
3810b57cec5SDimitry Andric }
382