[clang] [clang][analyzer] Add note tags to alpha.unix.BlockInCriticalSection (PR #80029)
Endre Fülöp via cfe-commits
cfe-commits at lists.llvm.org
Sun Mar 10 13:09:03 PDT 2024
https://github.com/gamesh411 updated https://github.com/llvm/llvm-project/pull/80029
>From 346e2296869e750c7ec5bd75cf05f80a23b70569 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Tue, 30 Jan 2024 11:33:30 +0100
Subject: [PATCH] [clang][analyzer] Improve BlockInCriticalSectionsChecker with
multi-section and recursive mutex support
* Add support for multiple, potentially overlapping critical sections:
The checker can now simultaneously handle several mutex's critical
sections without confusing them.
* Implement the handling of recursive mutexes:
By identifying the lock events, recursive mutexes are now supported.
A lock event is a pair of a lock expression and the SVal of the mutex
that it locks, so even multiple locks of the same mutex (and even by
the same expression) is now supported.
* Refine the note tags generated by the checker:
The note tags now correctly show just for mutexes those are
active at point of error, and multiple acqisitions of the same mutex
are also noted.
---
.../BlockInCriticalSectionChecker.cpp | 391 ++++++++++++++----
.../Analysis/block-in-critical-section.cpp | 270 +++++++++---
2 files changed, 515 insertions(+), 146 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 66e080adb1382b..74ec4b73bd8b5f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -20,48 +20,180 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+
+#include <iterator>
+#include <utility>
+#include <variant>
using namespace clang;
using namespace ento;
namespace {
+
+struct CritSectionMarker {
+ const Expr *LockExpr{};
+ const MemRegion *LockReg{};
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.Add(LockExpr);
+ ID.Add(LockReg);
+ }
+
+ [[nodiscard]] constexpr bool
+ operator==(const CritSectionMarker &Other) const noexcept {
+ return LockExpr == Other.LockExpr && LockReg == Other.LockReg;
+ }
+ [[nodiscard]] constexpr bool
+ operator!=(const CritSectionMarker &Other) const noexcept {
+ return !(*this == Other);
+ }
+};
+
+class FirstArgMutexDescriptor {
+ CallDescription LockFn;
+ CallDescription UnlockFn;
+
+public:
+ FirstArgMutexDescriptor(CallDescription &&LockFn, CallDescription &&UnlockFn)
+ : LockFn(std::move(LockFn)), UnlockFn(std::move(UnlockFn)) {}
+ [[nodiscard]] bool matchesLock(const CallEvent &Call) const {
+ return LockFn.matches(Call) && Call.getNumArgs() > 0;
+ }
+ [[nodiscard]] bool matchesUnlock(const CallEvent &Call) const {
+ return UnlockFn.matches(Call) && Call.getNumArgs() > 0;
+ }
+ [[nodiscard]] const MemRegion *getLockRegion(const CallEvent &Call) const {
+ return Call.getArgSVal(0).getAsRegion();
+ }
+ [[nodiscard]] const MemRegion *getUnlockRegion(const CallEvent &Call) const {
+ return Call.getArgSVal(0).getAsRegion();
+ }
+};
+
+class MemberMutexDescriptor {
+ CallDescription LockFn;
+ CallDescription UnlockFn;
+
+public:
+ MemberMutexDescriptor(CallDescription &&LockFn, CallDescription &&UnlockFn)
+ : LockFn(std::move(LockFn)), UnlockFn(std::move(UnlockFn)) {}
+ [[nodiscard]] bool matchesLock(const CallEvent &Call) const {
+ return LockFn.matches(Call);
+ }
+ bool matchesUnlock(const CallEvent &Call) const {
+ return UnlockFn.matches(Call);
+ }
+ [[nodiscard]] const MemRegion *getLockRegion(const CallEvent &Call) const {
+ return cast<CXXMemberCall>(Call).getCXXThisVal().getAsRegion();
+ }
+ [[nodiscard]] const MemRegion *getUnlockRegion(const CallEvent &Call) const {
+ return cast<CXXMemberCall>(Call).getCXXThisVal().getAsRegion();
+ }
+};
+
+class RAIIMutexDescriptor {
+ mutable const IdentifierInfo *Guard{};
+ mutable bool IdentifierInfoInitialized{};
+ mutable llvm::SmallString<32> GuardName{};
+
+ void initIdentifierInfo(const CallEvent &Call) 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.
+ Guard = &Call.getCalleeAnalysisDeclContext()->getASTContext().Idents.get(
+ GuardName);
+ IdentifierInfoInitialized = true;
+ }
+ }
+
+public:
+ RAIIMutexDescriptor(StringRef GuardName) : GuardName(GuardName) {}
+ [[nodiscard]] bool matchesLock(const CallEvent &Call) const {
+ initIdentifierInfo(Call);
+ const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call);
+ if (!Ctor)
+ return false;
+ auto *IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier();
+ return IdentifierInfo == Guard;
+ }
+ [[nodiscard]] bool matchesUnlock(const CallEvent &Call) const {
+ initIdentifierInfo(Call);
+ const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call);
+ if (!Dtor)
+ return false;
+ auto *IdentifierInfo =
+ cast<CXXRecordDecl>(Dtor->getDecl()->getParent())->getIdentifier();
+ return IdentifierInfo == Guard;
+ }
+ [[nodiscard]] const MemRegion *getLockRegion(const CallEvent &Call) const {
+ const MemRegion *LockRegion = nullptr;
+ if (std::optional<SVal> Object = Call.getReturnValueUnderConstruction()) {
+ LockRegion = Object->getAsRegion();
+ }
+ return LockRegion;
+ }
+ [[nodiscard]] const MemRegion *getUnlockRegion(const CallEvent &Call) const {
+ return cast<CXXDestructorCall>(Call).getCXXThisVal().getAsRegion();
+ }
+};
+
+using MutexDescriptor =
+ std::variant<FirstArgMutexDescriptor, MemberMutexDescriptor,
+ RAIIMutexDescriptor>;
+
class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
- mutable IdentifierInfo *IILockGuard = nullptr;
- mutable IdentifierInfo *IIUniqueLock = nullptr;
- mutable bool IdentifierInfoInitialized = false;
-
- const CallDescription LockFn{{"lock"}};
- const CallDescription UnlockFn{{"unlock"}};
- const CallDescription SleepFn{{"sleep"}};
- const CallDescription GetcFn{{"getc"}};
- const CallDescription FgetsFn{{"fgets"}};
- const CallDescription ReadFn{{"read"}};
- const CallDescription RecvFn{{"recv"}};
- const CallDescription PthreadLockFn{{"pthread_mutex_lock"}};
- const CallDescription PthreadTryLockFn{{"pthread_mutex_trylock"}};
- const CallDescription PthreadUnlockFn{{"pthread_mutex_unlock"}};
- const CallDescription MtxLock{{"mtx_lock"}};
- const CallDescription MtxTimedLock{{"mtx_timedlock"}};
- const CallDescription MtxTryLock{{"mtx_trylock"}};
- const CallDescription MtxUnlock{{"mtx_unlock"}};
-
- const llvm::StringLiteral ClassLockGuard{"lock_guard"};
- const llvm::StringLiteral ClassUniqueLock{"unique_lock"};
+private:
+ const std::array<MutexDescriptor, 8> MutexDescriptors{
+ MemberMutexDescriptor(
+ CallDescription(/*QualifiedName=*/{"std", "mutex", "lock"},
+ /*RequiredArgs=*/0),
+ CallDescription({"std", "mutex", "unlock"}, 0)),
+ FirstArgMutexDescriptor(CallDescription({"pthread_mutex_lock"}, 1),
+ CallDescription({"pthread_mutex_unlock"}, 1)),
+ FirstArgMutexDescriptor(CallDescription({"mtx_lock"}, 1),
+ CallDescription({"mtx_unlock"}, 1)),
+ FirstArgMutexDescriptor(CallDescription({"pthread_mutex_trylock"}, 1),
+ CallDescription({"pthread_mutex_unlock"}, 1)),
+ FirstArgMutexDescriptor(CallDescription({"mtx_trylock"}, 1),
+ CallDescription({"mtx_unlock"}, 1)),
+ FirstArgMutexDescriptor(CallDescription({"mtx_timedlock"}, 1),
+ CallDescription({"mtx_unlock"}, 1)),
+ RAIIMutexDescriptor("lock_guard"),
+ RAIIMutexDescriptor("unique_lock")};
+
+ const std::array<CallDescription, 5> BlockingFunctions{
+ ArrayRef{StringRef{"sleep"}}, ArrayRef{StringRef{"getc"}},
+ ArrayRef{StringRef{"fgets"}}, ArrayRef{StringRef{"read"}},
+ ArrayRef{StringRef{"recv"}}};
const BugType BlockInCritSectionBugType{
this, "Call to blocking function in critical section", "Blocking Error"};
- void initIdentifierInfo(ASTContext &Ctx) const;
+ void reportBlockInCritSection(const CallEvent &call, CheckerContext &C) const;
- void reportBlockInCritSection(SymbolRef FileDescSym,
- const CallEvent &call,
- CheckerContext &C) const;
+ [[nodiscard]] const NoteTag *createCritSectionNote(CritSectionMarker M,
+ CheckerContext &C) const;
+ [[nodiscard]] std::optional<MutexDescriptor>
+ checkLock(const CallEvent &Call, CheckerContext &C) const;
+ void handleLock(const MutexDescriptor &Mutex, const CallEvent &Call,
+ CheckerContext &C) const;
+ [[nodiscard]] std::optional<MutexDescriptor>
+ checkUnlock(const CallEvent &Call, CheckerContext &C) const;
+ void handleUnlock(const MutexDescriptor &Mutex, const CallEvent &Call,
+ CheckerContext &C) const;
+ [[nodiscard]] bool isBlockingInCritSection(const CallEvent &Call,
+ CheckerContext &C) const;
public:
- 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)
@@ -70,73 +202,121 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
} // end anonymous namespace
-REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
+REGISTER_LIST_WITH_PROGRAMSTATE(ActiveCritSections, CritSectionMarker)
-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;
- }
-}
+namespace std {
+// Iterator traits for ImmutableList data structure
+// that enable the use of STL algorithms.
+// TODO: Move these to llvm::ImmutableList when overhauling immutable data
+// structures for proper iterator concept support.
+template <>
+struct iterator_traits<
+ typename llvm::ImmutableList<CritSectionMarker>::iterator> {
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = CritSectionMarker;
+ using difference_type = std::ptrdiff_t;
+ using reference = CritSectionMarker &;
+ using pointer = CritSectionMarker *;
+};
+} // namespace std
-bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const {
- return matchesAny(Call, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn);
+std::optional<MutexDescriptor>
+BlockInCriticalSectionChecker::checkLock(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *LockDescriptor =
+ llvm::find_if(MutexDescriptors, [&Call](auto &&LockFn) {
+ return std::visit(
+ [&Call](auto &&Descriptor) { return Descriptor.matchesLock(Call); },
+ LockFn);
+ });
+ if (LockDescriptor != MutexDescriptors.end())
+ return *LockDescriptor;
+ return std::nullopt;
}
-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;
- }
+void BlockInCriticalSectionChecker::handleLock(
+ const MutexDescriptor &LockDescriptor, const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *MutexRegion = std::visit(
+ [&Call](auto &&Descriptor) { return Descriptor.getLockRegion(Call); },
+ LockDescriptor);
+ if (!MutexRegion)
+ return;
+
+ const auto MarkToAdd = CritSectionMarker{Call.getOriginExpr(), MutexRegion};
+ ProgramStateRef StateWithLockEvent =
+ C.getState()->add<ActiveCritSections>(MarkToAdd);
+ C.addTransition(StateWithLockEvent, createCritSectionNote(MarkToAdd, C));
+}
- return matchesAny(Call, LockFn, PthreadLockFn, PthreadTryLockFn, MtxLock,
- MtxTimedLock, MtxTryLock);
+std::optional<MutexDescriptor>
+BlockInCriticalSectionChecker::checkUnlock(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *UnlockDescriptor =
+ llvm::find_if(MutexDescriptors, [&Call](auto &&UnlockFn) {
+ return std::visit(
+ [&Call](auto &&Descriptor) {
+ return Descriptor.matchesUnlock(Call);
+ },
+ UnlockFn);
+ });
+ if (UnlockDescriptor != MutexDescriptors.end())
+ return *UnlockDescriptor;
+ return std::nullopt;
}
-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;
+void BlockInCriticalSectionChecker::handleUnlock(
+ const MutexDescriptor &UnlockDescriptor, const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *MutexRegion = std::visit(
+ [&Call](auto &&Descriptor) { return Descriptor.getUnlockRegion(Call); },
+ UnlockDescriptor);
+ if (!MutexRegion)
+ return;
+
+ ProgramStateRef State = C.getState();
+ const auto ActiveSections = State->get<ActiveCritSections>();
+ const auto MostRecentLock =
+ llvm::find_if(ActiveSections, [MutexRegion](auto &&Marker) {
+ return Marker.LockReg == MutexRegion;
+ });
+ if (MostRecentLock == ActiveSections.end())
+ return;
+
+ // Build a new ImmutableList without this element.
+ auto &Factory = State->get_context<ActiveCritSections>();
+ llvm::ImmutableList<CritSectionMarker> NewList = Factory.getEmptyList();
+ for (auto It = ActiveSections.begin(), End = ActiveSections.end(); It != End;
+ ++It) {
+ if (It != MostRecentLock)
+ NewList = Factory.add(*It, NewList);
}
- return matchesAny(Call, UnlockFn, PthreadUnlockFn, MtxUnlock);
+ State = State->set<ActiveCritSections>(NewList);
+ C.addTransition(State);
+}
+
+bool BlockInCriticalSectionChecker::isBlockingInCritSection(
+ const CallEvent &Call, CheckerContext &C) const {
+ return llvm::any_of(BlockingFunctions,
+ [&Call](auto &&Fn) { return Fn.matches(Call); }) &&
+ !C.getState()->get<ActiveCritSections>().isEmpty();
}
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);
+ if (isBlockingInCritSection(Call, C)) {
+ reportBlockInCritSection(Call, C);
+ } else if (auto Lock = checkLock(Call, C)) {
+ handleLock(*Lock, Call, C);
+ } else if (auto Unlock = checkUnlock(Call, C)) {
+ handleUnlock(*Unlock, Call, C);
}
}
void BlockInCriticalSectionChecker::reportBlockInCritSection(
- SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
- ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
+ const CallEvent &Call, CheckerContext &C) const {
+ ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState());
if (!ErrNode)
return;
@@ -147,14 +327,63 @@ void BlockInCriticalSectionChecker::reportBlockInCritSection(
auto R = std::make_unique<PathSensitiveBugReport>(BlockInCritSectionBugType,
os.str(), ErrNode);
R->addRange(Call.getSourceRange());
- R->markInteresting(BlockDescSym);
+ R->markInteresting(Call.getReturnValue());
C.emitReport(std::move(R));
}
+const NoteTag *
+BlockInCriticalSectionChecker::createCritSectionNote(CritSectionMarker M,
+ CheckerContext &C) const {
+ const BugType *BT = &this->BlockInCritSectionBugType;
+ return C.getNoteTag([M, BT](PathSensitiveBugReport &BR,
+ llvm::raw_ostream &OS) {
+ if (&BR.getBugType() != BT)
+ return;
+
+ // Get the lock events for the mutex of the current line's lock event.
+ const auto CritSectionBegins =
+ BR.getErrorNode()->getState()->get<ActiveCritSections>();
+ llvm::SmallVector<CritSectionMarker, 4> LocksForMutex;
+ llvm::copy_if(
+ CritSectionBegins, std::back_inserter(LocksForMutex),
+ [M](const auto &Marker) { return Marker.LockReg == M.LockReg; });
+ if (LocksForMutex.empty())
+ return;
+
+ // As the ImmutableList builds the locks by prepending them, we
+ // reverse the list to get the correct order.
+ std::reverse(LocksForMutex.begin(), LocksForMutex.end());
+
+ // Find the index of the lock expression in the list of all locks for a
+ // given mutex (in acquisition order).
+ const auto *Position =
+ llvm::find_if(std::as_const(LocksForMutex), [M](const auto &Marker) {
+ return Marker.LockExpr == M.LockExpr;
+ });
+ if (Position == LocksForMutex.end())
+ return;
+
+ // If there is only one lock event, we don't need to specify how many times
+ // the critical section was entered.
+ if (LocksForMutex.size() == 1) {
+ OS << "Entering critical section here";
+ return;
+ }
+
+ const auto IndexOfLock =
+ std::distance(std::as_const(LocksForMutex).begin(), Position);
+
+ const auto OrdinalOfLock = IndexOfLock + 1;
+ OS << "Entering critical section for the " << OrdinalOfLock
+ << llvm::getOrdinalSuffix(OrdinalOfLock) << " time here";
+ });
+}
+
void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
mgr.registerChecker<BlockInCriticalSectionChecker>();
}
-bool ento::shouldRegisterBlockInCriticalSectionChecker(const CheckerManager &mgr) {
+bool ento::shouldRegisterBlockInCriticalSectionChecker(
+ const CheckerManager &mgr) {
return true;
}
diff --git a/clang/test/Analysis/block-in-critical-section.cpp b/clang/test/Analysis/block-in-critical-section.cpp
index fcf6188fc033ec..87c26b9f1b5209 100644
--- a/clang/test/Analysis/block-in-critical-section.cpp
+++ b/clang/test/Analysis/block-in-critical-section.cpp
@@ -1,4 +1,8 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.unix.BlockInCriticalSection -std=c++11 -verify %s
+// RUN: %clang_analyze_cc1 \
+// RUN: -analyzer-checker=alpha.unix.BlockInCriticalSection \
+// RUN: -std=c++11 \
+// RUN: -analyzer-output text \
+// RUN: -verify %s
void sleep(int x) {}
@@ -21,108 +25,242 @@ template<typename T>
struct not_real_lock {
not_real_lock<T>(std::mutex) {}
};
-}
+} // namespace std
+
+struct FILE;
+int getc(FILE *stream);
+char* fgets(char *str, FILE *stream);
+using ssize_t = long long;
+using size_t = unsigned long long;
+ssize_t read(int fd, void *buf, size_t count);
+ssize_t recv(int sockfd, void *buf, size_t len, int flags);
-void getc() {}
-void fgets() {}
-void read() {}
-void recv() {}
+struct pthread_mutex_t;
+void pthread_mutex_lock(pthread_mutex_t *mutex);
+void pthread_mutex_trylock(pthread_mutex_t *mutex);
+void pthread_mutex_unlock(pthread_mutex_t *mutex);
-void pthread_mutex_lock() {}
-void pthread_mutex_trylock() {}
-void pthread_mutex_unlock() {}
+struct mtx_t;
+void mtx_lock(mtx_t *mutex);
+void mtx_timedlock(mtx_t *mutex);
+void mtx_trylock(mtx_t *mutex);
+void mtx_unlock(mtx_t *mutex);
-void mtx_lock() {}
-void mtx_timedlock() {}
-void mtx_trylock() {}
-void mtx_unlock() {}
+// global params for dummy function calls
+FILE *stream;
+char *str;
+int fd;
+void *buf;
+size_t count;
+int sockfd;
+size_t len;
+int flags;
void testBlockInCriticalSectionWithStdMutex() {
std::mutex m;
- m.lock();
+ m.lock(); // expected-note 5{{Entering critical section here}}
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
- getc(); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
- fgets(); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
- read(); // expected-warning {{Call to blocking function 'read' inside of critical section}}
- recv(); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'getc' inside of critical section}}
+ fgets(str, stream); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'fgets' inside of critical section}}
+ read(fd, buf, count); // expected-warning {{Call to blocking function 'read' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'read' inside of critical section}}
+ recv(sockfd, buf, count, flags); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'recv' inside of critical section}}
m.unlock();
}
-void testBlockInCriticalSectionWithPthreadMutex() {
- pthread_mutex_lock();
+void testBlockInCriticalSectionWithPthreadMutex(pthread_mutex_t *mutex) {
+ pthread_mutex_lock(mutex); // expected-note 5{{Entering critical section here}}
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
- getc(); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
- fgets(); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
- read(); // expected-warning {{Call to blocking function 'read' inside of critical section}}
- recv(); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
- pthread_mutex_unlock();
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'getc' inside of critical section}}
+ fgets(str, stream); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'fgets' inside of critical section}}
+ read(fd, buf, count); // expected-warning {{Call to blocking function 'read' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'read' inside of critical section}}
+ recv(sockfd, buf, count, flags); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'recv' inside of critical section}}
+ pthread_mutex_unlock(mutex);
- pthread_mutex_trylock();
+ pthread_mutex_trylock(mutex); // expected-note 5{{Entering critical section here}}
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
- getc(); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
- fgets(); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
- read(); // expected-warning {{Call to blocking function 'read' inside of critical section}}
- recv(); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
- pthread_mutex_unlock();
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'getc' inside of critical section}}
+ fgets(str, stream); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'fgets' inside of critical section}}
+ read(fd, buf, count); // expected-warning {{Call to blocking function 'read' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'read' inside of critical section}}
+ recv(sockfd, buf, count, flags); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'recv' inside of critical section}}
+ pthread_mutex_unlock(mutex);
}
-void testBlockInCriticalSectionC11Locks() {
- mtx_lock();
+void testBlockInCriticalSectionC11Locks(mtx_t *mutex) {
+ mtx_lock(mutex); // expected-note 5{{Entering critical section here}}
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
- getc(); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
- fgets(); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
- read(); // expected-warning {{Call to blocking function 'read' inside of critical section}}
- recv(); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
- mtx_unlock();
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'getc' inside of critical section}}
+ fgets(str, stream); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'fgets' inside of critical section}}
+ read(fd, buf, count); // expected-warning {{Call to blocking function 'read' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'read' inside of critical section}}
+ recv(sockfd, buf, count, flags); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'recv' inside of critical section}}
+ mtx_unlock(mutex);
- mtx_timedlock();
+ mtx_timedlock(mutex); // expected-note 5{{Entering critical section here}}
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
- getc(); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
- fgets(); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
- read(); // expected-warning {{Call to blocking function 'read' inside of critical section}}
- recv(); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
- mtx_unlock();
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'getc' inside of critical section}}
+ fgets(str, stream); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'fgets' inside of critical section}}
+ read(fd, buf, count); // expected-warning {{Call to blocking function 'read' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'read' inside of critical section}}
+ recv(sockfd, buf, count, flags); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'recv' inside of critical section}}
+ mtx_unlock(mutex);
- mtx_trylock();
+ mtx_trylock(mutex); // expected-note 5{{Entering critical section here}}
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
- getc(); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
- fgets(); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
- read(); // expected-warning {{Call to blocking function 'read' inside of critical section}}
- recv(); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
- mtx_unlock();
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'getc' inside of critical section}}
+ fgets(str, stream); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'fgets' inside of critical section}}
+ read(fd, buf, count); // expected-warning {{Call to blocking function 'read' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'read' inside of critical section}}
+ recv(sockfd, buf, count, flags); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'recv' inside of critical section}}
+ mtx_unlock(mutex);
+}
+
+void testMultipleBlockingCalls() {
+ std::mutex m;
+ m.lock(); // expected-note 1{{Entering critical section here}}
+ sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ m.unlock();
+ sleep(2); // no-warning
}
-void testBlockInCriticalSectionWithNestedMutexes() {
+void testMultipleMutexesMultipleBlockingCalls() {
std::mutex m, n, k;
- m.lock();
- n.lock();
- k.lock();
- sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ m.lock(); // expected-note 2{{Entering critical section here}}
+ n.lock(); // expected-note 2{{Entering critical section here}}
+ k.lock(); // expected-note 1{{Entering critical section here}}
+ sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
k.unlock();
- sleep(5); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ sleep(2); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+}
+
+
+void testRecursiveAcquisition() {
+ std::mutex m;
+ m.lock(); // expected-note {{Entering critical section for the 1st time here}}
+ m.lock(); // expected-note {{Entering critical section for the 2nd time here}}
+ sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ m.unlock();
+ m.unlock();
+}
+
+void testRecursiveAcquisitionWithMultipleBlockingCalls() {
+ std::mutex m;
+ m.lock(); // expected-note 1{{Entering critical section for the 1st time here}}
+ // expected-note at -1 {{Entering critical section here}}
+ m.lock(); // expected-note 1{{Entering critical section for the 2nd time here}}
+ sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ m.unlock();
+ // this next 'sleep' call is only in the critical section of the first lock
+ sleep(2); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ m.unlock();
+}
+
+void testRecursiveAcquisitionWithMultipleMutexes() {
+ std::mutex m, n;
+ m.lock(); // expected-note 1{{Entering critical section here}}
+ n.lock(); // expected-note 2{{Entering critical section here}}
+ sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ m.unlock();
+ // this next 'sleep' call is only in the critical section of mutex 'n'
+ sleep(2); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ n.unlock();
+}
+
+
+void testNestedMutexes() {
+ std::mutex m, n, k;
+ m.lock(); // expected-note 3{{Entering critical section here}}
+ n.lock(); // expected-note 2{{Entering critical section here}}
+ k.lock(); // expected-note 1{{Entering critical section here}}
+ sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ k.unlock();
+ sleep(2); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
n.unlock();
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ m.unlock();
+ sleep(4); // no-warning
+}
+
+void testNonOverlappingMutexes() {
+ std::mutex m;
+ m.lock(); // There should be no warning here
+ m.unlock();
+ m.lock(); // expected-note {{Entering critical section here}}
+ sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ m.unlock();
+}
+
+void testMixedMutexLocksWithIntermittentUnlock() {
+ std::mutex m, n, k;
+ m.lock(); // expected-note {{Entering critical section here}}
+ n.lock(); // the problem is not is this lock's critical section
+ n.unlock();
+ k.lock(); // same as for n.lock()
+ k.unlock();
+ sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
m.unlock();
- sleep(3); // no-warning
}
void f() {
sleep(1000); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
}
void testBlockInCriticalSectionInterProcedural() {
std::mutex m;
- m.lock();
- f();
+ m.lock(); // expected-note {{Entering critical section here}}
+ f(); // expected-note {{Calling 'f'}}
m.unlock();
}
+void unknown_function_that_may_lock(std::mutex &);
void testBlockInCriticalSectionUnexpectedUnlock() {
std::mutex m;
+ unknown_function_that_may_lock(m);
m.unlock();
sleep(1); // no-warning
- m.lock();
- sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ m.lock(); // expected-note {{Entering critical section here}}
+ sleep(2); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
}
void testBlockInCriticalSectionLockGuard() {
@@ -130,12 +268,13 @@ void testBlockInCriticalSectionLockGuard() {
std::not_real_lock<std::mutex> not_real_lock(g_mutex);
sleep(1); // no-warning
- std::lock_guard<std::mutex> lock(g_mutex);
+ std::lock_guard<std::mutex> lock(g_mutex); // expected-note {{Entering critical section here}}
sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
}
void testBlockInCriticalSectionLockGuardNested() {
- testBlockInCriticalSectionLockGuard();
+ testBlockInCriticalSectionLockGuard(); // expected-note {{Calling 'testBlockInCriticalSectionLockGuard'}}
sleep(1); // no-warning
}
@@ -144,11 +283,12 @@ void testBlockInCriticalSectionUniqueLock() {
std::not_real_lock<std::mutex> not_real_lock(g_mutex);
sleep(1); // no-warning
- std::unique_lock<std::mutex> lock(g_mutex);
+ std::unique_lock<std::mutex> lock(g_mutex); // expected-note {{Entering critical section here}}
sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
}
void testBlockInCriticalSectionUniqueLockNested() {
- testBlockInCriticalSectionUniqueLock();
+ testBlockInCriticalSectionUniqueLock(); // expected-note {{Calling 'testBlockInCriticalSectionUniqueLock'}}
sleep(1); // no-warning
}
More information about the cfe-commits
mailing list