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