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 check::PreCall> { 31 32 CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn, 33 PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn, 34 MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock; 35 36 std::unique_ptr<BugType> BlockInCritSectionBugType; 37 38 void reportBlockInCritSection(SymbolRef FileDescSym, 39 const CallEvent &call, 40 CheckerContext &C) const; 41 42 public: 43 BlockInCriticalSectionChecker(); 44 45 bool isBlockingFunction(const CallEvent &Call) const; 46 bool isLockFunction(const CallEvent &Call) const; 47 bool isUnlockFunction(const CallEvent &Call) const; 48 49 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 50 51 /// Process unlock. 52 /// Process lock. 53 /// Process blocking functions (sleep, getc, fgets, read, recv) 54 void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 55 56 }; 57 58 } // end anonymous namespace 59 60 REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned) 61 62 BlockInCriticalSectionChecker::BlockInCriticalSectionChecker() 63 : LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"), 64 FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"), 65 PthreadLockFn("pthread_mutex_lock"), 66 PthreadTryLockFn("pthread_mutex_trylock"), 67 PthreadUnlockFn("pthread_mutex_unlock"), 68 MtxLock("mtx_lock"), 69 MtxTimedLock("mtx_timedlock"), 70 MtxTryLock("mtx_trylock"), 71 MtxUnlock("mtx_unlock") { 72 // Initialize the bug type. 73 BlockInCritSectionBugType.reset( 74 new BugType(this, "Call to blocking function in critical section", 75 "Blocking Error")); 76 } 77 78 bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const { 79 if (Call.isCalled(SleepFn) 80 || Call.isCalled(GetcFn) 81 || Call.isCalled(FgetsFn) 82 || Call.isCalled(ReadFn) 83 || Call.isCalled(RecvFn)) { 84 return true; 85 } 86 return false; 87 } 88 89 bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const { 90 if (Call.isCalled(LockFn) 91 || Call.isCalled(PthreadLockFn) 92 || Call.isCalled(PthreadTryLockFn) 93 || Call.isCalled(MtxLock) 94 || Call.isCalled(MtxTimedLock) 95 || Call.isCalled(MtxTryLock)) { 96 return true; 97 } 98 return false; 99 } 100 101 bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const { 102 if (Call.isCalled(UnlockFn) 103 || Call.isCalled(PthreadUnlockFn) 104 || Call.isCalled(MtxUnlock)) { 105 return true; 106 } 107 return false; 108 } 109 110 void BlockInCriticalSectionChecker::checkPreCall(const CallEvent &Call, 111 CheckerContext &C) const { 112 } 113 114 void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call, 115 CheckerContext &C) const { 116 if (!isBlockingFunction(Call) 117 && !isLockFunction(Call) 118 && !isUnlockFunction(Call)) 119 return; 120 121 ProgramStateRef State = C.getState(); 122 unsigned mutexCount = State->get<MutexCounter>(); 123 if (isUnlockFunction(Call) && mutexCount > 0) { 124 State = State->set<MutexCounter>(--mutexCount); 125 C.addTransition(State); 126 } else if (isLockFunction(Call)) { 127 State = State->set<MutexCounter>(++mutexCount); 128 C.addTransition(State); 129 } else if (mutexCount > 0) { 130 SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol(); 131 reportBlockInCritSection(BlockDesc, Call, C); 132 } 133 } 134 135 void BlockInCriticalSectionChecker::reportBlockInCritSection( 136 SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const { 137 ExplodedNode *ErrNode = C.generateNonFatalErrorNode(); 138 if (!ErrNode) 139 return; 140 141 std::string msg; 142 llvm::raw_string_ostream os(msg); 143 os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName() 144 << "' inside of critical section"; 145 auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType, os.str(), ErrNode); 146 R->addRange(Call.getSourceRange()); 147 R->markInteresting(BlockDescSym); 148 C.emitReport(std::move(R)); 149 } 150 151 void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) { 152 mgr.registerChecker<BlockInCriticalSectionChecker>(); 153 } 154