BlockInCriticalSectionChecker.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  17. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  18. #include "clang/StaticAnalyzer/Core/Checker.h"
  19. #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
  20. #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
  21. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  22. using namespace clang;
  23. using namespace ento;
  24. namespace {
  25. class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
  26. mutable IdentifierInfo *IILockGuard, *IIUniqueLock;
  27. CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn,
  28. PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn,
  29. MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock;
  30. StringRef ClassLockGuard, ClassUniqueLock;
  31. mutable bool IdentifierInfoInitialized;
  32. std::unique_ptr<BugType> BlockInCritSectionBugType;
  33. void initIdentifierInfo(ASTContext &Ctx) const;
  34. void reportBlockInCritSection(SymbolRef FileDescSym,
  35. const CallEvent &call,
  36. CheckerContext &C) const;
  37. public:
  38. BlockInCriticalSectionChecker();
  39. bool isBlockingFunction(const CallEvent &Call) const;
  40. bool isLockFunction(const CallEvent &Call) const;
  41. bool isUnlockFunction(const CallEvent &Call) const;
  42. /// Process unlock.
  43. /// Process lock.
  44. /// Process blocking functions (sleep, getc, fgets, read, recv)
  45. void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
  46. };
  47. } // end anonymous namespace
  48. REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
  49. BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
  50. : IILockGuard(nullptr), IIUniqueLock(nullptr),
  51. LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"),
  52. FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"),
  53. PthreadLockFn("pthread_mutex_lock"),
  54. PthreadTryLockFn("pthread_mutex_trylock"),
  55. PthreadUnlockFn("pthread_mutex_unlock"),
  56. MtxLock("mtx_lock"),
  57. MtxTimedLock("mtx_timedlock"),
  58. MtxTryLock("mtx_trylock"),
  59. MtxUnlock("mtx_unlock"),
  60. ClassLockGuard("lock_guard"),
  61. ClassUniqueLock("unique_lock"),
  62. IdentifierInfoInitialized(false) {
  63. // Initialize the bug type.
  64. BlockInCritSectionBugType.reset(
  65. new BugType(this, "Call to blocking function in critical section",
  66. "Blocking Error"));
  67. }
  68. void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const {
  69. if (!IdentifierInfoInitialized) {
  70. /* In case of checking C code, or when the corresponding headers are not
  71. * included, we might end up query the identifier table every time when this
  72. * function is called instead of early returning it. To avoid this, a bool
  73. * variable (IdentifierInfoInitialized) is used and the function will be run
  74. * only once. */
  75. IILockGuard = &Ctx.Idents.get(ClassLockGuard);
  76. IIUniqueLock = &Ctx.Idents.get(ClassUniqueLock);
  77. IdentifierInfoInitialized = true;
  78. }
  79. }
  80. bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const {
  81. return matchesAny(Call, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn);
  82. }
  83. bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const {
  84. if (const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
  85. auto IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier();
  86. if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
  87. return true;
  88. }
  89. return matchesAny(Call, LockFn, PthreadLockFn, PthreadTryLockFn, MtxLock,
  90. MtxTimedLock, MtxTryLock);
  91. }
  92. bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
  93. if (const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
  94. const auto *DRecordDecl = cast<CXXRecordDecl>(Dtor->getDecl()->getParent());
  95. auto IdentifierInfo = DRecordDecl->getIdentifier();
  96. if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
  97. return true;
  98. }
  99. return matchesAny(Call, UnlockFn, PthreadUnlockFn, MtxUnlock);
  100. }
  101. void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
  102. CheckerContext &C) const {
  103. initIdentifierInfo(C.getASTContext());
  104. if (!isBlockingFunction(Call)
  105. && !isLockFunction(Call)
  106. && !isUnlockFunction(Call))
  107. return;
  108. ProgramStateRef State = C.getState();
  109. unsigned mutexCount = State->get<MutexCounter>();
  110. if (isUnlockFunction(Call) && mutexCount > 0) {
  111. State = State->set<MutexCounter>(--mutexCount);
  112. C.addTransition(State);
  113. } else if (isLockFunction(Call)) {
  114. State = State->set<MutexCounter>(++mutexCount);
  115. C.addTransition(State);
  116. } else if (mutexCount > 0) {
  117. SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol();
  118. reportBlockInCritSection(BlockDesc, Call, C);
  119. }
  120. }
  121. void BlockInCriticalSectionChecker::reportBlockInCritSection(
  122. SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
  123. ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
  124. if (!ErrNode)
  125. return;
  126. std::string msg;
  127. llvm::raw_string_ostream os(msg);
  128. os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName()
  129. << "' inside of critical section";
  130. auto R = std::make_unique<PathSensitiveBugReport>(*BlockInCritSectionBugType,
  131. os.str(), ErrNode);
  132. R->addRange(Call.getSourceRange());
  133. R->markInteresting(BlockDescSym);
  134. C.emitReport(std::move(R));
  135. }
  136. void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
  137. mgr.registerChecker<BlockInCriticalSectionChecker>();
  138. }
  139. bool ento::shouldRegisterBlockInCriticalSectionChecker(const CheckerManager &mgr) {
  140. return true;
  141. }