summaryrefslogtreecommitdiffstats
path: root/gnu/llvm/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
diff options
context:
space:
mode:
authorpatrick <patrick@openbsd.org>2020-08-03 14:31:31 +0000
committerpatrick <patrick@openbsd.org>2020-08-03 14:31:31 +0000
commite5dd70708596ae51455a0ffa086a00c5b29f8583 (patch)
tree5d676f27b570bacf71e786c3b5cff3e6f6679b59 /gnu/llvm/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
parentImport LLVM 10.0.0 release including clang, lld and lldb. (diff)
downloadwireguard-openbsd-e5dd70708596ae51455a0ffa086a00c5b29f8583.tar.xz
wireguard-openbsd-e5dd70708596ae51455a0ffa086a00c5b29f8583.zip
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom tested by plenty
Diffstat (limited to 'gnu/llvm/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp')
-rw-r--r--gnu/llvm/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp189
1 files changed, 189 insertions, 0 deletions
diff --git a/gnu/llvm/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/gnu/llvm/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
new file mode 100644
index 00000000000..0eb3c3d1d0e
--- /dev/null
+++ b/gnu/llvm/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -0,0 +1,189 @@
+//===-- BlockInCriticalSectionChecker.cpp -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines a checker for blocks in critical sections. This checker should find
+// the calls to blocking functions (for example: sleep, getc, fgets, read,
+// recv etc.) inside a critical section. When sleep(x) is called while a mutex
+// is held, other threades cannot lock the same mutex. This might take some
+// time, leading to bad performance or even deadlock.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
+
+ mutable IdentifierInfo *IILockGuard, *IIUniqueLock;
+
+ CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn,
+ PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn,
+ MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock;
+
+ StringRef ClassLockGuard, ClassUniqueLock;
+
+ mutable bool IdentifierInfoInitialized;
+
+ std::unique_ptr<BugType> BlockInCritSectionBugType;
+
+ void initIdentifierInfo(ASTContext &Ctx) const;
+
+ void reportBlockInCritSection(SymbolRef FileDescSym,
+ const CallEvent &call,
+ CheckerContext &C) const;
+
+public:
+ BlockInCriticalSectionChecker();
+
+ bool isBlockingFunction(const CallEvent &Call) const;
+ bool isLockFunction(const CallEvent &Call) const;
+ bool isUnlockFunction(const CallEvent &Call) const;
+
+ /// Process unlock.
+ /// Process lock.
+ /// Process blocking functions (sleep, getc, fgets, read, recv)
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+};
+
+} // end anonymous namespace
+
+REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
+
+BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
+ : IILockGuard(nullptr), IIUniqueLock(nullptr),
+ LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"),
+ FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"),
+ PthreadLockFn("pthread_mutex_lock"),
+ PthreadTryLockFn("pthread_mutex_trylock"),
+ PthreadUnlockFn("pthread_mutex_unlock"),
+ MtxLock("mtx_lock"),
+ MtxTimedLock("mtx_timedlock"),
+ MtxTryLock("mtx_trylock"),
+ MtxUnlock("mtx_unlock"),
+ ClassLockGuard("lock_guard"),
+ ClassUniqueLock("unique_lock"),
+ IdentifierInfoInitialized(false) {
+ // Initialize the bug type.
+ BlockInCritSectionBugType.reset(
+ new BugType(this, "Call to blocking function in critical section",
+ "Blocking Error"));
+}
+
+void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const {
+ if (!IdentifierInfoInitialized) {
+ /* In case of checking C code, or when the corresponding headers are not
+ * included, we might end up query the identifier table every time when this
+ * function is called instead of early returning it. To avoid this, a bool
+ * variable (IdentifierInfoInitialized) is used and the function will be run
+ * only once. */
+ IILockGuard = &Ctx.Idents.get(ClassLockGuard);
+ IIUniqueLock = &Ctx.Idents.get(ClassUniqueLock);
+ IdentifierInfoInitialized = true;
+ }
+}
+
+bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const {
+ if (Call.isCalled(SleepFn)
+ || Call.isCalled(GetcFn)
+ || Call.isCalled(FgetsFn)
+ || Call.isCalled(ReadFn)
+ || Call.isCalled(RecvFn)) {
+ return true;
+ }
+ return false;
+}
+
+bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const {
+ if (const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
+ auto IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier();
+ if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
+ return true;
+ }
+
+ if (Call.isCalled(LockFn)
+ || Call.isCalled(PthreadLockFn)
+ || Call.isCalled(PthreadTryLockFn)
+ || Call.isCalled(MtxLock)
+ || Call.isCalled(MtxTimedLock)
+ || Call.isCalled(MtxTryLock)) {
+ return true;
+ }
+ return false;
+}
+
+bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
+ if (const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
+ const auto *DRecordDecl = cast<CXXRecordDecl>(Dtor->getDecl()->getParent());
+ auto IdentifierInfo = DRecordDecl->getIdentifier();
+ if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
+ return true;
+ }
+
+ if (Call.isCalled(UnlockFn)
+ || Call.isCalled(PthreadUnlockFn)
+ || Call.isCalled(MtxUnlock)) {
+ return true;
+ }
+ return false;
+}
+
+void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ initIdentifierInfo(C.getASTContext());
+
+ if (!isBlockingFunction(Call)
+ && !isLockFunction(Call)
+ && !isUnlockFunction(Call))
+ return;
+
+ ProgramStateRef State = C.getState();
+ unsigned mutexCount = State->get<MutexCounter>();
+ if (isUnlockFunction(Call) && mutexCount > 0) {
+ State = State->set<MutexCounter>(--mutexCount);
+ C.addTransition(State);
+ } else if (isLockFunction(Call)) {
+ State = State->set<MutexCounter>(++mutexCount);
+ C.addTransition(State);
+ } else if (mutexCount > 0) {
+ SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol();
+ reportBlockInCritSection(BlockDesc, Call, C);
+ }
+}
+
+void BlockInCriticalSectionChecker::reportBlockInCritSection(
+ SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
+ ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
+ if (!ErrNode)
+ return;
+
+ std::string msg;
+ llvm::raw_string_ostream os(msg);
+ os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName()
+ << "' inside of critical section";
+ auto R = std::make_unique<PathSensitiveBugReport>(*BlockInCritSectionBugType,
+ os.str(), ErrNode);
+ R->addRange(Call.getSourceRange());
+ R->markInteresting(BlockDescSym);
+ C.emitReport(std::move(R));
+}
+
+void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
+ mgr.registerChecker<BlockInCriticalSectionChecker>();
+}
+
+bool ento::shouldRegisterBlockInCriticalSectionChecker(const LangOptions &LO) {
+ return true;
+}