BlockInCriticalSectionChecker.cpp 6.4 KB

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