1 //===-- BlockInCriticalSectionChecker.cpp -----------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // Defines a checker for blocks in critical sections. This checker should find 11 // the calls to blocking functions (for example: sleep, getc, fgets, read, 12 // recv etc.) inside a critical section. When sleep(x) is called while a mutex 13 // is held, other threades cannot lock the same mutex. This might take some 14 // time, leading to bad performance or even deadlock. 15 // 16 //===----------------------------------------------------------------------===// 17 18 #include "ClangSACheckers.h" 19 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 20 #include "clang/StaticAnalyzer/Core/Checker.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 if (Call.isCalled(SleepFn) 101 || Call.isCalled(GetcFn) 102 || Call.isCalled(FgetsFn) 103 || Call.isCalled(ReadFn) 104 || Call.isCalled(RecvFn)) { 105 return true; 106 } 107 return false; 108 } 109 110 bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const { 111 if (const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { 112 auto IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier(); 113 if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock) 114 return true; 115 } 116 117 if (Call.isCalled(LockFn) 118 || Call.isCalled(PthreadLockFn) 119 || Call.isCalled(PthreadTryLockFn) 120 || Call.isCalled(MtxLock) 121 || Call.isCalled(MtxTimedLock) 122 || Call.isCalled(MtxTryLock)) { 123 return true; 124 } 125 return false; 126 } 127 128 bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const { 129 if (const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) { 130 const auto *DRecordDecl = dyn_cast<CXXRecordDecl>(Dtor->getDecl()->getParent()); 131 auto IdentifierInfo = DRecordDecl->getIdentifier(); 132 if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock) 133 return true; 134 } 135 136 if (Call.isCalled(UnlockFn) 137 || Call.isCalled(PthreadUnlockFn) 138 || Call.isCalled(MtxUnlock)) { 139 return true; 140 } 141 return false; 142 } 143 144 void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call, 145 CheckerContext &C) const { 146 initIdentifierInfo(C.getASTContext()); 147 148 if (!isBlockingFunction(Call) 149 && !isLockFunction(Call) 150 && !isUnlockFunction(Call)) 151 return; 152 153 ProgramStateRef State = C.getState(); 154 unsigned mutexCount = State->get<MutexCounter>(); 155 if (isUnlockFunction(Call) && mutexCount > 0) { 156 State = State->set<MutexCounter>(--mutexCount); 157 C.addTransition(State); 158 } else if (isLockFunction(Call)) { 159 State = State->set<MutexCounter>(++mutexCount); 160 C.addTransition(State); 161 } else if (mutexCount > 0) { 162 SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol(); 163 reportBlockInCritSection(BlockDesc, Call, C); 164 } 165 } 166 167 void BlockInCriticalSectionChecker::reportBlockInCritSection( 168 SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const { 169 ExplodedNode *ErrNode = C.generateNonFatalErrorNode(); 170 if (!ErrNode) 171 return; 172 173 std::string msg; 174 llvm::raw_string_ostream os(msg); 175 os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName() 176 << "' inside of critical section"; 177 auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType, os.str(), ErrNode); 178 R->addRange(Call.getSourceRange()); 179 R->markInteresting(BlockDescSym); 180 C.emitReport(std::move(R)); 181 } 182 183 void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) { 184 mgr.registerChecker<BlockInCriticalSectionChecker>(); 185 } 186