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), LockFn({"lock"}), 68 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"}), MtxLock({"mtx_lock"}), 73 MtxTimedLock({"mtx_timedlock"}), MtxTryLock({"mtx_trylock"}), 74 MtxUnlock({"mtx_unlock"}), ClassLockGuard("lock_guard"), 75 ClassUniqueLock("unique_lock"), IdentifierInfoInitialized(false) { 76 // Initialize the bug type. 77 BlockInCritSectionBugType.reset( 78 new BugType(this, "Call to blocking function in critical section", 79 "Blocking Error")); 80 } 81 82 void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const { 83 if (!IdentifierInfoInitialized) { 84 /* In case of checking C code, or when the corresponding headers are not 85 * included, we might end up query the identifier table every time when this 86 * function is called instead of early returning it. To avoid this, a bool 87 * variable (IdentifierInfoInitialized) is used and the function will be run 88 * only once. */ 89 IILockGuard = &Ctx.Idents.get(ClassLockGuard); 90 IIUniqueLock = &Ctx.Idents.get(ClassUniqueLock); 91 IdentifierInfoInitialized = true; 92 } 93 } 94 95 bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const { 96 return matchesAny(Call, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn); 97 } 98 99 bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const { 100 if (const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { 101 auto IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier(); 102 if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock) 103 return true; 104 } 105 106 return matchesAny(Call, LockFn, PthreadLockFn, PthreadTryLockFn, MtxLock, 107 MtxTimedLock, MtxTryLock); 108 } 109 110 bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const { 111 if (const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) { 112 const auto *DRecordDecl = cast<CXXRecordDecl>(Dtor->getDecl()->getParent()); 113 auto IdentifierInfo = DRecordDecl->getIdentifier(); 114 if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock) 115 return true; 116 } 117 118 return matchesAny(Call, UnlockFn, PthreadUnlockFn, MtxUnlock); 119 } 120 121 void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call, 122 CheckerContext &C) const { 123 initIdentifierInfo(C.getASTContext()); 124 125 if (!isBlockingFunction(Call) 126 && !isLockFunction(Call) 127 && !isUnlockFunction(Call)) 128 return; 129 130 ProgramStateRef State = C.getState(); 131 unsigned mutexCount = State->get<MutexCounter>(); 132 if (isUnlockFunction(Call) && mutexCount > 0) { 133 State = State->set<MutexCounter>(--mutexCount); 134 C.addTransition(State); 135 } else if (isLockFunction(Call)) { 136 State = State->set<MutexCounter>(++mutexCount); 137 C.addTransition(State); 138 } else if (mutexCount > 0) { 139 SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol(); 140 reportBlockInCritSection(BlockDesc, Call, C); 141 } 142 } 143 144 void BlockInCriticalSectionChecker::reportBlockInCritSection( 145 SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const { 146 ExplodedNode *ErrNode = C.generateNonFatalErrorNode(); 147 if (!ErrNode) 148 return; 149 150 std::string msg; 151 llvm::raw_string_ostream os(msg); 152 os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName() 153 << "' inside of critical section"; 154 auto R = std::make_unique<PathSensitiveBugReport>(*BlockInCritSectionBugType, 155 os.str(), ErrNode); 156 R->addRange(Call.getSourceRange()); 157 R->markInteresting(BlockDescSym); 158 C.emitReport(std::move(R)); 159 } 160 161 void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) { 162 mgr.registerChecker<BlockInCriticalSectionChecker>(); 163 } 164 165 bool ento::shouldRegisterBlockInCriticalSectionChecker(const CheckerManager &mgr) { 166 return true; 167 } 168