xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp (revision 52418fc2be8efa5172b90a3a9e617017173612c4)
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"
230fca6ea1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
240fca6ea1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
250fca6ea1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
260fca6ea1SDimitry Andric #include "llvm/ADT/STLExtras.h"
270fca6ea1SDimitry Andric #include "llvm/ADT/SmallString.h"
280fca6ea1SDimitry Andric #include "llvm/ADT/StringExtras.h"
290fca6ea1SDimitry Andric 
300fca6ea1SDimitry Andric #include <iterator>
310fca6ea1SDimitry Andric #include <utility>
320fca6ea1SDimitry Andric #include <variant>
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric using namespace clang;
350b57cec5SDimitry Andric using namespace ento;
360b57cec5SDimitry Andric 
370b57cec5SDimitry Andric namespace {
380fca6ea1SDimitry Andric 
390fca6ea1SDimitry Andric struct CritSectionMarker {
400fca6ea1SDimitry Andric   const Expr *LockExpr{};
410fca6ea1SDimitry Andric   const MemRegion *LockReg{};
420fca6ea1SDimitry Andric 
Profile__anon83a18dc50111::CritSectionMarker430fca6ea1SDimitry Andric   void Profile(llvm::FoldingSetNodeID &ID) const {
440fca6ea1SDimitry Andric     ID.Add(LockExpr);
450fca6ea1SDimitry Andric     ID.Add(LockReg);
460fca6ea1SDimitry Andric   }
470fca6ea1SDimitry Andric 
480fca6ea1SDimitry Andric   [[nodiscard]] constexpr bool
operator ==__anon83a18dc50111::CritSectionMarker490fca6ea1SDimitry Andric   operator==(const CritSectionMarker &Other) const noexcept {
500fca6ea1SDimitry Andric     return LockExpr == Other.LockExpr && LockReg == Other.LockReg;
510fca6ea1SDimitry Andric   }
520fca6ea1SDimitry Andric   [[nodiscard]] constexpr bool
operator !=__anon83a18dc50111::CritSectionMarker530fca6ea1SDimitry Andric   operator!=(const CritSectionMarker &Other) const noexcept {
540fca6ea1SDimitry Andric     return !(*this == Other);
550fca6ea1SDimitry Andric   }
560fca6ea1SDimitry Andric };
570fca6ea1SDimitry Andric 
580fca6ea1SDimitry Andric class CallDescriptionBasedMatcher {
590fca6ea1SDimitry Andric   CallDescription LockFn;
600fca6ea1SDimitry Andric   CallDescription UnlockFn;
610fca6ea1SDimitry Andric 
620fca6ea1SDimitry Andric public:
CallDescriptionBasedMatcher(CallDescription && LockFn,CallDescription && UnlockFn)630fca6ea1SDimitry Andric   CallDescriptionBasedMatcher(CallDescription &&LockFn,
640fca6ea1SDimitry Andric                               CallDescription &&UnlockFn)
650fca6ea1SDimitry Andric       : LockFn(std::move(LockFn)), UnlockFn(std::move(UnlockFn)) {}
matches(const CallEvent & Call,bool IsLock) const660fca6ea1SDimitry Andric   [[nodiscard]] bool matches(const CallEvent &Call, bool IsLock) const {
670fca6ea1SDimitry Andric     if (IsLock) {
680fca6ea1SDimitry Andric       return LockFn.matches(Call);
690fca6ea1SDimitry Andric     }
700fca6ea1SDimitry Andric     return UnlockFn.matches(Call);
710fca6ea1SDimitry Andric   }
720fca6ea1SDimitry Andric };
730fca6ea1SDimitry Andric 
740fca6ea1SDimitry Andric class FirstArgMutexDescriptor : public CallDescriptionBasedMatcher {
750fca6ea1SDimitry Andric public:
FirstArgMutexDescriptor(CallDescription && LockFn,CallDescription && UnlockFn)760fca6ea1SDimitry Andric   FirstArgMutexDescriptor(CallDescription &&LockFn, CallDescription &&UnlockFn)
770fca6ea1SDimitry Andric       : CallDescriptionBasedMatcher(std::move(LockFn), std::move(UnlockFn)) {}
780fca6ea1SDimitry Andric 
getRegion(const CallEvent & Call,bool) const790fca6ea1SDimitry Andric   [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call, bool) const {
800fca6ea1SDimitry Andric     return Call.getArgSVal(0).getAsRegion();
810fca6ea1SDimitry Andric   }
820fca6ea1SDimitry Andric };
830fca6ea1SDimitry Andric 
840fca6ea1SDimitry Andric class MemberMutexDescriptor : public CallDescriptionBasedMatcher {
850fca6ea1SDimitry Andric public:
MemberMutexDescriptor(CallDescription && LockFn,CallDescription && UnlockFn)860fca6ea1SDimitry Andric   MemberMutexDescriptor(CallDescription &&LockFn, CallDescription &&UnlockFn)
870fca6ea1SDimitry Andric       : CallDescriptionBasedMatcher(std::move(LockFn), std::move(UnlockFn)) {}
880fca6ea1SDimitry Andric 
getRegion(const CallEvent & Call,bool) const890fca6ea1SDimitry Andric   [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call, bool) const {
900fca6ea1SDimitry Andric     return cast<CXXMemberCall>(Call).getCXXThisVal().getAsRegion();
910fca6ea1SDimitry Andric   }
920fca6ea1SDimitry Andric };
930fca6ea1SDimitry Andric 
940fca6ea1SDimitry Andric class RAIIMutexDescriptor {
950fca6ea1SDimitry Andric   mutable const IdentifierInfo *Guard{};
960fca6ea1SDimitry Andric   mutable bool IdentifierInfoInitialized{};
970fca6ea1SDimitry Andric   mutable llvm::SmallString<32> GuardName{};
980fca6ea1SDimitry Andric 
initIdentifierInfo(const CallEvent & Call) const990fca6ea1SDimitry Andric   void initIdentifierInfo(const CallEvent &Call) const {
1000fca6ea1SDimitry Andric     if (!IdentifierInfoInitialized) {
1010fca6ea1SDimitry Andric       // In case of checking C code, or when the corresponding headers are not
1020fca6ea1SDimitry Andric       // included, we might end up query the identifier table every time when
1030fca6ea1SDimitry Andric       // this function is called instead of early returning it. To avoid this, a
1040fca6ea1SDimitry Andric       // bool variable (IdentifierInfoInitialized) is used and the function will
1050fca6ea1SDimitry Andric       // be run only once.
1060fca6ea1SDimitry Andric       const auto &ASTCtx = Call.getState()->getStateManager().getContext();
1070fca6ea1SDimitry Andric       Guard = &ASTCtx.Idents.get(GuardName);
1080fca6ea1SDimitry Andric     }
1090fca6ea1SDimitry Andric   }
1100fca6ea1SDimitry Andric 
matchesImpl(const CallEvent & Call) const1110fca6ea1SDimitry Andric   template <typename T> bool matchesImpl(const CallEvent &Call) const {
1120fca6ea1SDimitry Andric     const T *C = dyn_cast<T>(&Call);
1130fca6ea1SDimitry Andric     if (!C)
1140fca6ea1SDimitry Andric       return false;
1150fca6ea1SDimitry Andric     const IdentifierInfo *II =
1160fca6ea1SDimitry Andric         cast<CXXRecordDecl>(C->getDecl()->getParent())->getIdentifier();
1170fca6ea1SDimitry Andric     return II == Guard;
1180fca6ea1SDimitry Andric   }
1190fca6ea1SDimitry Andric 
1200fca6ea1SDimitry Andric public:
RAIIMutexDescriptor(StringRef GuardName)1210fca6ea1SDimitry Andric   RAIIMutexDescriptor(StringRef GuardName) : GuardName(GuardName) {}
matches(const CallEvent & Call,bool IsLock) const1220fca6ea1SDimitry Andric   [[nodiscard]] bool matches(const CallEvent &Call, bool IsLock) const {
1230fca6ea1SDimitry Andric     initIdentifierInfo(Call);
1240fca6ea1SDimitry Andric     if (IsLock) {
1250fca6ea1SDimitry Andric       return matchesImpl<CXXConstructorCall>(Call);
1260fca6ea1SDimitry Andric     }
1270fca6ea1SDimitry Andric     return matchesImpl<CXXDestructorCall>(Call);
1280fca6ea1SDimitry Andric   }
getRegion(const CallEvent & Call,bool IsLock) const1290fca6ea1SDimitry Andric   [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call,
1300fca6ea1SDimitry Andric                                            bool IsLock) const {
1310fca6ea1SDimitry Andric     const MemRegion *LockRegion = nullptr;
1320fca6ea1SDimitry Andric     if (IsLock) {
1330fca6ea1SDimitry Andric       if (std::optional<SVal> Object = Call.getReturnValueUnderConstruction()) {
1340fca6ea1SDimitry Andric         LockRegion = Object->getAsRegion();
1350fca6ea1SDimitry Andric       }
1360fca6ea1SDimitry Andric     } else {
1370fca6ea1SDimitry Andric       LockRegion = cast<CXXDestructorCall>(Call).getCXXThisVal().getAsRegion();
1380fca6ea1SDimitry Andric     }
1390fca6ea1SDimitry Andric     return LockRegion;
1400fca6ea1SDimitry Andric   }
1410fca6ea1SDimitry Andric };
1420fca6ea1SDimitry Andric 
1430fca6ea1SDimitry Andric using MutexDescriptor =
1440fca6ea1SDimitry Andric     std::variant<FirstArgMutexDescriptor, MemberMutexDescriptor,
1450fca6ea1SDimitry Andric                  RAIIMutexDescriptor>;
1460fca6ea1SDimitry Andric 
1470b57cec5SDimitry Andric class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
1480fca6ea1SDimitry Andric private:
1490fca6ea1SDimitry Andric   const std::array<MutexDescriptor, 8> MutexDescriptors{
150*52418fc2SDimitry Andric       // NOTE: There are standard library implementations where some methods
151*52418fc2SDimitry Andric       // of `std::mutex` are inherited from an implementation detail base
152*52418fc2SDimitry Andric       // class, and those aren't matched by the name specification {"std",
153*52418fc2SDimitry Andric       // "mutex", "lock"}.
154*52418fc2SDimitry Andric       // As a workaround here we omit the class name and only require the
155*52418fc2SDimitry Andric       // presence of the name parts "std" and "lock"/"unlock".
156*52418fc2SDimitry Andric       // TODO: Ensure that CallDescription understands inherited methods.
157*52418fc2SDimitry Andric       MemberMutexDescriptor(
158*52418fc2SDimitry Andric           {/*MatchAs=*/CDM::CXXMethod,
159*52418fc2SDimitry Andric            /*QualifiedName=*/{"std", /*"mutex",*/ "lock"},
1600fca6ea1SDimitry Andric            /*RequiredArgs=*/0},
161*52418fc2SDimitry Andric           {CDM::CXXMethod, {"std", /*"mutex",*/ "unlock"}, 0}),
1620fca6ea1SDimitry Andric       FirstArgMutexDescriptor({CDM::CLibrary, {"pthread_mutex_lock"}, 1},
1630fca6ea1SDimitry Andric                               {CDM::CLibrary, {"pthread_mutex_unlock"}, 1}),
1640fca6ea1SDimitry Andric       FirstArgMutexDescriptor({CDM::CLibrary, {"mtx_lock"}, 1},
1650fca6ea1SDimitry Andric                               {CDM::CLibrary, {"mtx_unlock"}, 1}),
1660fca6ea1SDimitry Andric       FirstArgMutexDescriptor({CDM::CLibrary, {"pthread_mutex_trylock"}, 1},
1670fca6ea1SDimitry Andric                               {CDM::CLibrary, {"pthread_mutex_unlock"}, 1}),
1680fca6ea1SDimitry Andric       FirstArgMutexDescriptor({CDM::CLibrary, {"mtx_trylock"}, 1},
1690fca6ea1SDimitry Andric                               {CDM::CLibrary, {"mtx_unlock"}, 1}),
1700fca6ea1SDimitry Andric       FirstArgMutexDescriptor({CDM::CLibrary, {"mtx_timedlock"}, 1},
1710fca6ea1SDimitry Andric                               {CDM::CLibrary, {"mtx_unlock"}, 1}),
1720fca6ea1SDimitry Andric       RAIIMutexDescriptor("lock_guard"),
1730fca6ea1SDimitry Andric       RAIIMutexDescriptor("unique_lock")};
1740b57cec5SDimitry Andric 
1750fca6ea1SDimitry Andric   const CallDescriptionSet BlockingFunctions{{CDM::CLibrary, {"sleep"}},
1760fca6ea1SDimitry Andric                                              {CDM::CLibrary, {"getc"}},
1770fca6ea1SDimitry Andric                                              {CDM::CLibrary, {"fgets"}},
1780fca6ea1SDimitry Andric                                              {CDM::CLibrary, {"read"}},
1790fca6ea1SDimitry Andric                                              {CDM::CLibrary, {"recv"}}};
1800b57cec5SDimitry Andric 
181647cbc5dSDimitry Andric   const BugType BlockInCritSectionBugType{
182647cbc5dSDimitry Andric       this, "Call to blocking function in critical section", "Blocking Error"};
1830b57cec5SDimitry Andric 
1840fca6ea1SDimitry Andric   void reportBlockInCritSection(const CallEvent &call, CheckerContext &C) const;
1850b57cec5SDimitry Andric 
1860fca6ea1SDimitry Andric   [[nodiscard]] const NoteTag *createCritSectionNote(CritSectionMarker M,
1870fca6ea1SDimitry Andric                                                      CheckerContext &C) const;
1880fca6ea1SDimitry Andric 
1890fca6ea1SDimitry Andric   [[nodiscard]] std::optional<MutexDescriptor>
1900fca6ea1SDimitry Andric   checkDescriptorMatch(const CallEvent &Call, CheckerContext &C,
1910fca6ea1SDimitry Andric                        bool IsLock) const;
1920fca6ea1SDimitry Andric 
1930fca6ea1SDimitry Andric   void handleLock(const MutexDescriptor &Mutex, const CallEvent &Call,
1940fca6ea1SDimitry Andric                   CheckerContext &C) const;
1950fca6ea1SDimitry Andric 
1960fca6ea1SDimitry Andric   void handleUnlock(const MutexDescriptor &Mutex, const CallEvent &Call,
1970fca6ea1SDimitry Andric                     CheckerContext &C) const;
1980fca6ea1SDimitry Andric 
1990fca6ea1SDimitry Andric   [[nodiscard]] bool isBlockingInCritSection(const CallEvent &Call,
2000b57cec5SDimitry Andric                                              CheckerContext &C) const;
2010b57cec5SDimitry Andric 
2020b57cec5SDimitry Andric public:
2030b57cec5SDimitry Andric   /// Process unlock.
2040b57cec5SDimitry Andric   /// Process lock.
2050b57cec5SDimitry Andric   /// Process blocking functions (sleep, getc, fgets, read, recv)
2060b57cec5SDimitry Andric   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
2070b57cec5SDimitry Andric };
2080b57cec5SDimitry Andric 
2090b57cec5SDimitry Andric } // end anonymous namespace
2100b57cec5SDimitry Andric 
2110fca6ea1SDimitry Andric REGISTER_LIST_WITH_PROGRAMSTATE(ActiveCritSections, CritSectionMarker)
2120b57cec5SDimitry Andric 
2130fca6ea1SDimitry Andric // Iterator traits for ImmutableList data structure
2140fca6ea1SDimitry Andric // that enable the use of STL algorithms.
2150fca6ea1SDimitry Andric // TODO: Move these to llvm::ImmutableList when overhauling immutable data
2160fca6ea1SDimitry Andric // structures for proper iterator concept support.
2170fca6ea1SDimitry Andric template <>
2180fca6ea1SDimitry Andric struct std::iterator_traits<
2190fca6ea1SDimitry Andric     typename llvm::ImmutableList<CritSectionMarker>::iterator> {
2200fca6ea1SDimitry Andric   using iterator_category = std::forward_iterator_tag;
2210fca6ea1SDimitry Andric   using value_type = CritSectionMarker;
2220fca6ea1SDimitry Andric   using difference_type = std::ptrdiff_t;
2230fca6ea1SDimitry Andric   using reference = CritSectionMarker &;
2240fca6ea1SDimitry Andric   using pointer = CritSectionMarker *;
2250fca6ea1SDimitry Andric };
2260fca6ea1SDimitry Andric 
2270fca6ea1SDimitry Andric std::optional<MutexDescriptor>
checkDescriptorMatch(const CallEvent & Call,CheckerContext & C,bool IsLock) const2280fca6ea1SDimitry Andric BlockInCriticalSectionChecker::checkDescriptorMatch(const CallEvent &Call,
2290fca6ea1SDimitry Andric                                                     CheckerContext &C,
2300fca6ea1SDimitry Andric                                                     bool IsLock) const {
2310fca6ea1SDimitry Andric   const auto Descriptor =
2320fca6ea1SDimitry Andric       llvm::find_if(MutexDescriptors, [&Call, IsLock](auto &&Descriptor) {
2330fca6ea1SDimitry Andric         return std::visit(
2340fca6ea1SDimitry Andric             [&Call, IsLock](auto &&DescriptorImpl) {
2350fca6ea1SDimitry Andric               return DescriptorImpl.matches(Call, IsLock);
2360fca6ea1SDimitry Andric             },
2370fca6ea1SDimitry Andric             Descriptor);
2380fca6ea1SDimitry Andric       });
2390fca6ea1SDimitry Andric   if (Descriptor != MutexDescriptors.end())
2400fca6ea1SDimitry Andric     return *Descriptor;
2410fca6ea1SDimitry Andric   return std::nullopt;
2420b57cec5SDimitry Andric }
2430b57cec5SDimitry Andric 
getRegion(const CallEvent & Call,const MutexDescriptor & Descriptor,bool IsLock)2440fca6ea1SDimitry Andric static const MemRegion *getRegion(const CallEvent &Call,
2450fca6ea1SDimitry Andric                                   const MutexDescriptor &Descriptor,
2460fca6ea1SDimitry Andric                                   bool IsLock) {
2470fca6ea1SDimitry Andric   return std::visit(
2480fca6ea1SDimitry Andric       [&Call, IsLock](auto &&Descriptor) {
2490fca6ea1SDimitry Andric         return Descriptor.getRegion(Call, IsLock);
2500fca6ea1SDimitry Andric       },
2510fca6ea1SDimitry Andric       Descriptor);
2520b57cec5SDimitry Andric }
2530b57cec5SDimitry Andric 
handleLock(const MutexDescriptor & LockDescriptor,const CallEvent & Call,CheckerContext & C) const2540fca6ea1SDimitry Andric void BlockInCriticalSectionChecker::handleLock(
2550fca6ea1SDimitry Andric     const MutexDescriptor &LockDescriptor, const CallEvent &Call,
2560fca6ea1SDimitry Andric     CheckerContext &C) const {
2570fca6ea1SDimitry Andric   const MemRegion *MutexRegion =
2580fca6ea1SDimitry Andric       getRegion(Call, LockDescriptor, /*IsLock=*/true);
2590fca6ea1SDimitry Andric   if (!MutexRegion)
2600fca6ea1SDimitry Andric     return;
2610fca6ea1SDimitry Andric 
2620fca6ea1SDimitry Andric   const CritSectionMarker MarkToAdd{Call.getOriginExpr(), MutexRegion};
2630fca6ea1SDimitry Andric   ProgramStateRef StateWithLockEvent =
2640fca6ea1SDimitry Andric       C.getState()->add<ActiveCritSections>(MarkToAdd);
2650fca6ea1SDimitry Andric   C.addTransition(StateWithLockEvent, createCritSectionNote(MarkToAdd, C));
2660b57cec5SDimitry Andric }
2670b57cec5SDimitry Andric 
handleUnlock(const MutexDescriptor & UnlockDescriptor,const CallEvent & Call,CheckerContext & C) const2680fca6ea1SDimitry Andric void BlockInCriticalSectionChecker::handleUnlock(
2690fca6ea1SDimitry Andric     const MutexDescriptor &UnlockDescriptor, const CallEvent &Call,
2700fca6ea1SDimitry Andric     CheckerContext &C) const {
2710fca6ea1SDimitry Andric   const MemRegion *MutexRegion =
2720fca6ea1SDimitry Andric       getRegion(Call, UnlockDescriptor, /*IsLock=*/false);
2730fca6ea1SDimitry Andric   if (!MutexRegion)
2740fca6ea1SDimitry Andric     return;
2750fca6ea1SDimitry Andric 
2760fca6ea1SDimitry Andric   ProgramStateRef State = C.getState();
2770fca6ea1SDimitry Andric   const auto ActiveSections = State->get<ActiveCritSections>();
2780fca6ea1SDimitry Andric   const auto MostRecentLock =
2790fca6ea1SDimitry Andric       llvm::find_if(ActiveSections, [MutexRegion](auto &&Marker) {
2800fca6ea1SDimitry Andric         return Marker.LockReg == MutexRegion;
2810fca6ea1SDimitry Andric       });
2820fca6ea1SDimitry Andric   if (MostRecentLock == ActiveSections.end())
2830fca6ea1SDimitry Andric     return;
2840fca6ea1SDimitry Andric 
2850fca6ea1SDimitry Andric   // Build a new ImmutableList without this element.
2860fca6ea1SDimitry Andric   auto &Factory = State->get_context<ActiveCritSections>();
2870fca6ea1SDimitry Andric   llvm::ImmutableList<CritSectionMarker> NewList = Factory.getEmptyList();
2880fca6ea1SDimitry Andric   for (auto It = ActiveSections.begin(), End = ActiveSections.end(); It != End;
2890fca6ea1SDimitry Andric        ++It) {
2900fca6ea1SDimitry Andric     if (It != MostRecentLock)
2910fca6ea1SDimitry Andric       NewList = Factory.add(*It, NewList);
2920b57cec5SDimitry Andric   }
2930b57cec5SDimitry Andric 
2940fca6ea1SDimitry Andric   State = State->set<ActiveCritSections>(NewList);
2950fca6ea1SDimitry Andric   C.addTransition(State);
2960b57cec5SDimitry Andric }
2970b57cec5SDimitry Andric 
isBlockingInCritSection(const CallEvent & Call,CheckerContext & C) const2980fca6ea1SDimitry Andric bool BlockInCriticalSectionChecker::isBlockingInCritSection(
2990fca6ea1SDimitry Andric     const CallEvent &Call, CheckerContext &C) const {
3000fca6ea1SDimitry Andric   return BlockingFunctions.contains(Call) &&
3010fca6ea1SDimitry Andric          !C.getState()->get<ActiveCritSections>().isEmpty();
3020b57cec5SDimitry Andric }
3030b57cec5SDimitry Andric 
checkPostCall(const CallEvent & Call,CheckerContext & C) const3040b57cec5SDimitry Andric void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
3050b57cec5SDimitry Andric                                                   CheckerContext &C) const {
3060fca6ea1SDimitry Andric   if (isBlockingInCritSection(Call, C)) {
3070fca6ea1SDimitry Andric     reportBlockInCritSection(Call, C);
3080fca6ea1SDimitry Andric   } else if (std::optional<MutexDescriptor> LockDesc =
3090fca6ea1SDimitry Andric                  checkDescriptorMatch(Call, C, /*IsLock=*/true)) {
3100fca6ea1SDimitry Andric     handleLock(*LockDesc, Call, C);
3110fca6ea1SDimitry Andric   } else if (std::optional<MutexDescriptor> UnlockDesc =
3120fca6ea1SDimitry Andric                  checkDescriptorMatch(Call, C, /*IsLock=*/false)) {
3130fca6ea1SDimitry Andric     handleUnlock(*UnlockDesc, Call, C);
3140b57cec5SDimitry Andric   }
3150b57cec5SDimitry Andric }
3160b57cec5SDimitry Andric 
reportBlockInCritSection(const CallEvent & Call,CheckerContext & C) const3170b57cec5SDimitry Andric void BlockInCriticalSectionChecker::reportBlockInCritSection(
3180fca6ea1SDimitry Andric     const CallEvent &Call, CheckerContext &C) const {
3190fca6ea1SDimitry Andric   ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState());
3200b57cec5SDimitry Andric   if (!ErrNode)
3210b57cec5SDimitry Andric     return;
3220b57cec5SDimitry Andric 
3230b57cec5SDimitry Andric   std::string msg;
3240b57cec5SDimitry Andric   llvm::raw_string_ostream os(msg);
3250b57cec5SDimitry Andric   os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName()
3260b57cec5SDimitry Andric      << "' inside of critical section";
327647cbc5dSDimitry Andric   auto R = std::make_unique<PathSensitiveBugReport>(BlockInCritSectionBugType,
328a7dea167SDimitry Andric                                                     os.str(), ErrNode);
3290b57cec5SDimitry Andric   R->addRange(Call.getSourceRange());
3300fca6ea1SDimitry Andric   R->markInteresting(Call.getReturnValue());
3310b57cec5SDimitry Andric   C.emitReport(std::move(R));
3320b57cec5SDimitry Andric }
3330b57cec5SDimitry Andric 
3340fca6ea1SDimitry Andric const NoteTag *
createCritSectionNote(CritSectionMarker M,CheckerContext & C) const3350fca6ea1SDimitry Andric BlockInCriticalSectionChecker::createCritSectionNote(CritSectionMarker M,
3360fca6ea1SDimitry Andric                                                      CheckerContext &C) const {
3370fca6ea1SDimitry Andric   const BugType *BT = &this->BlockInCritSectionBugType;
3380fca6ea1SDimitry Andric   return C.getNoteTag([M, BT](PathSensitiveBugReport &BR,
3390fca6ea1SDimitry Andric                               llvm::raw_ostream &OS) {
3400fca6ea1SDimitry Andric     if (&BR.getBugType() != BT)
3410fca6ea1SDimitry Andric       return;
3420fca6ea1SDimitry Andric 
3430fca6ea1SDimitry Andric     // Get the lock events for the mutex of the current line's lock event.
3440fca6ea1SDimitry Andric     const auto CritSectionBegins =
3450fca6ea1SDimitry Andric         BR.getErrorNode()->getState()->get<ActiveCritSections>();
3460fca6ea1SDimitry Andric     llvm::SmallVector<CritSectionMarker, 4> LocksForMutex;
3470fca6ea1SDimitry Andric     llvm::copy_if(
3480fca6ea1SDimitry Andric         CritSectionBegins, std::back_inserter(LocksForMutex),
3490fca6ea1SDimitry Andric         [M](const auto &Marker) { return Marker.LockReg == M.LockReg; });
3500fca6ea1SDimitry Andric     if (LocksForMutex.empty())
3510fca6ea1SDimitry Andric       return;
3520fca6ea1SDimitry Andric 
3530fca6ea1SDimitry Andric     // As the ImmutableList builds the locks by prepending them, we
3540fca6ea1SDimitry Andric     // reverse the list to get the correct order.
3550fca6ea1SDimitry Andric     std::reverse(LocksForMutex.begin(), LocksForMutex.end());
3560fca6ea1SDimitry Andric 
3570fca6ea1SDimitry Andric     // Find the index of the lock expression in the list of all locks for a
3580fca6ea1SDimitry Andric     // given mutex (in acquisition order).
3590fca6ea1SDimitry Andric     const auto Position =
3600fca6ea1SDimitry Andric         llvm::find_if(std::as_const(LocksForMutex), [M](const auto &Marker) {
3610fca6ea1SDimitry Andric           return Marker.LockExpr == M.LockExpr;
3620fca6ea1SDimitry Andric         });
3630fca6ea1SDimitry Andric     if (Position == LocksForMutex.end())
3640fca6ea1SDimitry Andric       return;
3650fca6ea1SDimitry Andric 
3660fca6ea1SDimitry Andric     // If there is only one lock event, we don't need to specify how many times
3670fca6ea1SDimitry Andric     // the critical section was entered.
3680fca6ea1SDimitry Andric     if (LocksForMutex.size() == 1) {
3690fca6ea1SDimitry Andric       OS << "Entering critical section here";
3700fca6ea1SDimitry Andric       return;
3710fca6ea1SDimitry Andric     }
3720fca6ea1SDimitry Andric 
3730fca6ea1SDimitry Andric     const auto IndexOfLock =
3740fca6ea1SDimitry Andric         std::distance(std::as_const(LocksForMutex).begin(), Position);
3750fca6ea1SDimitry Andric 
3760fca6ea1SDimitry Andric     const auto OrdinalOfLock = IndexOfLock + 1;
3770fca6ea1SDimitry Andric     OS << "Entering critical section for the " << OrdinalOfLock
3780fca6ea1SDimitry Andric        << llvm::getOrdinalSuffix(OrdinalOfLock) << " time here";
3790fca6ea1SDimitry Andric   });
3800fca6ea1SDimitry Andric }
3810fca6ea1SDimitry Andric 
registerBlockInCriticalSectionChecker(CheckerManager & mgr)3820b57cec5SDimitry Andric void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
3830b57cec5SDimitry Andric   mgr.registerChecker<BlockInCriticalSectionChecker>();
3840b57cec5SDimitry Andric }
3850b57cec5SDimitry Andric 
shouldRegisterBlockInCriticalSectionChecker(const CheckerManager & mgr)3860fca6ea1SDimitry Andric bool ento::shouldRegisterBlockInCriticalSectionChecker(
3870fca6ea1SDimitry Andric     const CheckerManager &mgr) {
3880b57cec5SDimitry Andric   return true;
3890b57cec5SDimitry Andric }
390