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