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 34 std::unique_ptr<BugType> BlockInCritSectionBugType; 35 36 void reportBlockInCritSection(SymbolRef FileDescSym, 37 const CallEvent &call, 38 CheckerContext &C) const; 39 40 public: 41 BlockInCriticalSectionChecker(); 42 43 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 44 45 /// Process unlock. 46 /// Process lock. 47 /// Process blocking functions (sleep, getc, fgets, read, recv) 48 void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 49 50 }; 51 52 } // end anonymous namespace 53 54 REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned) 55 56 BlockInCriticalSectionChecker::BlockInCriticalSectionChecker() 57 : LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"), 58 FgetsFn("fgets"), ReadFn("read"), RecvFn("recv") { 59 // Initialize the bug type. 60 BlockInCritSectionBugType.reset( 61 new BugType(this, "Call to blocking function in critical section", 62 "Blocking Error")); 63 } 64 65 void BlockInCriticalSectionChecker::checkPreCall(const CallEvent &Call, 66 CheckerContext &C) const { 67 } 68 69 void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call, 70 CheckerContext &C) const { 71 if (!Call.isCalled(LockFn) 72 && !Call.isCalled(SleepFn) 73 && !Call.isCalled(GetcFn) 74 && !Call.isCalled(FgetsFn) 75 && !Call.isCalled(ReadFn) 76 && !Call.isCalled(RecvFn) 77 && !Call.isCalled(UnlockFn)) 78 return; 79 80 ProgramStateRef State = C.getState(); 81 unsigned mutexCount = State->get<MutexCounter>(); 82 if (Call.isCalled(UnlockFn) && mutexCount > 0) { 83 State = State->set<MutexCounter>(--mutexCount); 84 C.addTransition(State); 85 } else if (Call.isCalled(LockFn)) { 86 State = State->set<MutexCounter>(++mutexCount); 87 C.addTransition(State); 88 } else if (mutexCount > 0) { 89 SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol(); 90 reportBlockInCritSection(BlockDesc, Call, C); 91 } 92 } 93 94 void BlockInCriticalSectionChecker::reportBlockInCritSection( 95 SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const { 96 ExplodedNode *ErrNode = C.generateNonFatalErrorNode(); 97 if (!ErrNode) 98 return; 99 100 auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType, 101 "A blocking function %s is called inside a critical section.", ErrNode); 102 R->addRange(Call.getSourceRange()); 103 R->markInteresting(BlockDescSym); 104 C.emitReport(std::move(R)); 105 } 106 107 void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) { 108 mgr.registerChecker<BlockInCriticalSectionChecker>(); 109 } 110