[clang] [clang][analyzer][WIP] Introduce modeling for threading related checkers (PR #109636)

Endre Fülöp via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 23 01:39:42 PDT 2024


https://github.com/gamesh411 created https://github.com/llvm/llvm-project/pull/109636

None

>From 8b5fe13e95a251800f693b32a84b7c8f0e3076d3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Mon, 3 Jun 2024 16:05:51 +0200
Subject: [PATCH 01/30] introduce mutex modeling checker

move mutex modeling to non-alpha

add checker to bookkeeping lists
---
 .../clang/StaticAnalyzer/Checkers/Checkers.td |  6 +++++
 .../BlockInCriticalSectionChecker.cpp         |  2 ++
 .../StaticAnalyzer/Checkers/MutexModeling.h   | 23 +++++++++++++++++++
 .../test/Analysis/analyzer-enabled-checkers.c |  1 +
 ...c-library-functions-arg-enabled-checkers.c |  1 +
 5 files changed, 33 insertions(+)
 create mode 100644 clang/lib/StaticAnalyzer/Checkers/MutexModeling.h

diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 38b55a0eb0a7b0..96dfa3a35ea1ef 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -509,8 +509,14 @@ def UnixAPIMisuseChecker : Checker<"API">,
   HelpText<"Check calls to various UNIX/Posix functions">,
   Documentation<HasDocumentation>;
 
+def MutexModeling : Checker<"MutexModeling">,
+  HelpText<"Model mutexes lock and unlock events">,
+  Documentation<NotDocumented>,
+  Hidden;
+
 def BlockInCriticalSectionChecker : Checker<"BlockInCriticalSection">,
   HelpText<"Check for calls to blocking functions inside a critical section">,
+  Dependencies<[MutexModeling]>,
   Documentation<HasDocumentation>;
 
 def DynamicMemoryModeling: Checker<"DynamicMemoryModeling">,
diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 40f7e9cede1f12..d69a6c81a9f1c2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -27,6 +27,8 @@
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
 
+#include "MutexModeling.h"
+
 #include <iterator>
 #include <utility>
 #include <variant>
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.h
new file mode 100644
index 00000000000000..9d7e58e56eff6f
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.h
@@ -0,0 +1,23 @@
+//===--- MutexModeling.h - Modeling of mutexes ----------------------------===//
+//
+// 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 inter-checker API for tracking and manipulating the
+// modeled state of locked mutexes in the GDM.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELING_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELING_H
+
+namespace clang {
+namespace ento {
+namespace mutex_modeling {} // namespace mutex_modeling
+} // namespace ento
+} // namespace clang
+
+#endif
diff --git a/clang/test/Analysis/analyzer-enabled-checkers.c b/clang/test/Analysis/analyzer-enabled-checkers.c
index e605c62a66ad0e..5fe24318a807b3 100644
--- a/clang/test/Analysis/analyzer-enabled-checkers.c
+++ b/clang/test/Analysis/analyzer-enabled-checkers.c
@@ -42,6 +42,7 @@
 // CHECK-NEXT: security.insecureAPI.mktemp
 // CHECK-NEXT: security.insecureAPI.vfork
 // CHECK-NEXT: unix.API
+// CHECK-NEXT: unix.MutexModeling
 // CHECK-NEXT: unix.BlockInCriticalSection
 // CHECK-NEXT: unix.cstring.CStringModeling
 // CHECK-NEXT: unix.DynamicMemoryModeling
diff --git a/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c b/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c
index 345a4e8f44efd1..c3bacb0706782b 100644
--- a/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c
+++ b/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c
@@ -50,6 +50,7 @@
 // CHECK-NEXT: security.insecureAPI.mktemp
 // CHECK-NEXT: security.insecureAPI.vfork
 // CHECK-NEXT: unix.API
+// CHECK-NEXT: unix.MutexModeling
 // CHECK-NEXT: unix.BlockInCriticalSection
 // CHECK-NEXT: unix.cstring.CStringModeling
 // CHECK-NEXT: unix.DynamicMemoryModeling

>From 89564b5ca4b66f37de35978bd0c48baf4b8786cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Thu, 6 Jun 2024 09:39:29 +0200
Subject: [PATCH 02/30] copy the modeling part of BlockInCriticalSection
 checker to mutex modeling

move the GDM trait out of anonymous namespace to solve state sharing between checkers

add programstate header for the base template definition of ProgramStateTrait
---
 .../StaticAnalyzer/Checkers/CMakeLists.txt    |   1 +
 .../StaticAnalyzer/Checkers/MutexModeling.cpp | 237 ++++++++++++++++++
 .../StaticAnalyzer/Checkers/MutexModeling.h   |  50 +++-
 .../Checkers/MutexModelingGDM.h               |  95 +++++++
 4 files changed, 382 insertions(+), 1 deletion(-)
 create mode 100644 clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
 create mode 100644 clang/lib/StaticAnalyzer/Checkers/MutexModelingGDM.h

diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index 682cfa01bec963..136b01b43b039e 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -69,6 +69,7 @@ add_clang_library(clangStaticAnalyzerCheckers
   MmapWriteExecChecker.cpp
   MIGChecker.cpp
   MoveChecker.cpp
+  MutexModeling.cpp
   MPI-Checker/MPIBugReporter.cpp
   MPI-Checker/MPIChecker.cpp
   MPI-Checker/MPIFunctionClassifier.cpp
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
new file mode 100644
index 00000000000000..d0224133592838
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
@@ -0,0 +1,237 @@
+//===--- MutexModeling.cpp - Modeling of mutexes --------------------------===//
+//
+// 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 modeling checker for tracking mutex states.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MutexModeling.h"
+
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
+#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
+
+#include <variant>
+
+using namespace clang;
+using namespace ento;
+using namespace mutex_modeling;
+
+namespace {
+class CallDescriptionBasedMatcher {
+  CallDescription LockFn;
+  CallDescription UnlockFn;
+
+public:
+  CallDescriptionBasedMatcher(CallDescription &&LockFn,
+                              CallDescription &&UnlockFn)
+      : LockFn(std::move(LockFn)), UnlockFn(std::move(UnlockFn)) {}
+  [[nodiscard]] bool matches(const CallEvent &Call, bool IsLock) const {
+    if (IsLock) {
+      return LockFn.matches(Call);
+    }
+    return UnlockFn.matches(Call);
+  }
+};
+
+class FirstArgMutexDescriptor : public CallDescriptionBasedMatcher {
+public:
+  FirstArgMutexDescriptor(CallDescription &&LockFn, CallDescription &&UnlockFn)
+      : CallDescriptionBasedMatcher(std::move(LockFn), std::move(UnlockFn)) {}
+
+  [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call, bool) const {
+    return Call.getArgSVal(0).getAsRegion();
+  }
+};
+
+class MemberMutexDescriptor : public CallDescriptionBasedMatcher {
+public:
+  MemberMutexDescriptor(CallDescription &&LockFn, CallDescription &&UnlockFn)
+      : CallDescriptionBasedMatcher(std::move(LockFn), std::move(UnlockFn)) {}
+
+  [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call, bool) 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.
+      const auto &ASTCtx = Call.getState()->getStateManager().getContext();
+      Guard = &ASTCtx.Idents.get(GuardName);
+    }
+  }
+
+  template <typename T> bool matchesImpl(const CallEvent &Call) const {
+    const T *C = dyn_cast<T>(&Call);
+    if (!C)
+      return false;
+    const IdentifierInfo *II =
+        cast<CXXRecordDecl>(C->getDecl()->getParent())->getIdentifier();
+    return II == Guard;
+  }
+
+public:
+  RAIIMutexDescriptor(StringRef GuardName) : GuardName(GuardName) {}
+  [[nodiscard]] bool matches(const CallEvent &Call, bool IsLock) const {
+    initIdentifierInfo(Call);
+    if (IsLock) {
+      return matchesImpl<CXXConstructorCall>(Call);
+    }
+    return matchesImpl<CXXDestructorCall>(Call);
+  }
+  [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call,
+                                           bool IsLock) const {
+    const MemRegion *LockRegion = nullptr;
+    if (IsLock) {
+      if (std::optional<SVal> Object = Call.getReturnValueUnderConstruction()) {
+        LockRegion = Object->getAsRegion();
+      }
+    } else {
+      LockRegion = cast<CXXDestructorCall>(Call).getCXXThisVal().getAsRegion();
+    }
+    return LockRegion;
+  }
+};
+
+using MutexDescriptor =
+    std::variant<FirstArgMutexDescriptor, MemberMutexDescriptor,
+                 RAIIMutexDescriptor>;
+
+const MemRegion *getRegion(const CallEvent &Call,
+                           const MutexDescriptor &Descriptor, bool IsLock) {
+  return std::visit(
+      [&Call, IsLock](auto &&Descriptor) {
+        return Descriptor.getRegion(Call, IsLock);
+      },
+      Descriptor);
+}
+
+class MutexModeling : public Checker<check::PostCall> {
+  const std::array<MutexDescriptor, 8> MutexDescriptors{
+      MemberMutexDescriptor({/*MatchAs=*/CDM::CXXMethod,
+                             /*QualifiedName=*/{"std", "mutex", "lock"},
+                             /*RequiredArgs=*/0},
+                            {CDM::CXXMethod, {"std", "mutex", "unlock"}, 0}),
+      FirstArgMutexDescriptor({CDM::CLibrary, {"pthread_mutex_lock"}, 1},
+                              {CDM::CLibrary, {"pthread_mutex_unlock"}, 1}),
+      FirstArgMutexDescriptor({CDM::CLibrary, {"mtx_lock"}, 1},
+                              {CDM::CLibrary, {"mtx_unlock"}, 1}),
+      FirstArgMutexDescriptor({CDM::CLibrary, {"pthread_mutex_trylock"}, 1},
+                              {CDM::CLibrary, {"pthread_mutex_unlock"}, 1}),
+      FirstArgMutexDescriptor({CDM::CLibrary, {"mtx_trylock"}, 1},
+                              {CDM::CLibrary, {"mtx_unlock"}, 1}),
+      FirstArgMutexDescriptor({CDM::CLibrary, {"mtx_timedlock"}, 1},
+                              {CDM::CLibrary, {"mtx_unlock"}, 1}),
+      RAIIMutexDescriptor("lock_guard"),
+      RAIIMutexDescriptor("unique_lock")};
+  void handleLock(const MutexDescriptor &Mutex, const CallEvent &Call,
+                  CheckerContext &C) const;
+
+  void handleUnlock(const MutexDescriptor &Mutex, const CallEvent &Call,
+                    CheckerContext &C) const;
+
+  [[nodiscard]] std::optional<MutexDescriptor>
+  checkDescriptorMatch(const CallEvent &Call, CheckerContext &C,
+                       bool IsLock) const;
+
+public:
+  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+};
+
+void MutexModeling::handleLock(const MutexDescriptor &LockDescriptor,
+                               const CallEvent &Call, CheckerContext &C) const {
+  const MemRegion *MutexRegion =
+      getRegion(Call, LockDescriptor, /*IsLock=*/true);
+  if (!MutexRegion)
+    return;
+
+  const CritSectionMarker MarkToAdd{Call.getOriginExpr(), MutexRegion};
+  ProgramStateRef StateWithLockEvent =
+      C.getState()->add<ActiveCritSections>(MarkToAdd);
+  C.addTransition(StateWithLockEvent, createCritSectionNote(MarkToAdd, C));
+}
+
+void MutexModeling::handleUnlock(const MutexDescriptor &UnlockDescriptor,
+                                 const CallEvent &Call,
+                                 CheckerContext &C) const {
+  const MemRegion *MutexRegion =
+      getRegion(Call, UnlockDescriptor, /*IsLock=*/false);
+  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);
+  }
+
+  State = State->set<ActiveCritSections>(NewList);
+  C.addTransition(State);
+}
+
+std::optional<MutexDescriptor>
+MutexModeling::checkDescriptorMatch(const CallEvent &Call, CheckerContext &C,
+                                    bool IsLock) const {
+  std::optional<MutexDescriptor> Descriptor;
+  const auto DescriptorIt =
+      llvm::find_if(MutexDescriptors, [&Call, IsLock](auto &&Descriptor) {
+        return std::visit(
+            [&Call, IsLock](auto &&DescriptorImpl) {
+              return DescriptorImpl.matches(Call, IsLock);
+            },
+            Descriptor);
+      });
+  if (DescriptorIt != MutexDescriptors.end())
+    Descriptor.emplace(*DescriptorIt);
+  return Descriptor;
+}
+
+void MutexModeling::checkPostCall(const CallEvent &Call,
+                                  CheckerContext &C) const {
+  if (std::optional<MutexDescriptor> LockDesc =
+          checkDescriptorMatch(Call, C, /*IsLock=*/true)) {
+    handleLock(*LockDesc, Call, C);
+  } else if (std::optional<MutexDescriptor> UnlockDesc =
+                 checkDescriptorMatch(Call, C, /*IsLock=*/false)) {
+    handleUnlock(*UnlockDesc, Call, C);
+  }
+}
+
+} // namespace
+
+namespace clang {
+namespace ento {
+// Checker registration
+void registerMutexModeling(CheckerManager &mgr) {
+  mgr.registerChecker<MutexModeling>();
+}
+bool shouldRegisterMutexModeling(const CheckerManager &) { return true; }
+} // namespace ento
+} // namespace clang
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.h
index 9d7e58e56eff6f..2653dc22d2de33 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.h
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.h
@@ -14,9 +14,57 @@
 #ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELING_H
 #define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELING_H
 
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/StringExtras.h"
+
+#include "MutexModelingGDM.h"
+
 namespace clang {
 namespace ento {
-namespace mutex_modeling {} // namespace mutex_modeling
+namespace mutex_modeling {
+inline const NoteTag *createCritSectionNote(CritSectionMarker M,
+                                            CheckerContext &C) {
+  return C.getNoteTag([M](const PathSensitiveBugReport &BR,
+                          llvm::raw_ostream &OS) {
+    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 CritSectionMarker *const 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";
+  });
+}
+
+} // namespace mutex_modeling
 } // namespace ento
 } // namespace clang
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModelingGDM.h b/clang/lib/StaticAnalyzer/Checkers/MutexModelingGDM.h
new file mode 100644
index 00000000000000..6065fa829f270d
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModelingGDM.h
@@ -0,0 +1,95 @@
+//===--- MutexModelingGDM.h - Modeling of mutexes -------------------------===//
+//----------------------------===//
+//
+// 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 the GDM definitions for tracking mutex states.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELINGGDM_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELINGGDM_H
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+
+namespace clang {
+
+class Expr;
+
+namespace ento {
+
+class MemRegion;
+
+namespace mutex_modeling {
+struct CritSectionMarker {
+  const clang::Expr *LockExpr{};
+  const clang::ento::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);
+  }
+};
+
+// GDM-related handle-types for tracking mutex states.
+class ActiveCritSections {};
+using ActiveCritSectionsTy = llvm ::ImmutableList<CritSectionMarker>;
+
+} // namespace mutex_modeling
+} // namespace ento
+} // namespace clang
+
+// shorthand for the type of the GDM handle.
+namespace {
+using MutexModelingCritSectionMarker =
+    clang::ento::mutex_modeling::CritSectionMarker;
+} // namespace
+
+// Iterator traits for ImmutableList data structure
+// that enable the use of STL algorithms.
+namespace std {
+// TODO: Move these to llvm::ImmutableList when overhauling immutable data
+// structures for proper iterator concept support.
+
+template <>
+struct iterator_traits<
+    typename llvm::ImmutableList<MutexModelingCritSectionMarker>::iterator> {
+  using iterator_category = std::forward_iterator_tag;
+  using value_type = MutexModelingCritSectionMarker;
+  using difference_type = std::ptrdiff_t;
+  using reference = MutexModelingCritSectionMarker &;
+  using pointer = MutexModelingCritSectionMarker *;
+};
+} // namespace std
+
+// FIXME: ProgramState macros are not used here, because the visibility of these
+// GDM entries must span multiple translation units (multiple checkers).
+namespace clang {
+namespace ento {
+template <>
+struct ProgramStateTrait<clang::ento::mutex_modeling::ActiveCritSections>
+    : public ProgramStatePartialTrait<
+          clang::ento::mutex_modeling::ActiveCritSectionsTy> {
+  static void *GDMIndex() {
+    static int Index;
+    return &Index;
+  }
+};
+} // namespace ento
+} // namespace clang
+
+#endif

>From f067b7a49f890bda394b77337da4e454ef93b1d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Thu, 6 Jun 2024 09:40:13 +0200
Subject: [PATCH 03/30] strip modeling logic out of
 BlockInCriticalSectionChecker

---
 .../BlockInCriticalSectionChecker.cpp         | 312 +-----------------
 1 file changed, 11 insertions(+), 301 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index d69a6c81a9f1c2..70e289686512d3 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -14,158 +14,24 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "MutexModeling.h"
+
 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #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 "MutexModeling.h"
-
-#include <iterator>
 #include <utility>
-#include <variant>
 
 using namespace clang;
 using namespace ento;
+using namespace mutex_modeling;
 
 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 CallDescriptionBasedMatcher {
-  CallDescription LockFn;
-  CallDescription UnlockFn;
-
-public:
-  CallDescriptionBasedMatcher(CallDescription &&LockFn,
-                              CallDescription &&UnlockFn)
-      : LockFn(std::move(LockFn)), UnlockFn(std::move(UnlockFn)) {}
-  [[nodiscard]] bool matches(const CallEvent &Call, bool IsLock) const {
-    if (IsLock) {
-      return LockFn.matches(Call);
-    }
-    return UnlockFn.matches(Call);
-  }
-};
-
-class FirstArgMutexDescriptor : public CallDescriptionBasedMatcher {
-public:
-  FirstArgMutexDescriptor(CallDescription &&LockFn, CallDescription &&UnlockFn)
-      : CallDescriptionBasedMatcher(std::move(LockFn), std::move(UnlockFn)) {}
-
-  [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call, bool) const {
-    return Call.getArgSVal(0).getAsRegion();
-  }
-};
-
-class MemberMutexDescriptor : public CallDescriptionBasedMatcher {
-public:
-  MemberMutexDescriptor(CallDescription &&LockFn, CallDescription &&UnlockFn)
-      : CallDescriptionBasedMatcher(std::move(LockFn), std::move(UnlockFn)) {}
-
-  [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call, bool) 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.
-      const auto &ASTCtx = Call.getState()->getStateManager().getContext();
-      Guard = &ASTCtx.Idents.get(GuardName);
-    }
-  }
-
-  template <typename T> bool matchesImpl(const CallEvent &Call) const {
-    const T *C = dyn_cast<T>(&Call);
-    if (!C)
-      return false;
-    const IdentifierInfo *II =
-        cast<CXXRecordDecl>(C->getDecl()->getParent())->getIdentifier();
-    return II == Guard;
-  }
-
-public:
-  RAIIMutexDescriptor(StringRef GuardName) : GuardName(GuardName) {}
-  [[nodiscard]] bool matches(const CallEvent &Call, bool IsLock) const {
-    initIdentifierInfo(Call);
-    if (IsLock) {
-      return matchesImpl<CXXConstructorCall>(Call);
-    }
-    return matchesImpl<CXXDestructorCall>(Call);
-  }
-  [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call,
-                                           bool IsLock) const {
-    const MemRegion *LockRegion = nullptr;
-    if (IsLock) {
-      if (std::optional<SVal> Object = Call.getReturnValueUnderConstruction()) {
-        LockRegion = Object->getAsRegion();
-      }
-    } else {
-      LockRegion = cast<CXXDestructorCall>(Call).getCXXThisVal().getAsRegion();
-    }
-    return LockRegion;
-  }
-};
-
-using MutexDescriptor =
-    std::variant<FirstArgMutexDescriptor, MemberMutexDescriptor,
-                 RAIIMutexDescriptor>;
-
 class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
 private:
-  const std::array<MutexDescriptor, 8> MutexDescriptors{
-      MemberMutexDescriptor({/*MatchAs=*/CDM::CXXMethod,
-                             /*QualifiedName=*/{"std", "mutex", "lock"},
-                             /*RequiredArgs=*/0},
-                            {CDM::CXXMethod, {"std", "mutex", "unlock"}, 0}),
-      FirstArgMutexDescriptor({CDM::CLibrary, {"pthread_mutex_lock"}, 1},
-                              {CDM::CLibrary, {"pthread_mutex_unlock"}, 1}),
-      FirstArgMutexDescriptor({CDM::CLibrary, {"mtx_lock"}, 1},
-                              {CDM::CLibrary, {"mtx_unlock"}, 1}),
-      FirstArgMutexDescriptor({CDM::CLibrary, {"pthread_mutex_trylock"}, 1},
-                              {CDM::CLibrary, {"pthread_mutex_unlock"}, 1}),
-      FirstArgMutexDescriptor({CDM::CLibrary, {"mtx_trylock"}, 1},
-                              {CDM::CLibrary, {"mtx_unlock"}, 1}),
-      FirstArgMutexDescriptor({CDM::CLibrary, {"mtx_timedlock"}, 1},
-                              {CDM::CLibrary, {"mtx_unlock"}, 1}),
-      RAIIMutexDescriptor("lock_guard"),
-      RAIIMutexDescriptor("unique_lock")};
-
   const CallDescriptionSet BlockingFunctions{{CDM::CLibrary, {"sleep"}},
                                              {CDM::CLibrary, {"getc"}},
                                              {CDM::CLibrary, {"fgets"}},
@@ -175,139 +41,23 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
   const BugType BlockInCritSectionBugType{
       this, "Call to blocking function in critical section", "Blocking Error"};
 
-  void reportBlockInCritSection(const CallEvent &call, CheckerContext &C) const;
-
-  [[nodiscard]] const NoteTag *createCritSectionNote(CritSectionMarker M,
-                                                     CheckerContext &C) const;
-
-  [[nodiscard]] std::optional<MutexDescriptor>
-  checkDescriptorMatch(const CallEvent &Call, CheckerContext &C,
-                       bool IsLock) const;
-
-  void handleLock(const MutexDescriptor &Mutex, 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;
 
+  void reportBlockInCritSection(const CallEvent &call, CheckerContext &C) const;
+
 public:
-  /// Process unlock.
-  /// Process lock.
-  /// Process blocking functions (sleep, getc, fgets, read, recv)
   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
 };
 
 } // end anonymous namespace
 
-REGISTER_LIST_WITH_PROGRAMSTATE(ActiveCritSections, CritSectionMarker)
-
-// 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 std::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 *;
-};
-
-std::optional<MutexDescriptor>
-BlockInCriticalSectionChecker::checkDescriptorMatch(const CallEvent &Call,
-                                                    CheckerContext &C,
-                                                    bool IsLock) const {
-  const auto Descriptor =
-      llvm::find_if(MutexDescriptors, [&Call, IsLock](auto &&Descriptor) {
-        return std::visit(
-            [&Call, IsLock](auto &&DescriptorImpl) {
-              return DescriptorImpl.matches(Call, IsLock);
-            },
-            Descriptor);
-      });
-  if (Descriptor != MutexDescriptors.end())
-    return *Descriptor;
-  return std::nullopt;
-}
-
-static const MemRegion *getRegion(const CallEvent &Call,
-                                  const MutexDescriptor &Descriptor,
-                                  bool IsLock) {
-  return std::visit(
-      [&Call, IsLock](auto &&Descriptor) {
-        return Descriptor.getRegion(Call, IsLock);
-      },
-      Descriptor);
-}
-
-void BlockInCriticalSectionChecker::handleLock(
-    const MutexDescriptor &LockDescriptor, const CallEvent &Call,
-    CheckerContext &C) const {
-  const MemRegion *MutexRegion =
-      getRegion(Call, LockDescriptor, /*IsLock=*/true);
-  if (!MutexRegion)
-    return;
-
-  const CritSectionMarker MarkToAdd{Call.getOriginExpr(), MutexRegion};
-  ProgramStateRef StateWithLockEvent =
-      C.getState()->add<ActiveCritSections>(MarkToAdd);
-  C.addTransition(StateWithLockEvent, createCritSectionNote(MarkToAdd, C));
-}
-
-void BlockInCriticalSectionChecker::handleUnlock(
-    const MutexDescriptor &UnlockDescriptor, const CallEvent &Call,
-    CheckerContext &C) const {
-  const MemRegion *MutexRegion =
-      getRegion(Call, UnlockDescriptor, /*IsLock=*/false);
-  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);
-  }
-
-  State = State->set<ActiveCritSections>(NewList);
-  C.addTransition(State);
-}
-
 bool BlockInCriticalSectionChecker::isBlockingInCritSection(
     const CallEvent &Call, CheckerContext &C) const {
   return BlockingFunctions.contains(Call) &&
          !C.getState()->get<ActiveCritSections>().isEmpty();
 }
 
-void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
-                                                  CheckerContext &C) const {
-  if (isBlockingInCritSection(Call, C)) {
-    reportBlockInCritSection(Call, C);
-  } else if (std::optional<MutexDescriptor> LockDesc =
-                 checkDescriptorMatch(Call, C, /*IsLock=*/true)) {
-    handleLock(*LockDesc, Call, C);
-  } else if (std::optional<MutexDescriptor> UnlockDesc =
-                 checkDescriptorMatch(Call, C, /*IsLock=*/false)) {
-    handleUnlock(*UnlockDesc, Call, C);
-  }
-}
-
 void BlockInCriticalSectionChecker::reportBlockInCritSection(
     const CallEvent &Call, CheckerContext &C) const {
   ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState());
@@ -325,54 +75,14 @@ void BlockInCriticalSectionChecker::reportBlockInCritSection(
   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 BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
+                                                  CheckerContext &C) const {
+  if (!isBlockingInCritSection(Call, C))
+    return;
+  reportBlockInCritSection(Call, C);
 }
 
+// Checker registration
 void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
   mgr.registerChecker<BlockInCriticalSectionChecker>();
 }

>From 7a634a6b915eeb67635794b8053c8108d50cea8f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Thu, 6 Jun 2024 22:25:44 +0200
Subject: [PATCH 04/30] add minimal interchecker api for modeling
 BlockInCriticalSection usecases and add checker registration capability

---
 .../BlockInCriticalSectionChecker.cpp         |  3 +-
 .../StaticAnalyzer/Checkers/MutexModeling.cpp |  2 +-
 .../StaticAnalyzer/Checkers/MutexModeling.h   | 29 ++++++++++++++++---
 3 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 70e289686512d3..11433516bad065 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -54,8 +54,7 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
 
 bool BlockInCriticalSectionChecker::isBlockingInCritSection(
     const CallEvent &Call, CheckerContext &C) const {
-  return BlockingFunctions.contains(Call) &&
-         !C.getState()->get<ActiveCritSections>().isEmpty();
+  return BlockingFunctions.contains(Call) && AreAnyCritsectionsActive(C);
 }
 
 void BlockInCriticalSectionChecker::reportBlockInCritSection(
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
index d0224133592838..b745cdead68167 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
@@ -163,7 +163,7 @@ void MutexModeling::handleLock(const MutexDescriptor &LockDescriptor,
   const CritSectionMarker MarkToAdd{Call.getOriginExpr(), MutexRegion};
   ProgramStateRef StateWithLockEvent =
       C.getState()->add<ActiveCritSections>(MarkToAdd);
-  C.addTransition(StateWithLockEvent, createCritSectionNote(MarkToAdd, C));
+  C.addTransition(StateWithLockEvent, CreateMutexCritSectionNote(MarkToAdd, C));
 }
 
 void MutexModeling::handleUnlock(const MutexDescriptor &UnlockDescriptor,
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.h
index 2653dc22d2de33..690e8473323f4f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.h
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.h
@@ -14,18 +14,39 @@
 #ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELING_H
 #define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELING_H
 
+#include "MutexModelingGDM.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringExtras.h"
 
-#include "MutexModelingGDM.h"
-
 namespace clang {
+
 namespace ento {
+class BugType;
 namespace mutex_modeling {
-inline const NoteTag *createCritSectionNote(CritSectionMarker M,
-                                            CheckerContext &C) {
+
+inline llvm::SmallSet<const BugType *, 8> RegisteredCheckers{};
+
+inline void RegisterCheckerForMutexModeling(const BugType *BT) {
+  RegisteredCheckers.insert(BT);
+}
+
+inline bool IsCheckerRegisteredForMutexModeling(const BugType *BT) {
+  return RegisteredCheckers.contains(BT);
+}
+
+inline bool AreAnyCritsectionsActive(CheckerContext &C) {
+  return !C.getState()->get<ActiveCritSections>().isEmpty();
+}
+
+inline const NoteTag *CreateMutexCritSectionNote(CritSectionMarker M,
+                                                 CheckerContext &C) {
   return C.getNoteTag([M](const PathSensitiveBugReport &BR,
                           llvm::raw_ostream &OS) {
+    if (!IsCheckerRegisteredForMutexModeling(&BR.getBugType()))
+      return;
     const auto CritSectionBegins =
         BR.getErrorNode()->getState()->get<ActiveCritSections>();
     llvm::SmallVector<CritSectionMarker, 4> LocksForMutex;

>From bda97ee2d29c2616ab32503d64628d541f6d38e7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Thu, 6 Jun 2024 22:26:21 +0200
Subject: [PATCH 05/30] register BlockInCriticalSectionCheckers bugtype for
 mutex related notes

---
 .../Checkers/BlockInCriticalSectionChecker.cpp              | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 11433516bad065..5a15c0321ddc11 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -22,7 +22,6 @@
 #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/ProgramState_Fwd.h"
 #include <utility>
 
 using namespace clang;
@@ -47,11 +46,16 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
   void reportBlockInCritSection(const CallEvent &call, CheckerContext &C) const;
 
 public:
+  BlockInCriticalSectionChecker();
   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
 };
 
 } // end anonymous namespace
 
+BlockInCriticalSectionChecker::BlockInCriticalSectionChecker() {
+  RegisterCheckerForMutexModeling(&BlockInCritSectionBugType);
+}
+
 bool BlockInCriticalSectionChecker::isBlockingInCritSection(
     const CallEvent &Call, CheckerContext &C) const {
   return BlockingFunctions.contains(Call) && AreAnyCritsectionsActive(C);

>From cbdf9a5ef4b2a4f8e3cea8ef680417b16ab4e3d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Tue, 9 Jul 2024 03:37:33 +0200
Subject: [PATCH 06/30] move mutex modeling headers into a subdirectory

---
 .../{ => MutexModeling}/MutexModeling.h       |  0
 .../{ => MutexModeling}/MutexModelingGDM.h    | 54 ++++++++++++++++---
 2 files changed, 47 insertions(+), 7 deletions(-)
 rename clang/lib/StaticAnalyzer/Checkers/{ => MutexModeling}/MutexModeling.h (100%)
 rename clang/lib/StaticAnalyzer/Checkers/{ => MutexModeling}/MutexModelingGDM.h (66%)

diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModeling.h
similarity index 100%
rename from clang/lib/StaticAnalyzer/Checkers/MutexModeling.h
rename to clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModeling.h
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModelingGDM.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingGDM.h
similarity index 66%
rename from clang/lib/StaticAnalyzer/Checkers/MutexModelingGDM.h
rename to clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingGDM.h
index 6065fa829f270d..0e20ecc5941ca6 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModelingGDM.h
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingGDM.h
@@ -26,28 +26,68 @@ namespace ento {
 class MemRegion;
 
 namespace mutex_modeling {
-struct CritSectionMarker {
-  const clang::Expr *LockExpr{};
-  const clang::ento::MemRegion *LockReg{};
+
+enum class EventKind { Init, Acquire, TryAcquire, Release, Destroy };
+
+enum class SyntaxKind { FirstArg, Member, RAII };
+
+enum class LockingSemanticsKind { PthreadSemantics, XNUSemantics };
+
+enum class LockStateKind {
+  Unlocked,
+  Locked,
+  Destroyed,
+  UntouchedAndPossiblyDestroyed,
+  UnlockedAndPossiblyDestroyed
+};
+
+struct EventDescriptor {
+  EventKind Event;
+  SyntaxKind Syntax;
+  LockingSemanticsKind Semantics;
+
+  [[nodiscard]] constexpr bool
+  operator==(const EventDescriptor &Other) const noexcept {
+    return Event == Other.Event && Syntax == Other.Syntax &&
+           Semantics == Other.Semantics;
+  }
 
   void Profile(llvm::FoldingSetNodeID &ID) const {
+    ID.Add(Event);
+    ID.Add(Syntax);
+    ID.Add(Semantics);
+  }
+};
+
+struct EventMarker {
+  EventDescriptor Descriptor;
+  LockStateKind LockState;
+  clang::Expr *LockExpr{};
+  clang::ento::MemRegion *LockReg{};
+
+  void Profile(llvm::FoldingSetNodeID &ID) const {
+    ID.Add(Descriptor);
+    ID.Add(LockState);
     ID.Add(LockExpr);
     ID.Add(LockReg);
   }
 
   [[nodiscard]] constexpr bool
-  operator==(const CritSectionMarker &Other) const noexcept {
-    return LockExpr == Other.LockExpr && LockReg == Other.LockReg;
+  operator==(const EventMarker &Other) const noexcept {
+    return Descriptor == Other.Descriptor && LockState == Other.LockState &&
+           LockExpr == Other.LockExpr && LockReg == Other.LockReg;
   }
   [[nodiscard]] constexpr bool
-  operator!=(const CritSectionMarker &Other) const noexcept {
+  operator!=(const EventMarker &Other) const noexcept {
     return !(*this == Other);
   }
 };
 
+struct CritSectionMarker {};
+
 // GDM-related handle-types for tracking mutex states.
 class ActiveCritSections {};
-using ActiveCritSectionsTy = llvm ::ImmutableList<CritSectionMarker>;
+using ActiveCritSectionsTy = llvm ::ImmutableList<EventMarker>;
 
 } // namespace mutex_modeling
 } // namespace ento

>From 50e8afc16edd4253141cac95801af26554057f96 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Tue, 9 Jul 2024 05:03:34 +0200
Subject: [PATCH 07/30] reorganize headers, add basic modeling types

---
 .../BlockInCriticalSectionChecker.cpp         |   2 +-
 .../StaticAnalyzer/Checkers/MutexModeling.cpp |  22 +--
 .../{MutexModeling.h => MutexModelingAPI.h}   |  15 +-
 .../MutexModeling/MutexModelingDomain.h       |  91 ++++++++++++
 .../Checkers/MutexModeling/MutexModelingGDM.h | 136 ++++++++----------
 5 files changed, 168 insertions(+), 98 deletions(-)
 rename clang/lib/StaticAnalyzer/Checkers/MutexModeling/{MutexModeling.h => MutexModelingAPI.h} (85%)
 create mode 100644 clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h

diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 5a15c0321ddc11..91c3d154c3bbc2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -14,7 +14,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "MutexModeling.h"
+#include "MutexModeling/MutexModelingAPI.h"
 
 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
index b745cdead68167..e4f99724836882 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
@@ -10,10 +10,12 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "MutexModeling.h"
+#include "MutexModeling/MutexModelingAPI.h"
+#include "MutexModeling/MutexModelingDomain.h"
 
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
 #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
 
 #include <variant>
@@ -96,15 +98,15 @@ class RAIIMutexDescriptor {
   }
   [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call,
                                            bool IsLock) const {
-    const MemRegion *LockRegion = nullptr;
+    const MemRegion *MutexRegion = nullptr;
     if (IsLock) {
       if (std::optional<SVal> Object = Call.getReturnValueUnderConstruction()) {
-        LockRegion = Object->getAsRegion();
+        MutexRegion = Object->getAsRegion();
       }
     } else {
-      LockRegion = cast<CXXDestructorCall>(Call).getCXXThisVal().getAsRegion();
+      MutexRegion = cast<CXXDestructorCall>(Call).getCXXThisVal().getAsRegion();
     }
-    return LockRegion;
+    return MutexRegion;
   }
 };
 
@@ -162,7 +164,7 @@ void MutexModeling::handleLock(const MutexDescriptor &LockDescriptor,
 
   const CritSectionMarker MarkToAdd{Call.getOriginExpr(), MutexRegion};
   ProgramStateRef StateWithLockEvent =
-      C.getState()->add<ActiveCritSections>(MarkToAdd);
+      C.getState()->add<CritSections>(MarkToAdd);
   C.addTransition(StateWithLockEvent, CreateMutexCritSectionNote(MarkToAdd, C));
 }
 
@@ -175,16 +177,16 @@ void MutexModeling::handleUnlock(const MutexDescriptor &UnlockDescriptor,
     return;
 
   ProgramStateRef State = C.getState();
-  const auto ActiveSections = State->get<ActiveCritSections>();
+  const auto ActiveSections = State->get<CritSections>();
   const auto MostRecentLock =
       llvm::find_if(ActiveSections, [MutexRegion](auto &&Marker) {
-        return Marker.LockReg == MutexRegion;
+        return Marker.MutexRegion == MutexRegion;
       });
   if (MostRecentLock == ActiveSections.end())
     return;
 
   // Build a new ImmutableList without this element.
-  auto &Factory = State->get_context<ActiveCritSections>();
+  auto &Factory = State->get_context<CritSections>();
   llvm::ImmutableList<CritSectionMarker> NewList = Factory.getEmptyList();
   for (auto It = ActiveSections.begin(), End = ActiveSections.end(); It != End;
        ++It) {
@@ -192,7 +194,7 @@ void MutexModeling::handleUnlock(const MutexDescriptor &UnlockDescriptor,
       NewList = Factory.add(*It, NewList);
   }
 
-  State = State->set<ActiveCritSections>(NewList);
+  State = State->set<CritSections>(NewList);
   C.addTransition(State);
 }
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModeling.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h
similarity index 85%
rename from clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModeling.h
rename to clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h
index 690e8473323f4f..bdea2aba6470b2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModeling.h
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h
@@ -1,4 +1,4 @@
-//===--- MutexModeling.h - Modeling of mutexes ----------------------------===//
+//===--- MutexModelingAPI.h - API for modeling mutexes --------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -11,9 +11,10 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELING_H
-#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELING_H
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELINGAPI_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELINGAPI_H
 
+#include "MutexModelingDomain.h"
 #include "MutexModelingGDM.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -38,7 +39,7 @@ inline bool IsCheckerRegisteredForMutexModeling(const BugType *BT) {
 }
 
 inline bool AreAnyCritsectionsActive(CheckerContext &C) {
-  return !C.getState()->get<ActiveCritSections>().isEmpty();
+  return !C.getState()->get<CritSections>().isEmpty();
 }
 
 inline const NoteTag *CreateMutexCritSectionNote(CritSectionMarker M,
@@ -48,11 +49,11 @@ inline const NoteTag *CreateMutexCritSectionNote(CritSectionMarker M,
     if (!IsCheckerRegisteredForMutexModeling(&BR.getBugType()))
       return;
     const auto CritSectionBegins =
-        BR.getErrorNode()->getState()->get<ActiveCritSections>();
+        BR.getErrorNode()->getState()->get<CritSections>();
     llvm::SmallVector<CritSectionMarker, 4> LocksForMutex;
     llvm::copy_if(
         CritSectionBegins, std::back_inserter(LocksForMutex),
-        [M](const auto &Marker) { return Marker.LockReg == M.LockReg; });
+        [M](const auto &Marker) { return Marker.MutexRegion == M.MutexRegion; });
     if (LocksForMutex.empty())
       return;
 
@@ -64,7 +65,7 @@ inline const NoteTag *CreateMutexCritSectionNote(CritSectionMarker M,
     // given mutex (in acquisition order).
     const CritSectionMarker *const Position =
         llvm::find_if(std::as_const(LocksForMutex), [M](const auto &Marker) {
-          return Marker.LockExpr == M.LockExpr;
+          return Marker.BeginExpr == M.BeginExpr;
         });
     if (Position == LocksForMutex.end())
       return;
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h
new file mode 100644
index 00000000000000..609d4ec974a2c7
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h
@@ -0,0 +1,91 @@
+//===--- MutexModelingDomain.h - Common vocabulary for modeling mutexes ---===//
+//
+// 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 common types and related functions used in the mutex modeling domain.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELINGDOMAIN_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELINGDOMAIN_H
+
+namespace clang {
+
+class Expr;
+
+namespace ento {
+
+class MemRegion;
+
+namespace mutex_modeling {
+
+enum class EventKind { Init, Acquire, TryAcquire, Release, Destroy };
+
+enum class SyntaxKind { FirstArg, Member, RAII };
+
+enum class LockingSemanticsKind { PthreadSemantics, XNUSemantics };
+
+enum class LockStateKind {
+  Unlocked,
+  Locked,
+  Destroyed,
+  UntouchedAndPossiblyDestroyed,
+  UnlockedAndPossiblyDestroyed
+};
+
+struct EventDescriptor {
+  EventKind Kind{};
+  SyntaxKind Syntax{};
+  LockingSemanticsKind Semantics{};
+
+  [[nodiscard]] constexpr bool
+  operator==(const EventDescriptor &Other) const noexcept {
+    return Kind == Other.Kind && Syntax == Other.Syntax &&
+           Semantics == Other.Semantics;
+  }
+  [[nodiscard]] constexpr bool
+  operator!=(const EventDescriptor &Other) const noexcept {
+    return !(*this == Other);
+  }
+};
+
+struct EventMarker {
+  EventDescriptor Event{};
+  LockStateKind LockState{};
+  const clang::Expr *EventExpr{};
+  const clang::ento::MemRegion *MutexRegion{};
+
+  [[nodiscard]] constexpr bool
+  operator==(const EventMarker &Other) const noexcept {
+    return Event == Other.Event && LockState == Other.LockState &&
+           EventExpr == Other.EventExpr && MutexRegion == Other.MutexRegion;
+  }
+  [[nodiscard]] constexpr bool
+  operator!=(const EventMarker &Other) const noexcept {
+    return !(*this == Other);
+  }
+};
+
+struct CritSectionMarker {
+  const clang::Expr *BeginExpr;
+  const clang::ento::MemRegion *MutexRegion;
+
+  [[nodiscard]] constexpr bool
+  operator==(const CritSectionMarker &Other) const noexcept {
+    return BeginExpr == Other.BeginExpr && MutexRegion == Other.MutexRegion;
+  }
+  [[nodiscard]] constexpr bool
+  operator!=(const CritSectionMarker &Other) const noexcept {
+    return !(*this == Other);
+  }
+};
+
+} // namespace mutex_modeling
+} // namespace ento
+} // namespace clang
+
+#endif
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingGDM.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingGDM.h
index 0e20ecc5941ca6..3cd83c3c37123d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingGDM.h
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingGDM.h
@@ -1,5 +1,4 @@
-//===--- MutexModelingGDM.h - Modeling of mutexes -------------------------===//
-//----------------------------===//
+//===--- MutexModelingGDM.h - Modeling of mutexes in GDM ------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -14,116 +13,93 @@
 #ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELINGGDM_H
 #define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELINGGDM_H
 
+#include "MutexModelingDomain.h"
+
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "llvm/ADT/FoldingSet.h"
 
+// GDM-related handle-types for tracking mutex states.
 namespace clang {
-
-class Expr;
-
 namespace ento {
-
-class MemRegion;
-
 namespace mutex_modeling {
 
-enum class EventKind { Init, Acquire, TryAcquire, Release, Destroy };
-
-enum class SyntaxKind { FirstArg, Member, RAII };
-
-enum class LockingSemanticsKind { PthreadSemantics, XNUSemantics };
-
-enum class LockStateKind {
-  Unlocked,
-  Locked,
-  Destroyed,
-  UntouchedAndPossiblyDestroyed,
-  UnlockedAndPossiblyDestroyed
-};
+class MutexEvents {};
+using MutexEventsTy = llvm::ImmutableList<EventMarker>;
 
-struct EventDescriptor {
-  EventKind Event;
-  SyntaxKind Syntax;
-  LockingSemanticsKind Semantics;
+class CritSections {};
+using CritSectionsTy = llvm::ImmutableList<CritSectionMarker>;
 
-  [[nodiscard]] constexpr bool
-  operator==(const EventDescriptor &Other) const noexcept {
-    return Event == Other.Event && Syntax == Other.Syntax &&
-           Semantics == Other.Semantics;
-  }
+} // namespace mutex_modeling
+} // namespace ento
+} // namespace clang
 
-  void Profile(llvm::FoldingSetNodeID &ID) const {
-    ID.Add(Event);
-    ID.Add(Syntax);
-    ID.Add(Semantics);
+// Enable usage of mutex modeling data structures in llvm::FoldingSet.
+namespace llvm {
+template <> struct FoldingSetTrait<clang::ento::mutex_modeling::EventMarker> {
+  static void Profile(const clang::ento::mutex_modeling::EventMarker &EM,
+               llvm::FoldingSetNodeID &ID) {
+    ID.Add(EM.Event.Kind);
+    ID.Add(EM.Event.Syntax);
+    ID.Add(EM.Event.Semantics);
+    ID.Add(EM.LockState);
+    ID.Add(EM.EventExpr);
+    ID.Add(EM.MutexRegion);
   }
 };
 
-struct EventMarker {
-  EventDescriptor Descriptor;
-  LockStateKind LockState;
-  clang::Expr *LockExpr{};
-  clang::ento::MemRegion *LockReg{};
-
-  void Profile(llvm::FoldingSetNodeID &ID) const {
-    ID.Add(Descriptor);
-    ID.Add(LockState);
-    ID.Add(LockExpr);
-    ID.Add(LockReg);
-  }
-
-  [[nodiscard]] constexpr bool
-  operator==(const EventMarker &Other) const noexcept {
-    return Descriptor == Other.Descriptor && LockState == Other.LockState &&
-           LockExpr == Other.LockExpr && LockReg == Other.LockReg;
-  }
-  [[nodiscard]] constexpr bool
-  operator!=(const EventMarker &Other) const noexcept {
-    return !(*this == Other);
+template <>
+struct FoldingSetTrait<clang::ento::mutex_modeling::CritSectionMarker> {
+  static void Profile(const clang::ento::mutex_modeling::CritSectionMarker &CSM,
+               llvm::FoldingSetNodeID &ID) {
+    ID.Add(CSM.BeginExpr);
+    ID.Add(CSM.MutexRegion);
   }
 };
-
-struct CritSectionMarker {};
-
-// GDM-related handle-types for tracking mutex states.
-class ActiveCritSections {};
-using ActiveCritSectionsTy = llvm ::ImmutableList<EventMarker>;
-
-} // namespace mutex_modeling
-} // namespace ento
-} // namespace clang
-
-// shorthand for the type of the GDM handle.
-namespace {
-using MutexModelingCritSectionMarker =
-    clang::ento::mutex_modeling::CritSectionMarker;
-} // namespace
+} // namespace llvm
 
 // Iterator traits for ImmutableList data structure
 // that enable the use of STL algorithms.
 namespace std {
 // TODO: Move these to llvm::ImmutableList when overhauling immutable data
 // structures for proper iterator concept support.
-
 template <>
-struct iterator_traits<
-    typename llvm::ImmutableList<MutexModelingCritSectionMarker>::iterator> {
+struct iterator_traits<typename llvm::ImmutableList<
+    clang::ento::mutex_modeling::EventMarker>::iterator> {
   using iterator_category = std::forward_iterator_tag;
-  using value_type = MutexModelingCritSectionMarker;
+  using value_type = clang::ento::mutex_modeling::EventMarker;
   using difference_type = std::ptrdiff_t;
-  using reference = MutexModelingCritSectionMarker &;
-  using pointer = MutexModelingCritSectionMarker *;
+  using reference = clang::ento::mutex_modeling::EventMarker &;
+  using pointer = clang::ento::mutex_modeling::EventMarker *;
+};
+template <>
+struct iterator_traits<typename llvm::ImmutableList<
+    clang::ento::mutex_modeling::CritSectionMarker>::iterator> {
+  using iterator_category = std::forward_iterator_tag;
+  using value_type = clang::ento::mutex_modeling::CritSectionMarker;
+  using difference_type = std::ptrdiff_t;
+  using reference = clang::ento::mutex_modeling::CritSectionMarker &;
+  using pointer = clang::ento::mutex_modeling::CritSectionMarker *;
 };
 } // namespace std
 
-// FIXME: ProgramState macros are not used here, because the visibility of these
+// NOTE: ProgramState macros are not used here, because the visibility of these
 // GDM entries must span multiple translation units (multiple checkers).
 namespace clang {
 namespace ento {
 template <>
-struct ProgramStateTrait<clang::ento::mutex_modeling::ActiveCritSections>
+struct ProgramStateTrait<clang::ento::mutex_modeling::MutexEvents>
+    : public ProgramStatePartialTrait<
+          clang::ento::mutex_modeling::MutexEventsTy> {
+  static void *GDMIndex() {
+    static int Index;
+    return &Index;
+  }
+};
+template <>
+struct ProgramStateTrait<clang::ento::mutex_modeling::CritSections>
     : public ProgramStatePartialTrait<
-          clang::ento::mutex_modeling::ActiveCritSectionsTy> {
+          clang::ento::mutex_modeling::CritSectionsTy> {
   static void *GDMIndex() {
     static int Index;
     return &Index;

>From 2663535ffab96aa7cf86bd7dddde06739100755f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Wed, 10 Jul 2024 11:51:45 +0200
Subject: [PATCH 08/30] implement unified modeling

[WIP] stab 2 at unified modeling, just init events

fix brace error
---
 .../StaticAnalyzer/Checkers/MutexModeling.cpp | 316 +++++++++---------
 .../Checkers/MutexModeling/MutexModelingAPI.h |   7 +-
 .../MutexModeling/MutexModelingDefs.h         | 212 ++++++++++++
 .../MutexModeling/MutexModelingDomain.h       |  54 +--
 .../Checkers/MutexModeling/MutexModelingGDM.h |  65 +++-
 .../MutexModeling/MutexRegionExtractor.h      | 131 ++++++++
 6 files changed, 601 insertions(+), 184 deletions(-)
 create mode 100644 clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDefs.h
 create mode 100644 clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexRegionExtractor.h

diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
index e4f99724836882..badb6ed64fd312 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
@@ -11,154 +11,77 @@
 //===----------------------------------------------------------------------===//
 
 #include "MutexModeling/MutexModelingAPI.h"
+#include "MutexModeling/MutexModelingDefs.h"
 #include "MutexModeling/MutexModelingDomain.h"
+#include "MutexModeling/MutexRegionExtractor.h"
 
 #include "clang/StaticAnalyzer/Core/Checker.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
 #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
-
-#include <variant>
+#include <memory>
 
 using namespace clang;
 using namespace ento;
 using namespace mutex_modeling;
 
 namespace {
-class CallDescriptionBasedMatcher {
-  CallDescription LockFn;
-  CallDescription UnlockFn;
 
-public:
-  CallDescriptionBasedMatcher(CallDescription &&LockFn,
-                              CallDescription &&UnlockFn)
-      : LockFn(std::move(LockFn)), UnlockFn(std::move(UnlockFn)) {}
-  [[nodiscard]] bool matches(const CallEvent &Call, bool IsLock) const {
-    if (IsLock) {
-      return LockFn.matches(Call);
+// When a lock is destroyed, in some semantics(like PthreadSemantics) we are not
+// sure if the destroy call has succeeded or failed, and the lock enters one of
+// the 'possibly destroyed' state. There is a short time frame for the
+// programmer to check the return value to see if the lock was successfully
+// destroyed. Before we model the next operation over that lock, we call this
+// function to see if the return value was checked by now and set the lock state
+// - either to destroyed state or back to its previous state.
+
+// In PthreadSemantics, pthread_mutex_destroy() returns zero if the lock is
+// successfully destroyed and it returns a non-zero value otherwise.
+ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef State,
+                                              const MemRegion *LockReg,
+                                              const SymbolRef *Sym) {
+  const LockStateKind *LState = State->get<LockStates>(LockReg);
+  // Existence in DestroyRetVal ensures existence in LockMap.
+  // Existence in Destroyed also ensures that the lock state for lockR is either
+  // UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed.
+  assert(LState);
+  assert(*LState == LockStateKind::UntouchedAndPossiblyDestroyed ||
+         *LState == LockStateKind::UnlockedAndPossiblyDestroyed);
+
+  ConstraintManager &CMgr = State->getConstraintManager();
+  ConditionTruthVal RetZero = CMgr.isNull(State, *Sym);
+  if (RetZero.isConstrainedFalse()) {
+    switch (*LState) {
+    case LockStateKind::UntouchedAndPossiblyDestroyed: {
+      State = State->remove<LockStates>(LockReg);
+      break;
     }
-    return UnlockFn.matches(Call);
-  }
-};
-
-class FirstArgMutexDescriptor : public CallDescriptionBasedMatcher {
-public:
-  FirstArgMutexDescriptor(CallDescription &&LockFn, CallDescription &&UnlockFn)
-      : CallDescriptionBasedMatcher(std::move(LockFn), std::move(UnlockFn)) {}
-
-  [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call, bool) const {
-    return Call.getArgSVal(0).getAsRegion();
-  }
-};
-
-class MemberMutexDescriptor : public CallDescriptionBasedMatcher {
-public:
-  MemberMutexDescriptor(CallDescription &&LockFn, CallDescription &&UnlockFn)
-      : CallDescriptionBasedMatcher(std::move(LockFn), std::move(UnlockFn)) {}
-
-  [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call, bool) 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.
-      const auto &ASTCtx = Call.getState()->getStateManager().getContext();
-      Guard = &ASTCtx.Idents.get(GuardName);
+    case LockStateKind::UnlockedAndPossiblyDestroyed: {
+      State = State->set<LockStates>(LockReg, LockStateKind::Unlocked);
+      break;
     }
-  }
-
-  template <typename T> bool matchesImpl(const CallEvent &Call) const {
-    const T *C = dyn_cast<T>(&Call);
-    if (!C)
-      return false;
-    const IdentifierInfo *II =
-        cast<CXXRecordDecl>(C->getDecl()->getParent())->getIdentifier();
-    return II == Guard;
-  }
-
-public:
-  RAIIMutexDescriptor(StringRef GuardName) : GuardName(GuardName) {}
-  [[nodiscard]] bool matches(const CallEvent &Call, bool IsLock) const {
-    initIdentifierInfo(Call);
-    if (IsLock) {
-      return matchesImpl<CXXConstructorCall>(Call);
+    default: {
+      State = State->set<LockStates>(LockReg, LockStateKind::Destroyed);
     }
-    return matchesImpl<CXXDestructorCall>(Call);
-  }
-  [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call,
-                                           bool IsLock) const {
-    const MemRegion *MutexRegion = nullptr;
-    if (IsLock) {
-      if (std::optional<SVal> Object = Call.getReturnValueUnderConstruction()) {
-        MutexRegion = Object->getAsRegion();
-      }
-    } else {
-      MutexRegion = cast<CXXDestructorCall>(Call).getCXXThisVal().getAsRegion();
     }
-    return MutexRegion;
   }
-};
 
-using MutexDescriptor =
-    std::variant<FirstArgMutexDescriptor, MemberMutexDescriptor,
-                 RAIIMutexDescriptor>;
-
-const MemRegion *getRegion(const CallEvent &Call,
-                           const MutexDescriptor &Descriptor, bool IsLock) {
-  return std::visit(
-      [&Call, IsLock](auto &&Descriptor) {
-        return Descriptor.getRegion(Call, IsLock);
-      },
-      Descriptor);
+  // Removing the map entry (LockReg, sym) from DestroyRetVal as the lock
+  // state is now resolved.
+  return State->remove<DestroyedRetVals>(LockReg);
 }
 
-class MutexModeling : public Checker<check::PostCall> {
-  const std::array<MutexDescriptor, 8> MutexDescriptors{
-      MemberMutexDescriptor({/*MatchAs=*/CDM::CXXMethod,
-                             /*QualifiedName=*/{"std", "mutex", "lock"},
-                             /*RequiredArgs=*/0},
-                            {CDM::CXXMethod, {"std", "mutex", "unlock"}, 0}),
-      FirstArgMutexDescriptor({CDM::CLibrary, {"pthread_mutex_lock"}, 1},
-                              {CDM::CLibrary, {"pthread_mutex_unlock"}, 1}),
-      FirstArgMutexDescriptor({CDM::CLibrary, {"mtx_lock"}, 1},
-                              {CDM::CLibrary, {"mtx_unlock"}, 1}),
-      FirstArgMutexDescriptor({CDM::CLibrary, {"pthread_mutex_trylock"}, 1},
-                              {CDM::CLibrary, {"pthread_mutex_unlock"}, 1}),
-      FirstArgMutexDescriptor({CDM::CLibrary, {"mtx_trylock"}, 1},
-                              {CDM::CLibrary, {"mtx_unlock"}, 1}),
-      FirstArgMutexDescriptor({CDM::CLibrary, {"mtx_timedlock"}, 1},
-                              {CDM::CLibrary, {"mtx_unlock"}, 1}),
-      RAIIMutexDescriptor("lock_guard"),
-      RAIIMutexDescriptor("unique_lock")};
-  void handleLock(const MutexDescriptor &Mutex, const CallEvent &Call,
-                  CheckerContext &C) const;
-
-  void handleUnlock(const MutexDescriptor &Mutex, const CallEvent &Call,
-                    CheckerContext &C) const;
-
-  [[nodiscard]] std::optional<MutexDescriptor>
-  checkDescriptorMatch(const CallEvent &Call, CheckerContext &C,
-                       bool IsLock) const;
+ProgramStateRef doResolvePossiblyDestroyedMutex(ProgramStateRef State,
+                                                const MemRegion *MTX) {
+  assert(MTX && "should only be called with a mutex region");
 
-public:
-  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
-};
+  if (const SymbolRef *Sym = State->get<DestroyedRetVals>(MTX))
+    return resolvePossiblyDestroyedMutex(State, MTX, Sym);
+  return State;
+}
 
-void MutexModeling::handleLock(const MutexDescriptor &LockDescriptor,
-                               const CallEvent &Call, CheckerContext &C) const {
-  const MemRegion *MutexRegion =
-      getRegion(Call, LockDescriptor, /*IsLock=*/true);
+void updateCritSectionOnLock(const MutexRegionExtractor &LockDescriptor,
+                             const CallEvent &Call, CheckerContext &C) {
+  const MemRegion *MutexRegion = getRegion(LockDescriptor, Call);
   if (!MutexRegion)
     return;
 
@@ -168,11 +91,9 @@ void MutexModeling::handleLock(const MutexDescriptor &LockDescriptor,
   C.addTransition(StateWithLockEvent, CreateMutexCritSectionNote(MarkToAdd, C));
 }
 
-void MutexModeling::handleUnlock(const MutexDescriptor &UnlockDescriptor,
-                                 const CallEvent &Call,
-                                 CheckerContext &C) const {
-  const MemRegion *MutexRegion =
-      getRegion(Call, UnlockDescriptor, /*IsLock=*/false);
+void updateCriticalSectionOnUnlock(const EventDescriptor &UnlockDescriptor,
+                                   const CallEvent &Call, CheckerContext &C) {
+  const MemRegion *MutexRegion = getRegion(UnlockDescriptor.Trigger, Call);
   if (!MutexRegion)
     return;
 
@@ -194,37 +115,122 @@ void MutexModeling::handleUnlock(const MutexDescriptor &UnlockDescriptor,
       NewList = Factory.add(*It, NewList);
   }
 
-  State = State->set<CritSections>(NewList);
-  C.addTransition(State);
+  C.addTransition(State->set<CritSections>(NewList));
 }
 
-std::optional<MutexDescriptor>
-MutexModeling::checkDescriptorMatch(const CallEvent &Call, CheckerContext &C,
-                                    bool IsLock) const {
-  std::optional<MutexDescriptor> Descriptor;
-  const auto DescriptorIt =
-      llvm::find_if(MutexDescriptors, [&Call, IsLock](auto &&Descriptor) {
-        return std::visit(
-            [&Call, IsLock](auto &&DescriptorImpl) {
-              return DescriptorImpl.matches(Call, IsLock);
-            },
-            Descriptor);
-      });
-  if (DescriptorIt != MutexDescriptors.end())
-    Descriptor.emplace(*DescriptorIt);
-  return Descriptor;
+class MutexModeling : public Checker<check::PostCall> {
+  std::vector<EventDescriptor> handledEvents = getHandledEvents();
+
+public:
+  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+
+private:
+  std::unique_ptr<BugType> BT_initlock = std::make_unique<BugType>(
+      this->getCheckerName(), "Init invalid lock", "Lock checker");
+  ProgramStateRef handleInit(const EventDescriptor &Event, const MemRegion *MTX,
+                             const Expr *MTXExpr, const CallEvent &Call,
+                             ProgramStateRef State, CheckerContext &C) const;
+  ProgramStateRef handleEvent(const EventDescriptor &Event,
+                              const MemRegion *MTX, const Expr *MTXExpr,
+                              const CallEvent &Call, ProgramStateRef State,
+                              CheckerContext &C) const;
+};
+
+} // namespace
+
+ProgramStateRef
+MutexModeling::handleInit(const EventDescriptor &Event, const MemRegion *MTX,
+                          const Expr *MTXExpr, const CallEvent &Call,
+                          ProgramStateRef State, CheckerContext &C) const {
+  assert(MTX && "should only be called with a mutex region");
+  assert(MTXExpr && "should only be called with a valid mutex expression");
+
+  State = State->add<MutexEvents>(EventMarker{
+      Event.Kind, Event.Semantics, Call.getCalleeIdentifier(), MTXExpr, MTX});
+
+  const LockStateKind *LState = State->get<LockStates>(MTX);
+  if (!LState || *LState == LockStateKind::Destroyed) {
+    return State->set<LockStates>(MTX, LockStateKind::Unlocked);
+  }
+
+  // We are here if three is an init event on a lock that is modelled, and it
+  // is not in destroyed state. The bugreporting should be done in the
+  // reporting checker and not it the modeling, but we still want to be
+  // efficient.
+  StringRef Message;
+  if (*LState == LockStateKind::Locked) {
+    Message = "This lock is still being held";
+    State =
+        State->set<LockStates>(MTX, LockStateKind::Error_DoubleInitWhileLocked);
+  } else {
+    Message = "This lock has already been initialized";
+    State = State->set<LockStates>(MTX, LockStateKind::Error_DoubleInit);
+  }
+
+  // TODO: put this part in the reporting checker
+
+  ExplodedNode *N = C.generateErrorNode();
+  if (!N)
+    return State;
+  auto Report =
+      std::make_unique<PathSensitiveBugReport>(BT_initlock.get(), Message, N);
+  Report->addRange(MTXExpr->getSourceRange());
+  C.emitReport(std::move(Report));
+
+  return State;
+}
+
+ProgramStateRef
+MutexModeling::handleEvent(const EventDescriptor &Event, const MemRegion *MTX,
+                           const Expr *MTXExpr, const CallEvent &Call,
+                           ProgramStateRef State, CheckerContext &C) const {
+  assert(MTX && "should only be called with a mutex region");
+  assert(MTXExpr && "should only be called with a valid mutex expression");
+
+  switch (Event.Kind) {
+  case EventKind::Init:
+    return handleInit(Event, MTX, MTXExpr, Call, State, C);
+    break;
+  default:
+    llvm_unreachable("Unhandled event kind!");
+#if 0
+  case EventKind::Acquire:
+    handleAcquire(Event, Call, C);
+    break;
+  case EventKind::TryAcquire:
+    handleTryAcquire(Event, Call, C);
+    break;
+  case EventKind::Release:
+    handleRelease(Event, Call, C);
+    break;
+  case EventKind::Destroy:
+    handleDestroy(Event, Call, C);
+    break;
+#endif
+  }
 }
 
 void MutexModeling::checkPostCall(const CallEvent &Call,
                                   CheckerContext &C) const {
-  if (std::optional<MutexDescriptor> LockDesc =
-          checkDescriptorMatch(Call, C, /*IsLock=*/true)) {
-    handleLock(*LockDesc, Call, C);
-  } else if (std::optional<MutexDescriptor> UnlockDesc =
-                 checkDescriptorMatch(Call, C, /*IsLock=*/false)) {
-    handleUnlock(*UnlockDesc, Call, C);
+  // FIXME: Try to handle cases when the implementation was inlined rather than
+  // just giving up.
+  if (C.wasInlined)
+    return;
+
+  ProgramStateRef State = C.getState();
+  for (auto &&Event : handledEvents) {
+    if (matches(Event.Trigger, Call)) {
+      const MemRegion *MTX = getRegion(Event.Trigger, Call);
+      if (!MTX)
+        continue;
+      const Expr *MTXExpr = Call.getOriginExpr();
+      if (!MTXExpr)
+        continue;
+      State = doResolvePossiblyDestroyedMutex(State, MTX);
+      State = handleEvent(Event, MTX, MTXExpr, Call, State, C);
+      C.addTransition(State);
+    }
   }
-}
 
 } // namespace
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h
index bdea2aba6470b2..58d16045366973 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h
@@ -51,9 +51,10 @@ inline const NoteTag *CreateMutexCritSectionNote(CritSectionMarker M,
     const auto CritSectionBegins =
         BR.getErrorNode()->getState()->get<CritSections>();
     llvm::SmallVector<CritSectionMarker, 4> LocksForMutex;
-    llvm::copy_if(
-        CritSectionBegins, std::back_inserter(LocksForMutex),
-        [M](const auto &Marker) { return Marker.MutexRegion == M.MutexRegion; });
+    llvm::copy_if(CritSectionBegins, std::back_inserter(LocksForMutex),
+                  [M](const auto &Marker) {
+                    return Marker.MutexRegion == M.MutexRegion;
+                  });
     if (LocksForMutex.empty())
       return;
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDefs.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDefs.h
new file mode 100644
index 00000000000000..936fd5fc87ea34
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDefs.h
@@ -0,0 +1,212 @@
+//===--- MutexModelingDefs.h - Modeling of mutexes ------------------------===//
+//
+// 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 the default set of events that are handled by the mutex modeling.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELINGMUTEXMODELINGDEFS_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELINGMUTEXMODELINGDEFS_H
+
+#include "MutexModelingDomain.h"
+#include "MutexRegionExtractor.h"
+
+#include <vector>
+
+namespace clang::ento::mutex_modeling {
+
+inline auto getHandledEvents() -> std::vector<EventDescriptor> {
+  return std::vector<EventDescriptor>{
+                     // - Pthread
+                     EventDescriptor{FirstArgMutexExtractor{CallDescription{
+                         CDM::CLibrary, {"pthread_mutex_init"}, 2}}},
+                     EventKind::Init, SemanticsKind::NotApplicable},
+         // TODO: pthread_rwlock_init(2 arguments).
+         // TODO: lck_mtx_init(3 arguments).
+         // TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex.
+         // TODO: lck_rw_init(3 arguments).
+         // TODO: lck_rw_alloc_init(2 arguments) => returns the mutex.
+
+         // - Fuchsia
+         EventDescriptor{CallDescription{CDM::CLibrary, {"spin_lock_init"}, 1},
+                         Event::Init, Syntax::FirstArg,
+                         LockingSemantics::NotApplicable},
+
+         // - C11
+         EventDescriptor{CallDescription{CDM::CLibrary, {"mtx_init"}, 2},
+                         Event::Init, Syntax::FirstArg,
+                         LockingSemantics::NotApplicable},
+
+         // Acquire kind
+         // - Pthread
+         //
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"pthread_mutex_lock"}, 1},
+             Event::Acquire, Syntax::FirstArg,
+             LockingSemantics::PthreadSemantics},
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"pthread_rwlock_rdlock"}, 1},
+             Event::Acquire, Syntax::FirstArg,
+             LockingSemantics::PthreadSemantics},
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"pthread_rwlock_wrlock"}, 1},
+             Event::Acquire, Syntax::FirstArg,
+             LockingSemantics::PthreadSemantics},
+         EventDescriptor{CallDescription{CDM::CLibrary, {"lck_mtx_lock"}, 1},
+                         Event::Acquire, Syntax::FirstArg,
+                         LockingSemantics::XNUSemantics},
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"lck_rw_lock_exclusive"}, 1},
+             Event::Acquire, Syntax::FirstArg, LockingSemantics::XNUSemantics},
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"lck_rw_lock_shared"}, 1},
+             Event::Acquire, Syntax::FirstArg, LockingSemantics::XNUSemantics},
+
+         // - Fuchsia
+         EventDescriptor{CallDescription{CDM::CLibrary, {"spin_lock"}, 1},
+                         Event::Acquire, Syntax::FirstArg,
+                         LockingSemantics::PthreadSemantics},
+         EventDescriptor{CallDescription{CDM::CLibrary, {"spin_lock_save"}, 3},
+                         Event::Acquire, Syntax::FirstArg,
+                         LockingSemantics::PthreadSemantics},
+         EventDescriptor{CallDescription{CDM::CLibrary, {"sync_mutex_lock"}, 1},
+                         Event::Acquire, Syntax::FirstArg,
+                         LockingSemantics::PthreadSemantics},
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"sync_mutex_lock_with_waiter"}, 1},
+             Event::Acquire, Syntax::FirstArg,
+             LockingSemantics::PthreadSemantics},
+
+         // - C11
+         EventDescriptor{CallDescription{CDM::CLibrary, {"mtx_lock"}, 1},
+                         Event::Acquire, Syntax::FirstArg,
+                         LockingSemantics::PthreadSemantics},
+
+         // - std
+         EventDescriptor{
+             CallDescription{CDM::CXXMethod, {"std", "mutex", "lock"}, 0},
+             Event::Acquire, Syntax::Member,
+             LockingSemantics::PthreadSemantics},
+         EventDescriptor{
+             CallDescription{CDM::CXXMethod, {"std", "lock_guard"}, 1},
+             Event::Acquire, Syntax::RAII, LockingSemantics::PthreadSemantics},
+         EventDescriptor{
+             CallDescription{CDM::CXXMethod, {"std", "unique_lock"}, 1},
+             Event::Acquire, Syntax::RAII, LockingSemantics::PthreadSemantics},
+
+         // TryAcquire kind
+         // - Pthread
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"pthread_mutex_trylock"}, 1},
+             Event::TryAcquire, Syntax::FirstArg,
+             LockingSemantics::PthreadSemantics},
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"pthread_rwlock_tryrdlock"}, 1},
+             Event::TryAcquire, Syntax::FirstArg,
+             LockingSemantics::PthreadSemantics},
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"pthread_rwlock_trywrlock"}, 1},
+             Event::TryAcquire, Syntax::FirstArg,
+             LockingSemantics::PthreadSemantics},
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"lck_mtx_try_lock"}, 1},
+             Event::TryAcquire, Syntax::FirstArg,
+             LockingSemantics::XNUSemantics},
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"lck_rw_try_lock_exclusive"}, 1},
+             Event::TryAcquire, Syntax::FirstArg,
+             LockingSemantics::XNUSemantics},
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"lck_rw_try_lock_shared"}, 1},
+             Event::TryAcquire, Syntax::FirstArg,
+             LockingSemantics::XNUSemantics},
+
+         // - Fuchsia
+         EventDescriptor{CallDescription{CDM::CLibrary, {"spin_trylock"}, 1},
+                         Event::TryAcquire, Syntax::FirstArg,
+                         LockingSemantics::PthreadSemantics},
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"sync_mutex_trylock"}, 1},
+             Event::TryAcquire, Syntax::FirstArg,
+             LockingSemantics::PthreadSemantics},
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"sync_mutex_timedlock"}, 2},
+             Event::TryAcquire, Syntax::FirstArg,
+             LockingSemantics::PthreadSemantics},
+
+         // - C11
+         EventDescriptor{CallDescription{CDM::CLibrary, {"mtx_trylock"}, 1},
+                         Event::TryAcquire, Syntax::FirstArg,
+                         LockingSemantics::PthreadSemantics},
+         EventDescriptor{CallDescription{CDM::CLibrary, {"mtx_timedlock"}, 2},
+                         Event::TryAcquire, Syntax::FirstArg,
+                         LockingSemantics::PthreadSemantics},
+
+         // Release kind
+         // - Pthread
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"pthread_mutex_unlock"}, 1},
+             Event::Release, Syntax::FirstArg, LockingSemantics::NotApplicable},
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"pthread_rwlock_unlock"}, 1},
+             Event::Release, Syntax::FirstArg, LockingSemantics::NotApplicable},
+         EventDescriptor{CallDescription{CDM::CLibrary, {"lck_mtx_unlock"}, 1},
+                         Event::Release, Syntax::FirstArg,
+                         LockingSemantics::NotApplicable},
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"lck_rw_unlock_exclusive"}, 1},
+             Event::Release, Syntax::FirstArg, LockingSemantics::NotApplicable},
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"lck_rw_unlock_shared"}, 1},
+             Event::Release, Syntax::FirstArg, LockingSemantics::NotApplicable},
+         EventDescriptor{CallDescription{CDM::CLibrary, {"lck_rw_done"}, 1},
+                         Event::Release, Syntax::FirstArg,
+                         LockingSemantics::NotApplicable},
+
+         // - Fuchsia
+         EventDescriptor{CallDescription{CDM::CLibrary, {"spin_unlock"}, 1},
+                         Event::Release, Syntax::FirstArg,
+                         LockingSemantics::NotApplicable},
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"spin_unlock_restore"}, 3},
+             Event::Release, Syntax::FirstArg, LockingSemantics::NotApplicable},
+         EventDescriptor{
+             CallDescription{CDM::CLibrary, {"sync_mutex_unlock"}, 1},
+             Event::Release, Syntax::FirstArg, LockingSemantics::NotApplicable},
+
+         // - C11
+         EventDescriptor{CallDescription{CDM::CLibrary, {"mtx_unlock"}, 1},
+                         Event::Release, Syntax::FirstArg,
+                         LockingSemantics::NotApplicable},
+
+         // - std
+         EventDescriptor{
+             CallDescription{CDM::CXXMethod, {"std", "mutex", "unlock"}, 0},
+             Event::Release, Syntax::Member, LockingSemantics::NotApplicable},
+
+         // Destroy kind
+         // - Pthread
+         EventDescriptor{{CDM::CLibrary, {"pthread_mutex_destroy"}, 1},
+                         Event::Destroy,
+                         Syntax::FirstArg,
+                         LockingSemantics::NotApplicable},
+         EventDescriptor{CallDescription{CDM::CLibrary, {"lck_mtx_destroy"}, 2},
+                         Event::Destroy, Syntax::FirstArg,
+                         LockingSemantics::NotApplicable},
+         // TODO: pthread_rwlock_destroy(1 argument).
+         // TODO: lck_rw_destroy(2 arguments).
+
+         // - C11
+         EventDescriptor{CallDescription{CDM::CLibrary, {"mtx_destroy"}, 1},
+                         Event::Destroy, Syntax::FirstArg,
+                         LockingSemantics::NotApplicable}
+};
+
+} // namespace clang::ento::mutex_modeling
+
+#endif
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h
index 609d4ec974a2c7..a0dfb8dc73b62a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h
@@ -13,6 +13,9 @@
 #ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELINGDOMAIN_H
 #define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELINGDOMAIN_H
 
+#include "MutexRegionExtractor.h"
+#include "clang/Basic/IdentifierTable.h"
+
 namespace clang {
 
 class Expr;
@@ -27,46 +30,52 @@ enum class EventKind { Init, Acquire, TryAcquire, Release, Destroy };
 
 enum class SyntaxKind { FirstArg, Member, RAII };
 
-enum class LockingSemanticsKind { PthreadSemantics, XNUSemantics };
+enum class SemanticsKind { NotApplicable = 0, PthreadSemantics, XNUSemantics };
 
 enum class LockStateKind {
   Unlocked,
   Locked,
   Destroyed,
   UntouchedAndPossiblyDestroyed,
-  UnlockedAndPossiblyDestroyed
+  UnlockedAndPossiblyDestroyed,
+  Error_DoubleInit,
+  Error_DoubleInitWhileLocked,
 };
 
 struct EventDescriptor {
+  MutexRegionExtractor Trigger;
   EventKind Kind{};
-  SyntaxKind Syntax{};
-  LockingSemanticsKind Semantics{};
+  SemanticsKind Semantics{};
 
+  // TODO: Modernize to spaceship when C++20 is available.
   [[nodiscard]] constexpr bool
-  operator==(const EventDescriptor &Other) const noexcept {
-    return Kind == Other.Kind && Syntax == Other.Syntax &&
-           Semantics == Other.Semantics;
+  operator!=(const EventDescriptor &Other) const noexcept {
+    return !(Trigger == Other.Trigger) || Kind != Other.Kind ||
+           Semantics != Other.Semantics;
   }
   [[nodiscard]] constexpr bool
-  operator!=(const EventDescriptor &Other) const noexcept {
-    return !(*this == Other);
+  operator==(const EventDescriptor &Other) const noexcept {
+    return !(*this != Other);
   }
 };
 
 struct EventMarker {
-  EventDescriptor Event{};
-  LockStateKind LockState{};
+  EventKind Kind{};
+  SemanticsKind Semantics{};
+  const IdentifierInfo *Event;
   const clang::Expr *EventExpr{};
   const clang::ento::MemRegion *MutexRegion{};
 
+  // TODO: Modernize to spaceship when C++20 is available.
   [[nodiscard]] constexpr bool
-  operator==(const EventMarker &Other) const noexcept {
-    return Event == Other.Event && LockState == Other.LockState &&
-           EventExpr == Other.EventExpr && MutexRegion == Other.MutexRegion;
+  operator!=(const EventMarker &Other) const noexcept {
+    return Event != Other.Event || Kind != Other.Kind ||
+           Semantics != Other.Semantics || LockState != Other.LockState ||
+           EventExpr != Other.EventExpr || MutexRegion != Other.MutexRegion;
   }
   [[nodiscard]] constexpr bool
-  operator!=(const EventMarker &Other) const noexcept {
-    return !(*this == Other);
+  operator==(const EventMarker &Other) const noexcept {
+    return !(*this != Other);
   }
 };
 
@@ -74,13 +83,18 @@ struct CritSectionMarker {
   const clang::Expr *BeginExpr;
   const clang::ento::MemRegion *MutexRegion;
 
+  explicit CritSectionMarker(const clang::Expr *BeginExpr,
+                             const clang::ento::MemRegion *MutexRegion)
+      : BeginExpr(BeginExpr), MutexRegion(MutexRegion) {}
+
+  // TODO: Modernize to spaceship when C++20 is available.
   [[nodiscard]] constexpr bool
-  operator==(const CritSectionMarker &Other) const noexcept {
-    return BeginExpr == Other.BeginExpr && MutexRegion == Other.MutexRegion;
+  operator!=(const CritSectionMarker &Other) const noexcept {
+    return BeginExpr != Other.BeginExpr || MutexRegion != Other.MutexRegion;
   }
   [[nodiscard]] constexpr bool
-  operator!=(const CritSectionMarker &Other) const noexcept {
-    return !(*this == Other);
+  operator==(const CritSectionMarker &Other) const noexcept {
+    return !(*this != Other);
   }
 };
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingGDM.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingGDM.h
index 3cd83c3c37123d..566c8d7fdc730c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingGDM.h
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingGDM.h
@@ -24,12 +24,21 @@ namespace clang {
 namespace ento {
 namespace mutex_modeling {
 
+// Raw data of the mutex events.
 class MutexEvents {};
 using MutexEventsTy = llvm::ImmutableList<EventMarker>;
 
+// Aggregated data structures for tracking critical sections.
 class CritSections {};
 using CritSectionsTy = llvm::ImmutableList<CritSectionMarker>;
 
+// Aggregated data structures for tracking mutex states.
+class LockStates {};
+using LockStatesTy = llvm::ImmutableMap<const MemRegion *, LockStateKind>;
+
+class DestroyedRetVals {};
+using DestroyedRetValsTy = llvm::ImmutableMap<const MemRegion *, SymbolRef>;
+
 } // namespace mutex_modeling
 } // namespace ento
 } // namespace clang
@@ -38,10 +47,10 @@ using CritSectionsTy = llvm::ImmutableList<CritSectionMarker>;
 namespace llvm {
 template <> struct FoldingSetTrait<clang::ento::mutex_modeling::EventMarker> {
   static void Profile(const clang::ento::mutex_modeling::EventMarker &EM,
-               llvm::FoldingSetNodeID &ID) {
-    ID.Add(EM.Event.Kind);
-    ID.Add(EM.Event.Syntax);
-    ID.Add(EM.Event.Semantics);
+                      llvm::FoldingSetNodeID &ID) {
+    ID.Add(EM.Event);
+    ID.Add(EM.Kind);
+    ID.Add(EM.Semantics);
     ID.Add(EM.LockState);
     ID.Add(EM.EventExpr);
     ID.Add(EM.MutexRegion);
@@ -51,14 +60,14 @@ template <> struct FoldingSetTrait<clang::ento::mutex_modeling::EventMarker> {
 template <>
 struct FoldingSetTrait<clang::ento::mutex_modeling::CritSectionMarker> {
   static void Profile(const clang::ento::mutex_modeling::CritSectionMarker &CSM,
-               llvm::FoldingSetNodeID &ID) {
+                      llvm::FoldingSetNodeID &ID) {
     ID.Add(CSM.BeginExpr);
     ID.Add(CSM.MutexRegion);
   }
 };
 } // namespace llvm
 
-// Iterator traits for ImmutableList data structure
+// Iterator traits for ImmutableList and ImmutableMap data structures
 // that enable the use of STL algorithms.
 namespace std {
 // TODO: Move these to llvm::ImmutableList when overhauling immutable data
@@ -81,10 +90,36 @@ struct iterator_traits<typename llvm::ImmutableList<
   using reference = clang::ento::mutex_modeling::CritSectionMarker &;
   using pointer = clang::ento::mutex_modeling::CritSectionMarker *;
 };
+template <>
+struct iterator_traits<typename llvm::ImmutableMap<
+    const clang::ento::MemRegion *,
+    clang::ento::mutex_modeling::LockStateKind>::iterator> {
+  using iterator_category = std::forward_iterator_tag;
+  using value_type = std::pair<const clang::ento::MemRegion *,
+                               clang::ento::mutex_modeling::LockStateKind>;
+  using difference_type = std::ptrdiff_t;
+  using reference = std::pair<const clang::ento::MemRegion *,
+                              clang::ento::mutex_modeling::LockStateKind> &;
+  using pointer = std::pair<const clang::ento::MemRegion *,
+                            clang::ento::mutex_modeling::LockStateKind> *;
+};
+template <>
+struct iterator_traits<typename llvm::ImmutableMap<
+    const clang::ento::MemRegion *, clang::ento::SymbolRef>::iterator> {
+  using iterator_category = std::forward_iterator_tag;
+  using value_type =
+      std::pair<const clang::ento::MemRegion *, clang::ento::SymbolRef>;
+  using difference_type = std::ptrdiff_t;
+  using reference =
+      std::pair<const clang::ento::MemRegion *, clang::ento::SymbolRef> &;
+  using pointer =
+      std::pair<const clang::ento::MemRegion *, clang::ento::SymbolRef> *;
+};
 } // namespace std
 
 // NOTE: ProgramState macros are not used here, because the visibility of these
 // GDM entries must span multiple translation units (multiple checkers).
+// TODO: check if this is still true after finishing the implementation.
 namespace clang {
 namespace ento {
 template <>
@@ -105,6 +140,24 @@ struct ProgramStateTrait<clang::ento::mutex_modeling::CritSections>
     return &Index;
   }
 };
+template <>
+struct ProgramStateTrait<clang::ento::mutex_modeling::LockStates>
+    : public ProgramStatePartialTrait<
+          clang::ento::mutex_modeling::LockStatesTy> {
+  static void *GDMIndex() {
+    static int Index;
+    return &Index;
+  }
+};
+template <>
+struct ProgramStateTrait<clang::ento::mutex_modeling::DestroyedRetVals>
+    : public ProgramStatePartialTrait<
+          clang::ento::mutex_modeling::DestroyedRetValsTy> {
+  static void *GDMIndex() {
+    static int Index;
+    return &Index;
+  }
+};
 } // namespace ento
 } // namespace clang
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexRegionExtractor.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexRegionExtractor.h
new file mode 100644
index 00000000000000..88d7b79daad1b4
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexRegionExtractor.h
@@ -0,0 +1,131 @@
+//===--- MutexRegionExtractor.h - Modeling of mutexes ---------------------===//
+//
+// 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 modeling checker for tracking mutex states.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELING_MUTEXREGIONEXTRACTOR_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELING_MUTEXREGIONEXTRACTOR_H
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
+#include <variant>
+
+namespace clang::ento::mutex_modeling {
+
+class FirstArgMutexExtractor : public clang::ento::CallDescription {
+public:
+  template <typename T>
+  FirstArgMutexExtractor(T &&CD) : CallDescription(std::forward<T>(CD)) {}
+
+  [[nodiscard]] const clang::ento::MemRegion *
+  getRegion(const clang::ento::CallEvent &Call) const {
+    return Call.getArgSVal(0).getAsRegion();
+  }
+};
+
+class MemberMutexExtractor : public clang::ento::CallDescription {
+public:
+  template <typename T>
+  MemberMutexExtractor(T &&CD) : CallDescription(std::forward<T>(CD)) {}
+
+  [[nodiscard]] const clang::ento::MemRegion *
+  getRegion(const clang::ento::CallEvent &Call) const {
+    return llvm::cast<clang::ento::CXXMemberCall>(Call)
+        .getCXXThisVal()
+        .getAsRegion();
+  }
+};
+
+template <bool IsLock> class RAIIMutexExtractor {
+  mutable const clang::IdentifierInfo *Guard{};
+  mutable bool IdentifierInfoInitialized{};
+  mutable llvm::SmallString<32> GuardName{};
+
+  void initIdentifierInfo(const clang::ento::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.
+      const auto &ASTCtx = Call.getState()->getStateManager().getContext();
+      Guard = &ASTCtx.Idents.get(GuardName);
+    }
+  }
+
+  template <typename T>
+  bool matchesImpl(const clang::ento::CallEvent &Call) const {
+    const T *C = llvm::dyn_cast<T>(&Call);
+    if (!C)
+      return false;
+    const clang::IdentifierInfo *II =
+        llvm::cast<clang::CXXRecordDecl>(C->getDecl()->getParent())
+            ->getIdentifier();
+    return II == Guard;
+  }
+
+public:
+  RAIIMutexExtractor(llvm::StringRef GuardName) : GuardName(GuardName) {}
+  [[nodiscard]] bool matches(const clang::ento::CallEvent &Call) const {
+    initIdentifierInfo(Call);
+    if constexpr (IsLock) {
+      return matchesImpl<clang::ento::CXXConstructorCall>(Call);
+    } else {
+      return matchesImpl<clang::ento::CXXDestructorCall>(Call);
+    }
+  }
+  [[nodiscard]] const clang::ento::MemRegion *
+  getRegion(const clang::ento::CallEvent &Call) const {
+    const clang::ento::MemRegion *MutexRegion = nullptr;
+    if constexpr (IsLock) {
+      if (std::optional<clang::ento::SVal> Object =
+              Call.getReturnValueUnderConstruction()) {
+        MutexRegion = Object->getAsRegion();
+      }
+    } else {
+      MutexRegion =
+          llvm::cast<CXXDestructorCall>(Call).getCXXThisVal().getAsRegion();
+    }
+    return MutexRegion;
+  }
+};
+
+using RAIILockExtractor = RAIIMutexExtractor<true>;
+using RAIIReleaseExtractor = RAIIMutexExtractor<false>;
+
+using MutexRegionExtractor =
+    std::variant<FirstArgMutexExtractor, MemberMutexExtractor,
+                 RAIILockExtractor, RAIIReleaseExtractor>;
+
+inline const clang::ento::MemRegion *
+getRegion(const MutexRegionExtractor &Extractor,
+          const clang::ento::CallEvent &Call) {
+  return std::visit(
+      [&Call](auto &&Descriptor) { return Descriptor.getRegion(Call); },
+      Extractor);
+}
+
+inline bool operator==(const MutexRegionExtractor &LHS,
+                       const MutexRegionExtractor &RHS) {
+  return std::visit([](auto &&LHS, auto &&RHS) { return LHS == RHS; }, LHS,
+                    RHS);
+}
+
+inline bool matches(const MutexRegionExtractor &Extractor,
+                    const CallEvent &Call) {
+  return std::visit(
+      [](auto &&Extractor, const CallEvent &Call) {
+        return Extractor.matches(Call);
+      },
+      Extractor, Call);
+}
+
+} // namespace clang::ento::mutex_modeling
+
+#endif

>From 64d07fcf6db9982048ef03548e9c8f7e690ec146 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Thu, 25 Jul 2024 22:12:18 +0200
Subject: [PATCH 09/30] create multiple headers

---
 .../clang/StaticAnalyzer/Checkers/Checkers.td |   2 +-
 .../BlockInCriticalSectionChecker.cpp         |   2 +-
 .../StaticAnalyzer/Checkers/MutexModeling.cpp |  98 ++---
 .../Checkers/MutexModeling/MutexModelingAPI.h | 155 ++++++-
 .../MutexModeling/MutexModelingDefs.h         |  71 ++--
 .../MutexModeling/MutexModelingDomain.h       |  46 ++-
 .../Checkers/MutexModeling/MutexModelingGDM.h |   4 +-
 .../MutexModeling/MutexRegionExtractor.h      |  62 +--
 .../Checkers/PthreadLockChecker.cpp           | 386 +++++-------------
 9 files changed, 402 insertions(+), 424 deletions(-)

diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 96dfa3a35ea1ef..e504b08946242a 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -622,7 +622,7 @@ def ChrootChecker : Checker<"Chroot">,
 
 def PthreadLockChecker : Checker<"PthreadLock">,
   HelpText<"Simple lock -> unlock checker">,
-  Dependencies<[PthreadLockBase]>,
+  Dependencies<[PthreadLockBase, MutexModeling]>,
   Documentation<HasDocumentation>;
 
 def SimpleStreamChecker : Checker<"SimpleStream">,
diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 91c3d154c3bbc2..12b61a36cf934c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -53,7 +53,7 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
 } // end anonymous namespace
 
 BlockInCriticalSectionChecker::BlockInCriticalSectionChecker() {
-  RegisterCheckerForMutexModeling(&BlockInCritSectionBugType);
+  RegisterBugTypeForMutexModeling(&BlockInCritSectionBugType);
 }
 
 bool BlockInCriticalSectionChecker::isBlockingInCritSection(
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
index badb6ed64fd312..0c8166ac61c6a6 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
@@ -119,77 +119,61 @@ void updateCriticalSectionOnUnlock(const EventDescriptor &UnlockDescriptor,
 }
 
 class MutexModeling : public Checker<check::PostCall> {
-  std::vector<EventDescriptor> handledEvents = getHandledEvents();
-
 public:
   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
 
 private:
-  std::unique_ptr<BugType> BT_initlock = std::make_unique<BugType>(
-      this->getCheckerName(), "Init invalid lock", "Lock checker");
+  mutable std::unique_ptr<BugType> BT_initlock;
   ProgramStateRef handleInit(const EventDescriptor &Event, const MemRegion *MTX,
-                             const Expr *MTXExpr, const CallEvent &Call,
-                             ProgramStateRef State, CheckerContext &C) const;
+                             const CallEvent &Call, ProgramStateRef State,
+                             CheckerContext &C) const;
   ProgramStateRef handleEvent(const EventDescriptor &Event,
-                              const MemRegion *MTX, const Expr *MTXExpr,
-                              const CallEvent &Call, ProgramStateRef State,
-                              CheckerContext &C) const;
+                              const MemRegion *MTX, const CallEvent &Call,
+                              ProgramStateRef State, CheckerContext &C) const;
 };
 
 } // namespace
 
-ProgramStateRef
-MutexModeling::handleInit(const EventDescriptor &Event, const MemRegion *MTX,
-                          const Expr *MTXExpr, const CallEvent &Call,
-                          ProgramStateRef State, CheckerContext &C) const {
+ProgramStateRef MutexModeling::handleInit(const EventDescriptor &Event,
+                                          const MemRegion *MTX,
+                                          const CallEvent &Call,
+                                          ProgramStateRef State,
+                                          CheckerContext &C) const {
   assert(MTX && "should only be called with a mutex region");
-  assert(MTXExpr && "should only be called with a valid mutex expression");
 
-  State = State->add<MutexEvents>(EventMarker{
-      Event.Kind, Event.Semantics, Call.getCalleeIdentifier(), MTXExpr, MTX});
+  State = State->add<MutexEvents>(
+      EventMarker{Event.Kind, Event.Semantics, Event.Library,
+                  Call.getCalleeIdentifier(), Call.getOriginExpr(), MTX});
 
   const LockStateKind *LState = State->get<LockStates>(MTX);
-  if (!LState || *LState == LockStateKind::Destroyed) {
+
+  if (!LState)
     return State->set<LockStates>(MTX, LockStateKind::Unlocked);
-  }
 
-  // We are here if three is an init event on a lock that is modelled, and it
-  // is not in destroyed state. The bugreporting should be done in the
-  // reporting checker and not it the modeling, but we still want to be
-  // efficient.
-  StringRef Message;
-  if (*LState == LockStateKind::Locked) {
-    Message = "This lock is still being held";
-    State =
-        State->set<LockStates>(MTX, LockStateKind::Error_DoubleInitWhileLocked);
-  } else {
-    Message = "This lock has already been initialized";
-    State = State->set<LockStates>(MTX, LockStateKind::Error_DoubleInit);
+  switch (*LState) {
+  case (LockStateKind::Destroyed): {
+    return State->set<LockStates>(MTX, LockStateKind::Unlocked);
+  }
+  case (LockStateKind::Locked): {
+    return State->set<LockStates>(MTX,
+                                  LockStateKind::Error_DoubleInitWhileLocked);
+  }
+  default: {
+    return State->set<LockStates>(MTX, LockStateKind::Error_DoubleInit);
+  }
   }
-
-  // TODO: put this part in the reporting checker
-
-  ExplodedNode *N = C.generateErrorNode();
-  if (!N)
-    return State;
-  auto Report =
-      std::make_unique<PathSensitiveBugReport>(BT_initlock.get(), Message, N);
-  Report->addRange(MTXExpr->getSourceRange());
-  C.emitReport(std::move(Report));
-
-  return State;
 }
 
-ProgramStateRef
-MutexModeling::handleEvent(const EventDescriptor &Event, const MemRegion *MTX,
-                           const Expr *MTXExpr, const CallEvent &Call,
-                           ProgramStateRef State, CheckerContext &C) const {
+ProgramStateRef MutexModeling::handleEvent(const EventDescriptor &Event,
+                                           const MemRegion *MTX,
+                                           const CallEvent &Call,
+                                           ProgramStateRef State,
+                                           CheckerContext &C) const {
   assert(MTX && "should only be called with a mutex region");
-  assert(MTXExpr && "should only be called with a valid mutex expression");
 
   switch (Event.Kind) {
   case EventKind::Init:
-    return handleInit(Event, MTX, MTXExpr, Call, State, C);
+    return handleInit(Event, MTX, Call, State, C);
     break;
   default:
     llvm_unreachable("Unhandled event kind!");
@@ -212,33 +196,33 @@ MutexModeling::handleEvent(const EventDescriptor &Event, const MemRegion *MTX,
 
 void MutexModeling::checkPostCall(const CallEvent &Call,
                                   CheckerContext &C) const {
-  // FIXME: Try to handle cases when the implementation was inlined rather than
-  // just giving up.
+  // FIXME: Try to handle cases when the implementation was inlined rather
+  // than just giving up.
+
   if (C.wasInlined)
     return;
 
   ProgramStateRef State = C.getState();
-  for (auto &&Event : handledEvents) {
+  for (auto &&Event : RegisteredEvents) {
     if (matches(Event.Trigger, Call)) {
       const MemRegion *MTX = getRegion(Event.Trigger, Call);
       if (!MTX)
         continue;
-      const Expr *MTXExpr = Call.getOriginExpr();
-      if (!MTXExpr)
-        continue;
       State = doResolvePossiblyDestroyedMutex(State, MTX);
-      State = handleEvent(Event, MTX, MTXExpr, Call, State, C);
+      State = handleEvent(Event, MTX, Call, State, C);
       C.addTransition(State);
     }
   }
-
-} // namespace
+}
 
 namespace clang {
 namespace ento {
 // Checker registration
 void registerMutexModeling(CheckerManager &mgr) {
   mgr.registerChecker<MutexModeling>();
+  RegisterEvent(
+      EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_init"}, 2),
+                      EventKind::Init, LibraryKind::Pthread});
 }
 bool shouldRegisterMutexModeling(const CheckerManager &) { return true; }
 } // namespace ento
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h
index 58d16045366973..2e53b71c932eb4 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h
@@ -28,14 +28,44 @@ namespace ento {
 class BugType;
 namespace mutex_modeling {
 
-inline llvm::SmallSet<const BugType *, 8> RegisteredCheckers{};
+inline llvm::SmallSet<const BugType *, 0> RegisteredBugTypes{};
 
-inline void RegisterCheckerForMutexModeling(const BugType *BT) {
-  RegisteredCheckers.insert(BT);
+inline void RegisterBugTypeForMutexModeling(const BugType *BT) {
+  RegisteredBugTypes.insert(BT);
 }
 
-inline bool IsCheckerRegisteredForMutexModeling(const BugType *BT) {
-  return RegisteredCheckers.contains(BT);
+inline bool IsBugTypeRegisteredForMutexModeling(const BugType *BT) {
+  return RegisteredBugTypes.contains(BT);
+}
+
+inline llvm::SmallVector<EventDescriptor, 0> RegisteredEvents{};
+
+inline auto RegisterEvent(EventDescriptor Event) {
+  RegisteredEvents.push_back(Event);
+}
+
+// Opinionated make functions for commonly used parameter values as default
+// arguments.
+inline auto
+MakeFirstArgExtractor(ArrayRef<StringRef> NameParts, int NumArgsRequired = 1,
+                      CallDescription::Mode MatchAs = CDM::CLibrary) {
+  return FirstArgMutexExtractor{
+      CallDescription{MatchAs, NameParts, NumArgsRequired}};
+}
+
+inline auto MakeMemberExtractor(ArrayRef<StringRef> NameParts,
+                                int NumArgsRequired = 1,
+                                CallDescription::Mode MatchAs = CDM::CLibrary) {
+  return MemberMutexExtractor{
+      CallDescription{MatchAs, NameParts, NumArgsRequired}};
+}
+
+inline auto MakeRAIILockExtractor(StringRef GuardObjectName) {
+  return RAIILockExtractor{GuardObjectName};
+}
+
+inline auto MakeRAIIReleaseExtractor(StringRef GuardObjectName) {
+  return RAIIReleaseExtractor{GuardObjectName};
 }
 
 inline bool AreAnyCritsectionsActive(CheckerContext &C) {
@@ -46,7 +76,7 @@ inline const NoteTag *CreateMutexCritSectionNote(CritSectionMarker M,
                                                  CheckerContext &C) {
   return C.getNoteTag([M](const PathSensitiveBugReport &BR,
                           llvm::raw_ostream &OS) {
-    if (!IsCheckerRegisteredForMutexModeling(&BR.getBugType()))
+    if (!IsBugTypeRegisteredForMutexModeling(&BR.getBugType()))
       return;
     const auto CritSectionBegins =
         BR.getErrorNode()->getState()->get<CritSections>();
@@ -87,6 +117,119 @@ inline const NoteTag *CreateMutexCritSectionNote(CritSectionMarker M,
   });
 }
 
+inline void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
+                       const char *Sep) {
+
+  const MutexEventsTy &ME = State->get<MutexEvents>();
+  if (!ME.isEmpty()) {
+    Out << Sep << "Mutex event:" << NL;
+    for (auto I : ME) {
+      Out << Sep << "Kind: " << ": ";
+      switch (I.Kind) {
+      case (EventKind::Init):
+        Out << "Init";
+        break;
+      case (EventKind::Acquire):
+        Out << "Acquire";
+        break;
+      case (EventKind::TryAcquire):
+        Out << "TryAcquire";
+        break;
+      case (EventKind::Release):
+        Out << "Release";
+        break;
+      case (EventKind::Destroy):
+        Out << "Destroy";
+        break;
+      default:
+        llvm_unreachable("Unknown event kind");
+      }
+      Out << NL;
+      Out << Sep << "Semantics: ";
+      switch (I.Semantics) {
+      case (SemanticsKind::NotApplicable):
+        Out << "NotApplicable";
+        break;
+      case (SemanticsKind::PthreadSemantics):
+        Out << "PthreadSemantics";
+        break;
+      case (SemanticsKind::XNUSemantics):
+        Out << "XNUSemantics";
+        break;
+      default:
+        llvm_unreachable("Unknown semantics");
+      }
+      Out << NL;
+      Out << Sep << "Library: ";
+      switch (I.Library) {
+      case (LibraryKind::Pthread):
+        Out << "Pthread";
+        break;
+      case (LibraryKind::Fuchsia):
+        Out << "Fuchsia";
+        break;
+      case (LibraryKind::C11):
+        Out << "C11";
+        break;
+      default:
+        llvm_unreachable("Unknown library");
+      }
+      Out << NL;
+
+      // Omit MutexExpr and EventExpr
+
+      Out << Sep << "Mutex region: ";
+      I.MutexRegion->dumpToStream(Out);
+      Out << NL;
+    }
+
+    const LockStatesTy &LM = State->get<LockStates>();
+    if (!LM.isEmpty()) {
+      Out << Sep << "Mutex states:" << NL;
+      for (auto I : LM) {
+        I.first->dumpToStream(Out);
+        switch (I.second) {
+        case (LockStateKind::Locked):
+          Out << ": locked";
+          break;
+        case (LockStateKind::Unlocked):
+          Out << ": unlocked";
+          break;
+        case (LockStateKind::Destroyed):
+          Out << ": destroyed";
+          break;
+        case (LockStateKind::UntouchedAndPossiblyDestroyed):
+          Out << ": not tracked, possibly destroyed";
+          break;
+        case (LockStateKind::UnlockedAndPossiblyDestroyed):
+          Out << ": unlocked, possibly destroyed";
+          break;
+        case (LockStateKind::Error_DoubleInit):
+          Out << ": error: double init";
+          break;
+        case (LockStateKind::Error_DoubleInitWhileLocked):
+          Out << ": error: double init while locked";
+          break;
+        default:
+          llvm_unreachable("Unknown lock state");
+        }
+        Out << NL;
+      }
+    }
+
+    const DestroyedRetValsTy &DRV = State->get<DestroyedRetVals>();
+    if (!DRV.isEmpty()) {
+      Out << Sep << "Mutexes in unresolved possibly destroyed state:" << NL;
+      for (auto I : DRV) {
+        I.first->dumpToStream(Out);
+        Out << ": ";
+        I.second->dumpToStream(Out);
+        Out << NL;
+      }
+    }
+  }
+}
+
 } // namespace mutex_modeling
 } // namespace ento
 } // namespace clang
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDefs.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDefs.h
index 936fd5fc87ea34..d0b2f5957bdcfe 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDefs.h
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDefs.h
@@ -20,39 +20,42 @@
 
 namespace clang::ento::mutex_modeling {
 
-inline auto getHandledEvents() -> std::vector<EventDescriptor> {
-  return std::vector<EventDescriptor>{
-                     // - Pthread
-                     EventDescriptor{FirstArgMutexExtractor{CallDescription{
-                         CDM::CLibrary, {"pthread_mutex_init"}, 2}}},
-                     EventKind::Init, SemanticsKind::NotApplicable},
-         // TODO: pthread_rwlock_init(2 arguments).
-         // TODO: lck_mtx_init(3 arguments).
-         // TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex.
-         // TODO: lck_rw_init(3 arguments).
-         // TODO: lck_rw_alloc_init(2 arguments) => returns the mutex.
-
-         // - Fuchsia
-         EventDescriptor{CallDescription{CDM::CLibrary, {"spin_lock_init"}, 1},
-                         Event::Init, Syntax::FirstArg,
-                         LockingSemantics::NotApplicable},
-
-         // - C11
-         EventDescriptor{CallDescription{CDM::CLibrary, {"mtx_init"}, 2},
-                         Event::Init, Syntax::FirstArg,
-                         LockingSemantics::NotApplicable},
-
-         // Acquire kind
-         // - Pthread
-         //
-         EventDescriptor{
-             CallDescription{CDM::CLibrary, {"pthread_mutex_lock"}, 1},
-             Event::Acquire, Syntax::FirstArg,
-             LockingSemantics::PthreadSemantics},
-         EventDescriptor{
-             CallDescription{CDM::CLibrary, {"pthread_rwlock_rdlock"}, 1},
-             Event::Acquire, Syntax::FirstArg,
-             LockingSemantics::PthreadSemantics},
+static auto getHandledEvents(){return std::vector<EventDescriptor> {
+  // - Pthread
+  EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_init"}), EventKind::Init,
+                  LibraryKind::Pthread},
+#if 0
+             // TODO: pthread_rwlock_init(2 arguments).
+             // TODO: lck_mtx_init(3 arguments).
+             // TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex.
+             // TODO: lck_rw_init(3 arguments).
+             // TODO: lck_rw_alloc_init(2 arguments) => returns the mutex.
+
+             // - Fuchsia
+             EventDescriptor{FirstArgMutexExtractor{CallDescription{
+                                 CDM::CLibrary, {"spin_lock_init"}, 1}},
+                             EventKind::Init, LibraryKind::Fuchsia},
+
+             // - C11
+             EventDescriptor{FirstArgMutexExtractor{CallDescription{
+                                 CDM::CLibrary, {"mtx_init"}, 2}},
+                             EventKind::Init, LibraryKind::C11},
+
+             // Acquire kind
+             // - Pthread
+             //
+             EventDescriptor{FirstArgMutexExtractor{CallDescription{
+                                 CDM::CLibrary, {"pthread_mutex_lock"}, 1}},
+                             Event::Acquire, LibraryKind::Pthread,
+                             SemanticsKind::PthreadSemantics},
+             EventDescriptor{FirstArgMutexExtractor{
+                 CallDescription{CDM::CLibrary, {"pthread_rwlock_rdlock"}, 1},
+                 Event::Acquire, LibraryKind::Pthread,
+                 SemanticsKind::PthreadSemantics}},
+             EventDescriptor{FirstArgMutexExtractor{CallDescription{
+                                 CDM::CLibrary, {"pthread_rwlock_wrlock"}, 1}},
+                             Event::Acquire, LibraryKind::Pthread,
+                             SemanticsKind::PthreadSemantics}},
          EventDescriptor{
              CallDescription{CDM::CLibrary, {"pthread_rwlock_wrlock"}, 1},
              Event::Acquire, Syntax::FirstArg,
@@ -205,7 +208,9 @@ inline auto getHandledEvents() -> std::vector<EventDescriptor> {
          EventDescriptor{CallDescription{CDM::CLibrary, {"mtx_destroy"}, 1},
                          Event::Destroy, Syntax::FirstArg,
                          LockingSemantics::NotApplicable}
+#endif
 };
+} // namespace clang::ento::mutex_modeling
 
 } // namespace clang::ento::mutex_modeling
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h
index a0dfb8dc73b62a..56e50bf4273efc 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h
@@ -14,21 +14,28 @@
 #define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELINGDOMAIN_H
 
 #include "MutexRegionExtractor.h"
-#include "clang/Basic/IdentifierTable.h"
 
+// Forwad declarations.
 namespace clang {
-
 class Expr;
-
+class IdentifierInfo;
 namespace ento {
-
 class MemRegion;
+} // namespace ento
+} // namespace clang
 
-namespace mutex_modeling {
+namespace clang::ento::mutex_modeling {
 
 enum class EventKind { Init, Acquire, TryAcquire, Release, Destroy };
 
-enum class SyntaxKind { FirstArg, Member, RAII };
+// TODO: Ideally the modeling should not know about which checkers consume the
+// modeling information. This enum is here to make a correspondence between the
+// checked mutex event the library that event came from. In order to keep the
+// external API of multiple distinct checkers (PthreadLockChecker,
+// FuchsiaLockChecker and C11LockChecker), this mapping is done here, but if
+// more consumers of this modeling arise, adding all of them here may note be
+// feasible and we may need to make this modeling more flexible.
+enum class LibraryKind { Pthread = 0, Fuchsia, C11, NumLibraryKinds };
 
 enum class SemanticsKind { NotApplicable = 0, PthreadSemantics, XNUSemantics };
 
@@ -42,35 +49,40 @@ enum class LockStateKind {
   Error_DoubleInitWhileLocked,
 };
 
+/// This class is intended for describing the list of events to detect.
+/// This list of events is the configuration of the MutexModeling checker.
 struct EventDescriptor {
   MutexRegionExtractor Trigger;
   EventKind Kind{};
+  LibraryKind Library{};
   SemanticsKind Semantics{};
 
   // TODO: Modernize to spaceship when C++20 is available.
-  [[nodiscard]] constexpr bool
-  operator!=(const EventDescriptor &Other) const noexcept {
-    return !(Trigger == Other.Trigger) || Kind != Other.Kind ||
-           Semantics != Other.Semantics;
+  [[nodiscard]] bool operator!=(const EventDescriptor &Other) const noexcept {
+    return !(Trigger == Other.Trigger) || Library != Other.Library ||
+           Kind != Other.Kind || Semantics != Other.Semantics;
   }
-  [[nodiscard]] constexpr bool
-  operator==(const EventDescriptor &Other) const noexcept {
+  [[nodiscard]] bool operator==(const EventDescriptor &Other) const noexcept {
     return !(*this != Other);
   }
 };
 
+/// This class is used in the GDM to describe the events that were detected.
+/// As instances of this class can appear many times in the ExplodedGraph, it
+/// best to keep it as simple and small as possible.
 struct EventMarker {
   EventKind Kind{};
   SemanticsKind Semantics{};
-  const IdentifierInfo *Event;
+  LibraryKind Library{};
+  const IdentifierInfo *EventII;
   const clang::Expr *EventExpr{};
   const clang::ento::MemRegion *MutexRegion{};
 
   // TODO: Modernize to spaceship when C++20 is available.
   [[nodiscard]] constexpr bool
   operator!=(const EventMarker &Other) const noexcept {
-    return Event != Other.Event || Kind != Other.Kind ||
-           Semantics != Other.Semantics || LockState != Other.LockState ||
+    return EventII != Other.EventII || Kind != Other.Kind ||
+           Semantics != Other.Semantics || Library != Other.Library ||
            EventExpr != Other.EventExpr || MutexRegion != Other.MutexRegion;
   }
   [[nodiscard]] constexpr bool
@@ -98,8 +110,6 @@ struct CritSectionMarker {
   }
 };
 
-} // namespace mutex_modeling
-} // namespace ento
-} // namespace clang
+} // namespace clang::ento::mutex_modeling
 
 #endif
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingGDM.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingGDM.h
index 566c8d7fdc730c..603f8172c9806a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingGDM.h
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingGDM.h
@@ -48,10 +48,10 @@ namespace llvm {
 template <> struct FoldingSetTrait<clang::ento::mutex_modeling::EventMarker> {
   static void Profile(const clang::ento::mutex_modeling::EventMarker &EM,
                       llvm::FoldingSetNodeID &ID) {
-    ID.Add(EM.Event);
     ID.Add(EM.Kind);
+    ID.Add(EM.Library);
     ID.Add(EM.Semantics);
-    ID.Add(EM.LockState);
+    ID.Add(EM.EventII);
     ID.Add(EM.EventExpr);
     ID.Add(EM.MutexRegion);
   }
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexRegionExtractor.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexRegionExtractor.h
index 88d7b79daad1b4..6fa33f9437adbc 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexRegionExtractor.h
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexRegionExtractor.h
@@ -18,27 +18,35 @@
 
 namespace clang::ento::mutex_modeling {
 
-class FirstArgMutexExtractor : public clang::ento::CallDescription {
+class FirstArgMutexExtractor {
+  CallDescription CD;
+
 public:
   template <typename T>
-  FirstArgMutexExtractor(T &&CD) : CallDescription(std::forward<T>(CD)) {}
+  FirstArgMutexExtractor(T &&CD) : CD(std::forward<T>(CD)) {}
+
+  [[nodiscard]] bool matches(const CallEvent &Call) const {
+    return CD.matches(Call);
+  }
 
-  [[nodiscard]] const clang::ento::MemRegion *
-  getRegion(const clang::ento::CallEvent &Call) const {
+  [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call) const {
     return Call.getArgSVal(0).getAsRegion();
   }
 };
 
-class MemberMutexExtractor : public clang::ento::CallDescription {
+class MemberMutexExtractor {
+  CallDescription CD;
+
 public:
   template <typename T>
-  MemberMutexExtractor(T &&CD) : CallDescription(std::forward<T>(CD)) {}
+  MemberMutexExtractor(T &&CD) : CD(std::forward<T>(CD)) {}
 
-  [[nodiscard]] const clang::ento::MemRegion *
-  getRegion(const clang::ento::CallEvent &Call) const {
-    return llvm::cast<clang::ento::CXXMemberCall>(Call)
-        .getCXXThisVal()
-        .getAsRegion();
+  [[nodiscard]] bool matches(const CallEvent &Call) const {
+    return CD.matches(Call);
+  }
+
+  [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call) const {
+    return llvm::cast<CXXMemberCall>(Call).getCXXThisVal().getAsRegion();
   }
 };
 
@@ -47,7 +55,7 @@ template <bool IsLock> class RAIIMutexExtractor {
   mutable bool IdentifierInfoInitialized{};
   mutable llvm::SmallString<32> GuardName{};
 
-  void initIdentifierInfo(const clang::ento::CallEvent &Call) const {
+  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
@@ -59,8 +67,7 @@ template <bool IsLock> class RAIIMutexExtractor {
     }
   }
 
-  template <typename T>
-  bool matchesImpl(const clang::ento::CallEvent &Call) const {
+  template <typename T> bool matchesImpl(const CallEvent &Call) const {
     const T *C = llvm::dyn_cast<T>(&Call);
     if (!C)
       return false;
@@ -72,21 +79,22 @@ template <bool IsLock> class RAIIMutexExtractor {
 
 public:
   RAIIMutexExtractor(llvm::StringRef GuardName) : GuardName(GuardName) {}
-  [[nodiscard]] bool matches(const clang::ento::CallEvent &Call) const {
+  [[nodiscard]] bool matches(const CallEvent &Call) const {
     initIdentifierInfo(Call);
     if constexpr (IsLock) {
-      return matchesImpl<clang::ento::CXXConstructorCall>(Call);
+      return matchesImpl<CXXConstructorCall>(Call);
     } else {
-      return matchesImpl<clang::ento::CXXDestructorCall>(Call);
+      return matchesImpl<CXXDestructorCall>(Call);
     }
   }
-  [[nodiscard]] const clang::ento::MemRegion *
-  getRegion(const clang::ento::CallEvent &Call) const {
-    const clang::ento::MemRegion *MutexRegion = nullptr;
+  [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call) const {
+    const MemRegion *MutexRegion = nullptr;
     if constexpr (IsLock) {
-      if (std::optional<clang::ento::SVal> Object =
-              Call.getReturnValueUnderConstruction()) {
+      if (std::optional<SVal> Object = Call.getReturnValueUnderConstruction()) {
         MutexRegion = Object->getAsRegion();
+      } else {
+        MutexRegion =
+            llvm::cast<CXXDestructorCall>(Call).getCXXThisVal().getAsRegion();
       }
     } else {
       MutexRegion =
@@ -103,9 +111,8 @@ using MutexRegionExtractor =
     std::variant<FirstArgMutexExtractor, MemberMutexExtractor,
                  RAIILockExtractor, RAIIReleaseExtractor>;
 
-inline const clang::ento::MemRegion *
-getRegion(const MutexRegionExtractor &Extractor,
-          const clang::ento::CallEvent &Call) {
+inline const MemRegion *getRegion(const MutexRegionExtractor &Extractor,
+                                  const CallEvent &Call) {
   return std::visit(
       [&Call](auto &&Descriptor) { return Descriptor.getRegion(Call); },
       Extractor);
@@ -120,10 +127,7 @@ inline bool operator==(const MutexRegionExtractor &LHS,
 inline bool matches(const MutexRegionExtractor &Extractor,
                     const CallEvent &Call) {
   return std::visit(
-      [](auto &&Extractor, const CallEvent &Call) {
-        return Extractor.matches(Call);
-      },
-      Extractor, Call);
+      [&Call](auto &&Extractor) { return Extractor.matches(Call); }, Extractor);
 }
 
 } // namespace clang::ento::mutex_modeling
diff --git a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index 86530086ff1b27..63adf792eba04b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -21,12 +21,14 @@
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
 
+#include "MutexModeling/MutexModelingAPI.h"
+
 using namespace clang;
 using namespace ento;
+using namespace mutex_modeling;
 
 namespace {
 
@@ -68,191 +70,46 @@ struct LockState {
   void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
 };
 
-class PthreadLockChecker : public Checker<check::PostCall, check::DeadSymbols,
-                                          check::RegionChanges> {
+class PthreadLockChecker : public Checker<check::PostCall> {
+// , check::DeadSymbols,
+//                                          check::RegionChanges> {
+
 public:
-  enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics };
   enum CheckerKind {
     CK_PthreadLockChecker,
     CK_FuchsiaLockChecker,
     CK_C11LockChecker,
     CK_NumCheckKinds
   };
+
   bool ChecksEnabled[CK_NumCheckKinds] = {false};
   CheckerNameRef CheckNames[CK_NumCheckKinds];
 
-private:
-  typedef void (PthreadLockChecker::*FnCheck)(const CallEvent &Call,
-                                              CheckerContext &C,
-                                              CheckerKind CheckKind) const;
-  CallDescriptionMap<FnCheck> PThreadCallbacks = {
-      // Init.
-      {{CDM::CLibrary, {"pthread_mutex_init"}, 2},
-       &PthreadLockChecker::InitAnyLock},
-      // TODO: pthread_rwlock_init(2 arguments).
-      // TODO: lck_mtx_init(3 arguments).
-      // TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex.
-      // TODO: lck_rw_init(3 arguments).
-      // TODO: lck_rw_alloc_init(2 arguments) => returns the mutex.
-
-      // Acquire.
-      {{CDM::CLibrary, {"pthread_mutex_lock"}, 1},
-       &PthreadLockChecker::AcquirePthreadLock},
-      {{CDM::CLibrary, {"pthread_rwlock_rdlock"}, 1},
-       &PthreadLockChecker::AcquirePthreadLock},
-      {{CDM::CLibrary, {"pthread_rwlock_wrlock"}, 1},
-       &PthreadLockChecker::AcquirePthreadLock},
-      {{CDM::CLibrary, {"lck_mtx_lock"}, 1},
-       &PthreadLockChecker::AcquireXNULock},
-      {{CDM::CLibrary, {"lck_rw_lock_exclusive"}, 1},
-       &PthreadLockChecker::AcquireXNULock},
-      {{CDM::CLibrary, {"lck_rw_lock_shared"}, 1},
-       &PthreadLockChecker::AcquireXNULock},
-
-      // Try.
-      {{CDM::CLibrary, {"pthread_mutex_trylock"}, 1},
-       &PthreadLockChecker::TryPthreadLock},
-      {{CDM::CLibrary, {"pthread_rwlock_tryrdlock"}, 1},
-       &PthreadLockChecker::TryPthreadLock},
-      {{CDM::CLibrary, {"pthread_rwlock_trywrlock"}, 1},
-       &PthreadLockChecker::TryPthreadLock},
-      {{CDM::CLibrary, {"lck_mtx_try_lock"}, 1},
-       &PthreadLockChecker::TryXNULock},
-      {{CDM::CLibrary, {"lck_rw_try_lock_exclusive"}, 1},
-       &PthreadLockChecker::TryXNULock},
-      {{CDM::CLibrary, {"lck_rw_try_lock_shared"}, 1},
-       &PthreadLockChecker::TryXNULock},
-
-      // Release.
-      {{CDM::CLibrary, {"pthread_mutex_unlock"}, 1},
-       &PthreadLockChecker::ReleaseAnyLock},
-      {{CDM::CLibrary, {"pthread_rwlock_unlock"}, 1},
-       &PthreadLockChecker::ReleaseAnyLock},
-      {{CDM::CLibrary, {"lck_mtx_unlock"}, 1},
-       &PthreadLockChecker::ReleaseAnyLock},
-      {{CDM::CLibrary, {"lck_rw_unlock_exclusive"}, 1},
-       &PthreadLockChecker::ReleaseAnyLock},
-      {{CDM::CLibrary, {"lck_rw_unlock_shared"}, 1},
-       &PthreadLockChecker::ReleaseAnyLock},
-      {{CDM::CLibrary, {"lck_rw_done"}, 1},
-       &PthreadLockChecker::ReleaseAnyLock},
-
-      // Destroy.
-      {{CDM::CLibrary, {"pthread_mutex_destroy"}, 1},
-       &PthreadLockChecker::DestroyPthreadLock},
-      {{CDM::CLibrary, {"lck_mtx_destroy"}, 2},
-       &PthreadLockChecker::DestroyXNULock},
-      // TODO: pthread_rwlock_destroy(1 argument).
-      // TODO: lck_rw_destroy(2 arguments).
-  };
-
-  CallDescriptionMap<FnCheck> FuchsiaCallbacks = {
-      // Init.
-      {{CDM::CLibrary, {"spin_lock_init"}, 1},
-       &PthreadLockChecker::InitAnyLock},
-
-      // Acquire.
-      {{CDM::CLibrary, {"spin_lock"}, 1},
-       &PthreadLockChecker::AcquirePthreadLock},
-      {{CDM::CLibrary, {"spin_lock_save"}, 3},
-       &PthreadLockChecker::AcquirePthreadLock},
-      {{CDM::CLibrary, {"sync_mutex_lock"}, 1},
-       &PthreadLockChecker::AcquirePthreadLock},
-      {{CDM::CLibrary, {"sync_mutex_lock_with_waiter"}, 1},
-       &PthreadLockChecker::AcquirePthreadLock},
-
-      // Try.
-      {{CDM::CLibrary, {"spin_trylock"}, 1},
-       &PthreadLockChecker::TryFuchsiaLock},
-      {{CDM::CLibrary, {"sync_mutex_trylock"}, 1},
-       &PthreadLockChecker::TryFuchsiaLock},
-      {{CDM::CLibrary, {"sync_mutex_timedlock"}, 2},
-       &PthreadLockChecker::TryFuchsiaLock},
-
-      // Release.
-      {{CDM::CLibrary, {"spin_unlock"}, 1},
-       &PthreadLockChecker::ReleaseAnyLock},
-      {{CDM::CLibrary, {"spin_unlock_restore"}, 3},
-       &PthreadLockChecker::ReleaseAnyLock},
-      {{CDM::CLibrary, {"sync_mutex_unlock"}, 1},
-       &PthreadLockChecker::ReleaseAnyLock},
-  };
-
-  CallDescriptionMap<FnCheck> C11Callbacks = {
-      // Init.
-      {{CDM::CLibrary, {"mtx_init"}, 2}, &PthreadLockChecker::InitAnyLock},
-
-      // Acquire.
-      {{CDM::CLibrary, {"mtx_lock"}, 1},
-       &PthreadLockChecker::AcquirePthreadLock},
-
-      // Try.
-      {{CDM::CLibrary, {"mtx_trylock"}, 1}, &PthreadLockChecker::TryC11Lock},
-      {{CDM::CLibrary, {"mtx_timedlock"}, 2}, &PthreadLockChecker::TryC11Lock},
-
-      // Release.
-      {{CDM::CLibrary, {"mtx_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
-
-      // Destroy
-      {{CDM::CLibrary, {"mtx_destroy"}, 1},
-       &PthreadLockChecker::DestroyPthreadLock},
-  };
+  PthreadLockChecker() {
+    RegisterEvents();
+  }
 
-  ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state,
-                                                const MemRegion *lockR,
-                                                const SymbolRef *sym) const;
+private:
+  std::vector<EventDescriptor> EventsToModel{
+      EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_init"}, 2),
+                      EventKind::Init, LibraryKind::Pthread}};
+  void RegisterEvents() const {
+    for (auto &&Event : EventsToModel) {
+      RegisterEvent(Event);
+    }
+  }
   void reportBug(CheckerContext &C, std::unique_ptr<BugType> BT[],
                  const Expr *MtxExpr, CheckerKind CheckKind,
                  StringRef Desc) const;
 
-  // Init.
-  void InitAnyLock(const CallEvent &Call, CheckerContext &C,
-                   CheckerKind CheckKind) const;
-  void InitLockAux(const CallEvent &Call, CheckerContext &C,
-                   const Expr *MtxExpr, SVal MtxVal,
-                   CheckerKind CheckKind) const;
-
-  // Lock, Try-lock.
-  void AcquirePthreadLock(const CallEvent &Call, CheckerContext &C,
-                          CheckerKind CheckKind) const;
-  void AcquireXNULock(const CallEvent &Call, CheckerContext &C,
-                      CheckerKind CheckKind) const;
-  void TryPthreadLock(const CallEvent &Call, CheckerContext &C,
-                      CheckerKind CheckKind) const;
-  void TryXNULock(const CallEvent &Call, CheckerContext &C,
-                  CheckerKind CheckKind) const;
-  void TryFuchsiaLock(const CallEvent &Call, CheckerContext &C,
-                      CheckerKind CheckKind) const;
-  void TryC11Lock(const CallEvent &Call, CheckerContext &C,
-                  CheckerKind CheckKind) const;
-  void AcquireLockAux(const CallEvent &Call, CheckerContext &C,
-                      const Expr *MtxExpr, SVal MtxVal, bool IsTryLock,
-                      LockingSemantics Semantics, CheckerKind CheckKind) const;
-
-  // Release.
-  void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C,
-                      CheckerKind CheckKind) const;
-  void ReleaseLockAux(const CallEvent &Call, CheckerContext &C,
-                      const Expr *MtxExpr, SVal MtxVal,
-                      CheckerKind CheckKind) const;
-
-  // Destroy.
-  void DestroyPthreadLock(const CallEvent &Call, CheckerContext &C,
-                          CheckerKind CheckKind) const;
-  void DestroyXNULock(const CallEvent &Call, CheckerContext &C,
-                      CheckerKind CheckKind) const;
-  void DestroyLockAux(const CallEvent &Call, CheckerContext &C,
-                      const Expr *MtxExpr, SVal MtxVal,
-                      LockingSemantics Semantics, CheckerKind CheckKind) const;
-
 public:
   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
-  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
-  ProgramStateRef
-  checkRegionChanges(ProgramStateRef State, const InvalidatedSymbols *Symbols,
-                     ArrayRef<const MemRegion *> ExplicitRegions,
-                     ArrayRef<const MemRegion *> Regions,
-                     const LocationContext *LCtx, const CallEvent *Call) const;
+  // void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+  // ProgramStateRef
+  // checkRegionChanges(ProgramStateRef State, const InvalidatedSymbols *Symbols,
+  //                    ArrayRef<const MemRegion *> ExplicitRegions,
+  //                    ArrayRef<const MemRegion *> Regions,
+  //                    const LocationContext *LCtx, const CallEvent *Call) const;
   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
                   const char *Sep) const override;
 
@@ -261,6 +118,7 @@ class PthreadLockChecker : public Checker<check::PostCall, check::DeadSymbols,
   mutable std::unique_ptr<BugType> BT_doubleunlock[CK_NumCheckKinds];
   mutable std::unique_ptr<BugType> BT_destroylock[CK_NumCheckKinds];
   mutable std::unique_ptr<BugType> BT_initlock[CK_NumCheckKinds];
+  mutable std::unique_ptr<BugType> BT_initlockPthread;
   mutable std::unique_ptr<BugType> BT_lor[CK_NumCheckKinds];
 
   void initBugType(CheckerKind CheckKind) const {
@@ -277,111 +135,97 @@ class PthreadLockChecker : public Checker<check::PostCall, check::DeadSymbols,
     BT_lor[CheckKind].reset(new BugType{CheckNames[CheckKind],
                                         "Lock order reversal", "Lock checker"});
   }
+
+  [[nodiscard]] constexpr PthreadLockChecker::CheckerKind
+  detectCheckerKind(mutex_modeling::EventMarker EV) const noexcept {
+    switch (EV.Library) {
+    case mutex_modeling::LibraryKind::Pthread:
+      return PthreadLockChecker::CK_PthreadLockChecker;
+    case mutex_modeling::LibraryKind::Fuchsia:
+      return PthreadLockChecker::CK_FuchsiaLockChecker;
+    case mutex_modeling::LibraryKind::C11:
+      return PthreadLockChecker::CK_C11LockChecker;
+    default:
+      llvm_unreachable("Unknown locking library");
+    }
+  }
 };
 } // end anonymous namespace
 
-// A stack of locks for tracking lock-unlock order.
-REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *)
+void PthreadLockChecker::checkPostCall(const CallEvent &Call,
+                                       CheckerContext &C) const {
 
-// An entry for tracking lock states.
-REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState)
+  ProgramStateRef State = C.getState();
 
-// Return values for unresolved calls to pthread_mutex_destroy().
-REGISTER_MAP_WITH_PROGRAMSTATE(DestroyRetVal, const MemRegion *, SymbolRef)
+  const auto &MTXEvents = State->get<MutexEvents>();
 
-void PthreadLockChecker::checkPostCall(const CallEvent &Call,
-                                       CheckerContext &C) const {
-  // FIXME: Try to handle cases when the implementation was inlined rather
-  // than just giving up.
-  if (C.wasInlined)
+  Call.dump(llvm::errs());
+  llvm::errs() << "before MTXEvents isEmpty check\n";
+
+  if (MTXEvents.isEmpty()) {
     return;
+  }
 
-  if (const FnCheck *Callback = PThreadCallbacks.lookup(Call))
-    (this->**Callback)(Call, C, CK_PthreadLockChecker);
-  else if (const FnCheck *Callback = FuchsiaCallbacks.lookup(Call))
-    (this->**Callback)(Call, C, CK_FuchsiaLockChecker);
-  else if (const FnCheck *Callback = C11Callbacks.lookup(Call))
-    (this->**Callback)(Call, C, CK_C11LockChecker);
-}
+  llvm::errs() << "after MTXEvents isEmpty check\n";
+  printState(llvm::errs(), State, "\n", " ");
 
-// When a lock is destroyed, in some semantics(like PthreadSemantics) we are not
-// sure if the destroy call has succeeded or failed, and the lock enters one of
-// the 'possibly destroyed' state. There is a short time frame for the
-// programmer to check the return value to see if the lock was successfully
-// destroyed. Before we model the next operation over that lock, we call this
-// function to see if the return value was checked by now and set the lock state
-// - either to destroyed state or back to its previous state.
-
-// In PthreadSemantics, pthread_mutex_destroy() returns zero if the lock is
-// successfully destroyed and it returns a non-zero value otherwise.
-ProgramStateRef PthreadLockChecker::resolvePossiblyDestroyedMutex(
-    ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const {
-  const LockState *lstate = state->get<LockMap>(lockR);
-  // Existence in DestroyRetVal ensures existence in LockMap.
-  // Existence in Destroyed also ensures that the lock state for lockR is either
-  // UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed.
-  assert(lstate);
-  assert(lstate->isUntouchedAndPossiblyDestroyed() ||
-         lstate->isUnlockedAndPossiblyDestroyed());
-
-  ConstraintManager &CMgr = state->getConstraintManager();
-  ConditionTruthVal retZero = CMgr.isNull(state, *sym);
-  if (retZero.isConstrainedFalse()) {
-    if (lstate->isUntouchedAndPossiblyDestroyed())
-      state = state->remove<LockMap>(lockR);
-    else if (lstate->isUnlockedAndPossiblyDestroyed())
-      state = state->set<LockMap>(lockR, LockState::getUnlocked());
-  } else
-    state = state->set<LockMap>(lockR, LockState::getDestroyed());
-
-  // Removing the map entry (lockR, sym) from DestroyRetVal as the lock state is
-  // now resolved.
-  state = state->remove<DestroyRetVal>(lockR);
-  return state;
-}
+  const auto &LastEvent = MTXEvents.getHead();
 
-void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State,
-                                    const char *NL, const char *Sep) const {
-  LockMapTy LM = State->get<LockMap>();
-  if (!LM.isEmpty()) {
-    Out << Sep << "Mutex states:" << NL;
-    for (auto I : LM) {
-      I.first->dumpToStream(Out);
-      if (I.second.isLocked())
-        Out << ": locked";
-      else if (I.second.isUnlocked())
-        Out << ": unlocked";
-      else if (I.second.isDestroyed())
-        Out << ": destroyed";
-      else if (I.second.isUntouchedAndPossiblyDestroyed())
-        Out << ": not tracked, possibly destroyed";
-      else if (I.second.isUnlockedAndPossiblyDestroyed())
-        Out << ": unlocked, possibly destroyed";
-      Out << NL;
-    }
+  if (LastEvent.Kind != EventKind::Init) {
+    return;
   }
 
-  LockSetTy LS = State->get<LockSet>();
-  if (!LS.isEmpty()) {
-    Out << Sep << "Mutex lock order:" << NL;
-    for (auto I : LS) {
-      I->dumpToStream(Out);
-      Out << NL;
-    }
+  const LockStateKind *const LockState =
+      State->get<LockStates>(LastEvent.MutexRegion);
+
+  if (!LockState || *LockState == LockStateKind::Destroyed) {
+    return;
   }
 
-  DestroyRetValTy DRV = State->get<DestroyRetVal>();
-  if (!DRV.isEmpty()) {
-    Out << Sep << "Mutexes in unresolved possibly destroyed state:" << NL;
-    for (auto I : DRV) {
-      I.first->dumpToStream(Out);
-      Out << ": ";
-      I.second->dumpToStream(Out);
-      Out << NL;
-    }
+  bool IsError = false;
+  StringRef Message;
+  switch (*LockState) {
+  case LockStateKind::Error_DoubleInit: {
+    IsError = true;
+    Message = "This lock has already been initialized";
+    break;
+  }
+  case LockStateKind::Error_DoubleInitWhileLocked: {
+    IsError = true;
+    Message = "This lock is still being held";
+    break;
   }
+  default: {
+  }
+  }
+
+  if (!IsError) {
+    return;
+  }
+
+  reportBug(C, BT_initlock, LastEvent.EventExpr, detectCheckerKind(LastEvent), Message);
+}
+
+void PthreadLockChecker::reportBug(CheckerContext &C,
+                                   std::unique_ptr<BugType> BT[],
+                                   const Expr *MtxExpr, CheckerKind CheckKind,
+                                   StringRef Desc) const {
+  ExplodedNode *N = C.generateErrorNode();
+  if (!N)
+    return;
+  initBugType(CheckKind);
+  auto Report =
+      std::make_unique<PathSensitiveBugReport>(*BT[CheckKind], Desc, N);
+  Report->addRange(MtxExpr->getSourceRange());
+  C.emitReport(std::move(Report));
 }
 
+void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State,
+                                    const char *NL, const char *Sep) const {
+  mutex_modeling::printState(Out, State, NL, Sep);
+}
+
+#if 0
 void PthreadLockChecker::AcquirePthreadLock(const CallEvent &Call,
                                             CheckerContext &C,
                                             CheckerKind CheckKind) const {
@@ -650,20 +494,6 @@ void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C,
   reportBug(C, BT_initlock, MtxExpr, CheckKind, Message);
 }
 
-void PthreadLockChecker::reportBug(CheckerContext &C,
-                                   std::unique_ptr<BugType> BT[],
-                                   const Expr *MtxExpr, CheckerKind CheckKind,
-                                   StringRef Desc) const {
-  ExplodedNode *N = C.generateErrorNode();
-  if (!N)
-    return;
-  initBugType(CheckKind);
-  auto Report =
-      std::make_unique<PathSensitiveBugReport>(*BT[CheckKind], Desc, N);
-  Report->addRange(MtxExpr->getSourceRange());
-  C.emitReport(std::move(Report));
-}
-
 void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper,
                                           CheckerContext &C) const {
   ProgramStateRef State = C.getState();
@@ -725,23 +555,25 @@ ProgramStateRef PthreadLockChecker::checkRegionChanges(
 
   return State;
 }
+#endif
 
 void ento::registerPthreadLockBase(CheckerManager &mgr) {
   mgr.registerChecker<PthreadLockChecker>();
 }
 
-bool ento::shouldRegisterPthreadLockBase(const CheckerManager &mgr) { return true; }
+bool ento::shouldRegisterPthreadLockBase(const CheckerManager &mgr) {
+  return true;
+}
 
-#define REGISTER_CHECKER(name)                                                 \
+#define REGISTER_CHECKER(name, library)                                        \
   void ento::register##name(CheckerManager &mgr) {                             \
     PthreadLockChecker *checker = mgr.getChecker<PthreadLockChecker>();        \
     checker->ChecksEnabled[PthreadLockChecker::CK_##name] = true;              \
     checker->CheckNames[PthreadLockChecker::CK_##name] =                       \
         mgr.getCurrentCheckerName();                                           \
   }                                                                            \
-                                                                               \
   bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
 
-REGISTER_CHECKER(PthreadLockChecker)
-REGISTER_CHECKER(FuchsiaLockChecker)
-REGISTER_CHECKER(C11LockChecker)
+REGISTER_CHECKER(PthreadLockChecker, LibraryKind::Pthread)
+REGISTER_CHECKER(FuchsiaLockChecker, LibraryKind::Fuchsia)
+REGISTER_CHECKER(C11LockChecker, LibraryKind::C11)

>From 0c4cbfca2f0d3a5abb3d21834f1baab747d5ef28 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Fri, 26 Jul 2024 09:50:47 +0200
Subject: [PATCH 10/30] add acquire to modeling

---
 .../StaticAnalyzer/Checkers/MutexModeling.cpp | 45 +++++++++-
 .../MutexModeling/MutexModelingDomain.h       |  2 +
 .../Checkers/PthreadLockChecker.cpp           | 89 +++++++++++++------
 3 files changed, 103 insertions(+), 33 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
index 0c8166ac61c6a6..319b3e39a8055c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
@@ -141,10 +141,6 @@ ProgramStateRef MutexModeling::handleInit(const EventDescriptor &Event,
                                           CheckerContext &C) const {
   assert(MTX && "should only be called with a mutex region");
 
-  State = State->add<MutexEvents>(
-      EventMarker{Event.Kind, Event.Semantics, Event.Library,
-                  Call.getCalleeIdentifier(), Call.getOriginExpr(), MTX});
-
   const LockStateKind *LState = State->get<LockStates>(MTX);
 
   if (!LState)
@@ -164,6 +160,43 @@ ProgramStateRef MutexModeling::handleInit(const EventDescriptor &Event,
   }
 }
 
+ProgramStateRef handleAcquire(const EventDescriptor &Event,
+                              const MemRegion *MTX, const CallEvent &Call,
+                              ProgramStateRef State, CheckerContext &C) const {
+  assert(MTX && "should only be called with a mutex region");
+
+  const LockStateKind *LState = State->get<LockStates>(MTX);
+
+  if (!LState)
+    return State->set<LockStates>(MTX, LockStateKind::Locked);
+
+  switch (*LState) {
+  case (LockStateKind::Locked): {
+    return State->set<LockStates>(MTX, LockStateKind::Error_DoubleLock);
+  }
+  case (LockStateKind::Destroyed): {
+    return State->set<LockStates>(MTX, LockStateKind::Error_LockDestroyed);
+  }
+  }
+
+  if (Semantics == PthreadSemantics) {
+    // Assume that the return value was 0.
+    SVal RetVal = Call.getReturnValue();
+    if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
+      // FIXME: If the lock function was inlined and returned true,
+      // we need to behave sanely - at least generate sink.
+      lockSucc = state->assume(*DefinedRetVal, false);
+      assert(lockSucc);
+    }
+    // We might want to handle the case when the mutex lock function was inlined
+    // and returned an Unknown or Undefined value.
+  } else {
+    // XNU locking semantics return void on non-try locks
+    assert((Semantics == XNUSemantics) && "Unknown locking semantics");
+    lockSucc = state;
+  }
+}
+
 ProgramStateRef MutexModeling::handleEvent(const EventDescriptor &Event,
                                            const MemRegion *MTX,
                                            const CallEvent &Call,
@@ -171,6 +204,10 @@ ProgramStateRef MutexModeling::handleEvent(const EventDescriptor &Event,
                                            CheckerContext &C) const {
   assert(MTX && "should only be called with a mutex region");
 
+  State = State->add<MutexEvents>(
+      EventMarker{Event.Kind, Event.Semantics, Event.Library,
+                  Call.getCalleeIdentifier(), Call.getOriginExpr(), MTX});
+
   switch (Event.Kind) {
   case EventKind::Init:
     return handleInit(Event, MTX, Call, State, C);
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h
index 56e50bf4273efc..088d486fe48635 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h
@@ -47,6 +47,8 @@ enum class LockStateKind {
   UnlockedAndPossiblyDestroyed,
   Error_DoubleInit,
   Error_DoubleInitWhileLocked,
+  Error_DoubleLock,
+  Error_LockDestroyed
 };
 
 /// This class is intended for describing the list of events to detect.
diff --git a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index 63adf792eba04b..e1688a244f1645 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -71,8 +71,8 @@ struct LockState {
 };
 
 class PthreadLockChecker : public Checker<check::PostCall> {
-// , check::DeadSymbols,
-//                                          check::RegionChanges> {
+  // , check::DeadSymbols,
+  //                                          check::RegionChanges> {
 
 public:
   enum CheckerKind {
@@ -85,9 +85,7 @@ class PthreadLockChecker : public Checker<check::PostCall> {
   bool ChecksEnabled[CK_NumCheckKinds] = {false};
   CheckerNameRef CheckNames[CK_NumCheckKinds];
 
-  PthreadLockChecker() {
-    RegisterEvents();
-  }
+  PthreadLockChecker() { RegisterEvents(); }
 
 private:
   std::vector<EventDescriptor> EventsToModel{
@@ -106,10 +104,12 @@ class PthreadLockChecker : public Checker<check::PostCall> {
   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
   // void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
   // ProgramStateRef
-  // checkRegionChanges(ProgramStateRef State, const InvalidatedSymbols *Symbols,
+  // checkRegionChanges(ProgramStateRef State, const InvalidatedSymbols
+  // *Symbols,
   //                    ArrayRef<const MemRegion *> ExplicitRegions,
   //                    ArrayRef<const MemRegion *> Regions,
-  //                    const LocationContext *LCtx, const CallEvent *Call) const;
+  //                    const LocationContext *LCtx, const CallEvent *Call)
+  //                    const;
   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
                   const char *Sep) const override;
 
@@ -149,32 +149,20 @@ class PthreadLockChecker : public Checker<check::PostCall> {
       llvm_unreachable("Unknown locking library");
     }
   }
+
+  void checkInitEvent(const EventMarker &LastEvent, CheckerContext &C) const;
+  void checkAcquireEvent(const EventMarker &LastEvent, CheckerContext &C) const;
+  void checkTryAcquireEvent(const EventMarker &LastEvent,
+                            CheckerContext &C) const;
+  void checkReleaseEvent(const EventMarker &LastEvent, CheckerContext &C) const;
+  void checkDestroyEvent(const EventMarker &LastEvent, CheckerContext &C) const;
 };
 } // end anonymous namespace
 
-void PthreadLockChecker::checkPostCall(const CallEvent &Call,
-                                       CheckerContext &C) const {
-
+void PthreadLockChecker::checkInitEvent(const EventMarker &LastEvent,
+                                        CheckerContext &C) const {
   ProgramStateRef State = C.getState();
 
-  const auto &MTXEvents = State->get<MutexEvents>();
-
-  Call.dump(llvm::errs());
-  llvm::errs() << "before MTXEvents isEmpty check\n";
-
-  if (MTXEvents.isEmpty()) {
-    return;
-  }
-
-  llvm::errs() << "after MTXEvents isEmpty check\n";
-  printState(llvm::errs(), State, "\n", " ");
-
-  const auto &LastEvent = MTXEvents.getHead();
-
-  if (LastEvent.Kind != EventKind::Init) {
-    return;
-  }
-
   const LockStateKind *const LockState =
       State->get<LockStates>(LastEvent.MutexRegion);
 
@@ -203,7 +191,50 @@ void PthreadLockChecker::checkPostCall(const CallEvent &Call,
     return;
   }
 
-  reportBug(C, BT_initlock, LastEvent.EventExpr, detectCheckerKind(LastEvent), Message);
+  reportBug(C, BT_initlock, LastEvent.EventExpr, detectCheckerKind(LastEvent),
+            Message);
+}
+
+void PthreadLockChecker::checkAcquireEvent(const EventMarker &LastEvent,
+                                           CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+
+  const LockStateKind *const LockState =
+      State->get<LockStates>(LastEvent.MutexRegion);
+}
+
+void PthreadLockChecker::checkPostCall(const CallEvent &Call,
+                                       CheckerContext &C) const {
+
+  ProgramStateRef State = C.getState();
+
+  const auto &MTXEvents = State->get<MutexEvents>();
+
+  if (MTXEvents.isEmpty()) {
+    return;
+  }
+
+  const auto &LastEvent = MTXEvents.getHead();
+
+  switch (LastEvent.Kind) {
+  case EventKind::Init:
+    checkInitEvent(LastEvent, C);
+    break;
+  case EventKind::Acquire:
+    checkAcquireEvent(LastEvent, C);
+    break;
+  case EventKind::TryAcquire:
+    checkTryAcquireEvent(LastEvent, C);
+    break;
+  case EventKind::Release:
+    checkReleaseEvent(LastEvent, C);
+    break;
+  case EventKind::Destroy:
+    checkDestroyEvent(LastEvent, C);
+    break;
+  default:
+    llvm_unreachable("Unknown event kind");
+  }
 }
 
 void PthreadLockChecker::reportBug(CheckerContext &C,

>From 028485e4cb958a1bb9c87e997badb3d2cd80ecaf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Fri, 26 Jul 2024 12:06:19 +0200
Subject: [PATCH 11/30] add trylock stub

---
 .../StaticAnalyzer/Checkers/MutexModeling.cpp | 112 +++++++++++++-----
 1 file changed, 83 insertions(+), 29 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
index 319b3e39a8055c..c9bac9c6a814fc 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
@@ -127,6 +127,19 @@ class MutexModeling : public Checker<check::PostCall> {
   ProgramStateRef handleInit(const EventDescriptor &Event, const MemRegion *MTX,
                              const CallEvent &Call, ProgramStateRef State,
                              CheckerContext &C) const;
+  ProgramStateRef handleAcquire(const EventDescriptor &Event,
+                                const MemRegion *MTX, const CallEvent &Call,
+                                ProgramStateRef State, CheckerContext &C) const;
+  ProgramStateRef handleTryAcquire(const EventDescriptor &Event,
+                                   const MemRegion *MTX, const CallEvent &Call,
+                                   ProgramStateRef State,
+                                   CheckerContext &C) const;
+  ProgramStateRef handleRelease(const EventDescriptor &Event,
+                                const MemRegion *MTX, const CallEvent &Call,
+                                ProgramStateRef State, CheckerContext &C) const;
+  ProgramStateRef handleDestroy(const EventDescriptor &Event,
+                                const MemRegion *MTX, const CallEvent &Call,
+                                ProgramStateRef State, CheckerContext &C) const;
   ProgramStateRef handleEvent(const EventDescriptor &Event,
                               const MemRegion *MTX, const CallEvent &Call,
                               ProgramStateRef State, CheckerContext &C) const;
@@ -160,40 +173,83 @@ ProgramStateRef MutexModeling::handleInit(const EventDescriptor &Event,
   }
 }
 
-ProgramStateRef handleAcquire(const EventDescriptor &Event,
-                              const MemRegion *MTX, const CallEvent &Call,
-                              ProgramStateRef State, CheckerContext &C) const {
-  assert(MTX && "should only be called with a mutex region");
-
+static ProgramStateRef doAcquireCommonLogic(ProgramStateRef State,
+                                            const MemRegion *MTX) {
   const LockStateKind *LState = State->get<LockStates>(MTX);
 
   if (!LState)
-    return State->set<LockStates>(MTX, LockStateKind::Locked);
+    State = State->set<LockStates>(MTX, LockStateKind::Locked);
+  else if (*LState == LockStateKind::Locked)
+    State = State->set<LockStates>(MTX, LockStateKind::Error_DoubleLock);
+  if (*LState == LockStateKind::Destroyed)
+    State = State->set<LockStates>(MTX, LockStateKind::Error_LockDestroyed);
 
-  switch (*LState) {
-  case (LockStateKind::Locked): {
-    return State->set<LockStates>(MTX, LockStateKind::Error_DoubleLock);
-  }
-  case (LockStateKind::Destroyed): {
-    return State->set<LockStates>(MTX, LockStateKind::Error_LockDestroyed);
-  }
-  }
+  return State;
+}
+
+ProgramStateRef MutexModeling::handleAcquire(const EventDescriptor &Event,
+                                             const MemRegion *MTX,
+                                             const CallEvent &Call,
+                                             ProgramStateRef State,
+                                             CheckerContext &C) const {
 
-  if (Semantics == PthreadSemantics) {
+  State = doAcquireCommonLogic(State, MTX);
+
+  switch (Event.Semantics) {
+  case SemanticsKind::PthreadSemantics: {
     // Assume that the return value was 0.
     SVal RetVal = Call.getReturnValue();
     if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
       // FIXME: If the lock function was inlined and returned true,
       // we need to behave sanely - at least generate sink.
-      lockSucc = state->assume(*DefinedRetVal, false);
-      assert(lockSucc);
+      State = State->assume(*DefinedRetVal, false);
+      assert(State);
     }
+    break;
+  }
+  case SemanticsKind::XNUSemantics:
+    // XNU semantics return void on non-try locks.
+    break;
+  default:
+    llvm_unreachable(
+        "Acquire events should have either Pthread or XNU semantics");
+  }
+
+  return State;
+}
+
+ProgramStateRef MutexModeling::handleTryAcquire(const EventDescriptor &Event,
+                                                const MemRegion *MTX,
+                                                const CallEvent &Call,
+                                                ProgramStateRef State,
+                                                CheckerContext &C) const {
+
+  State = doAcquireCommonLogic(State, MTX);
+
+  // Bifurcate the state, and allow a mode where the lock acquisition fails.
+  ProgramStateRef LockSucc;
+  SVal RetVal = Call.getReturnValue();
+  if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
+    ProgramStateRef LockFail;
+    switch (Event.Semantics) {
+    case SemanticsKind::PthreadSemantics:
+      std::tie(LockFail, LockSucc) = State->assume(*DefinedRetVal);
+      break;
+    case SemanticsKind::XNUSemantics:
+      std::tie(LockSucc, LockFail) = State->assume(*DefinedRetVal);
+      break;
+    default:
+      llvm_unreachable("Unknown tryLock locking semantics");
+    }
+
+    // This is the bifurcation point in the ExplodedGraph, we do not need to
+    // pass the new ExplodedGraph node because we do not plan on building this
+    // failed case part forward in this checker.
+    C.addTransition(LockFail);
+
+    return LockSucc;
     // We might want to handle the case when the mutex lock function was inlined
     // and returned an Unknown or Undefined value.
-  } else {
-    // XNU locking semantics return void on non-try locks
-    assert((Semantics == XNUSemantics) && "Unknown locking semantics");
-    lockSucc = state;
   }
 }
 
@@ -212,22 +268,20 @@ ProgramStateRef MutexModeling::handleEvent(const EventDescriptor &Event,
   case EventKind::Init:
     return handleInit(Event, MTX, Call, State, C);
     break;
-  default:
-    llvm_unreachable("Unhandled event kind!");
-#if 0
   case EventKind::Acquire:
-    handleAcquire(Event, Call, C);
+    handleAcquire(Event, MTX, Call, State, C);
     break;
   case EventKind::TryAcquire:
-    handleTryAcquire(Event, Call, C);
+    handleTryAcquire(Event, MTX, Call, State, C);
     break;
   case EventKind::Release:
-    handleRelease(Event, Call, C);
+    handleRelease(Event, MTX, Call, State, C);
     break;
   case EventKind::Destroy:
-    handleDestroy(Event, Call, C);
+    handleDestroy(Event, MTX, Call, State, C);
     break;
-#endif
+  default:
+    llvm_unreachable("Unhandled event kind!");
   }
 }
 

>From 13e25c32212c7403f00164ff1402ce68d0dd5c71 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Fri, 26 Jul 2024 14:51:06 +0200
Subject: [PATCH 12/30] add release stub

---
 .../StaticAnalyzer/Checkers/MutexModeling.cpp | 41 ++++++++++++++++++-
 1 file changed, 40 insertions(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
index c9bac9c6a814fc..b1df525df13989 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
@@ -247,10 +247,49 @@ ProgramStateRef MutexModeling::handleTryAcquire(const EventDescriptor &Event,
     // failed case part forward in this checker.
     C.addTransition(LockFail);
 
-    return LockSucc;
+    // Pass the state where the locking succeeded onwards.
+    State = LockSucc;
     // We might want to handle the case when the mutex lock function was inlined
     // and returned an Unknown or Undefined value.
   }
+  return State;
+}
+
+ProgramStateRef MutexModeling::handleRelease(const EventDescriptor &Event,
+                                             const MemRegion *MTX,
+                                             const CallEvent &Call,
+                                             ProgramStateRef State,
+                                             CheckerContext &C) const {
+
+  const LockStateKind *LState = State->get<LockStates>(MTX);
+
+  if (!LState)
+    return State->set<LockStates>(MTX, LockStateKind::Unlocked);
+
+  if (*LState == LockStateKind::Unlocked)
+    return State->set<LockStates>(MTX, LockStateKind::Error_DoubleUnlock);
+
+  if (*LState == LockStateKind::Destroyed)
+    return State->set<LockStates>(MTX, LockStateKind::Error_UnlockDestroyed);
+
+  // Check if the currently released mutex is also the most recently locked one.
+  // If not, report a lock reversal bug.
+  // NOTE: MutexEvents stores events in reverse order as the ImmutableList data
+  // structure grows towards its Head element.
+  const auto &Events = State->get<MutexEvents>();
+  bool IsLockReversal = false;
+  for (const auto &Event : Events) {
+    if (Event.Kind == EventKind::Acquire) {
+      IsLockReversal = Event.MutexRegion != MTX;
+      break;
+    }
+  }
+
+  if (IsLockReversal) {
+    return State->set<LockStates>(MTX, LockStateKind::Error_LockReversal);
+  }
+
+  return State->set<LockStates>(MTX, LockStateKind::Unlocked);
 }
 
 ProgramStateRef MutexModeling::handleEvent(const EventDescriptor &Event,

>From 2e3f45b56d759e3cefd073ee0778d436308aeb6b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Mon, 29 Jul 2024 20:56:30 +0200
Subject: [PATCH 13/30] add all event kind checks, move mutexmodeling because
 of dependency issues

---
 .../clang/StaticAnalyzer/Checkers/Checkers.td |  11 +-
 .../StaticAnalyzer/Checkers/MutexModeling.cpp | 171 ++++++-
 .../MutexModeling/MutexModelingDomain.h       |   7 +-
 .../Checkers/PthreadLockChecker.cpp           | 464 +++---------------
 4 files changed, 227 insertions(+), 426 deletions(-)

diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index e504b08946242a..a3b3ec742e458b 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -261,6 +261,11 @@ def NonnullGlobalConstantsChecker: Checker<"NonnilStringConstants">,
 
 let ParentPackage = CoreAlpha in {
 
+def MutexModeling : Checker<"MutexModeling">,
+  HelpText<"Model mutexes lock and unlock events">,
+  Documentation<NotDocumented>,
+  Hidden;
+
 def BoolAssignmentChecker : Checker<"BoolAssignment">,
   HelpText<"Warn about assigning non-{0,1} values to Boolean variables">,
   Documentation<HasDocumentation>;
@@ -313,6 +318,7 @@ def StackAddrAsyncEscapeChecker : Checker<"StackAddressAsyncEscape">,
 
 def PthreadLockBase : Checker<"PthreadLockBase">,
   HelpText<"Helper registering multiple checks.">,
+  Dependencies<[MutexModeling]>,
   Documentation<NotDocumented>,
   Hidden;
 
@@ -509,11 +515,6 @@ def UnixAPIMisuseChecker : Checker<"API">,
   HelpText<"Check calls to various UNIX/Posix functions">,
   Documentation<HasDocumentation>;
 
-def MutexModeling : Checker<"MutexModeling">,
-  HelpText<"Model mutexes lock and unlock events">,
-  Documentation<NotDocumented>,
-  Hidden;
-
 def BlockInCriticalSectionChecker : Checker<"BlockInCriticalSection">,
   HelpText<"Check for calls to blocking functions inside a critical section">,
   Dependencies<[MutexModeling]>,
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
index b1df525df13989..2d7d18364a90f5 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
@@ -38,7 +38,7 @@ namespace {
 // successfully destroyed and it returns a non-zero value otherwise.
 ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef State,
                                               const MemRegion *LockReg,
-                                              const SymbolRef *Sym) {
+                                              const SymbolRef *LockReturnSym) {
   const LockStateKind *LState = State->get<LockStates>(LockReg);
   // Existence in DestroyRetVal ensures existence in LockMap.
   // Existence in Destroyed also ensures that the lock state for lockR is either
@@ -48,7 +48,7 @@ ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef State,
          *LState == LockStateKind::UnlockedAndPossiblyDestroyed);
 
   ConstraintManager &CMgr = State->getConstraintManager();
-  ConditionTruthVal RetZero = CMgr.isNull(State, *Sym);
+  ConditionTruthVal RetZero = CMgr.isNull(State, *LockReturnSym);
   if (RetZero.isConstrainedFalse()) {
     switch (*LState) {
     case LockStateKind::UntouchedAndPossiblyDestroyed: {
@@ -118,10 +118,19 @@ void updateCriticalSectionOnUnlock(const EventDescriptor &UnlockDescriptor,
   C.addTransition(State->set<CritSections>(NewList));
 }
 
-class MutexModeling : public Checker<check::PostCall> {
+class MutexModeling : public Checker<check::PostCall, check::DeadSymbols,
+                                     check::RegionChanges> {
 public:
   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
 
+  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+
+  ProgramStateRef
+  checkRegionChanges(ProgramStateRef State, const InvalidatedSymbols *Symbols,
+                     ArrayRef<const MemRegion *> ExplicitRegions,
+                     ArrayRef<const MemRegion *> Regions,
+                     const LocationContext *LCtx, const CallEvent *Call) const;
+
 private:
   mutable std::unique_ptr<BugType> BT_initlock;
   ProgramStateRef handleInit(const EventDescriptor &Event, const MemRegion *MTX,
@@ -178,13 +187,18 @@ static ProgramStateRef doAcquireCommonLogic(ProgramStateRef State,
   const LockStateKind *LState = State->get<LockStates>(MTX);
 
   if (!LState)
-    State = State->set<LockStates>(MTX, LockStateKind::Locked);
-  else if (*LState == LockStateKind::Locked)
-    State = State->set<LockStates>(MTX, LockStateKind::Error_DoubleLock);
-  if (*LState == LockStateKind::Destroyed)
-    State = State->set<LockStates>(MTX, LockStateKind::Error_LockDestroyed);
+    return State->set<LockStates>(MTX, LockStateKind::Locked);
 
-  return State;
+  switch (*LState) {
+  case LockStateKind::Unlocked:
+    return State->set<LockStates>(MTX, LockStateKind::Locked);
+  case LockStateKind::Locked:
+    return State->set<LockStates>(MTX, LockStateKind::Error_DoubleLock);
+  case LockStateKind::Destroyed:
+    return State->set<LockStates>(MTX, LockStateKind::Error_LockDestroyed);
+  default:
+    return State;
+  }
 }
 
 ProgramStateRef MutexModeling::handleAcquire(const EventDescriptor &Event,
@@ -249,8 +263,8 @@ ProgramStateRef MutexModeling::handleTryAcquire(const EventDescriptor &Event,
 
     // Pass the state where the locking succeeded onwards.
     State = LockSucc;
-    // We might want to handle the case when the mutex lock function was inlined
-    // and returned an Unknown or Undefined value.
+    // We might want to handle the case when the mutex lock function was
+    // inlined and returned an Unknown or Undefined value.
   }
   return State;
 }
@@ -272,10 +286,10 @@ ProgramStateRef MutexModeling::handleRelease(const EventDescriptor &Event,
   if (*LState == LockStateKind::Destroyed)
     return State->set<LockStates>(MTX, LockStateKind::Error_UnlockDestroyed);
 
-  // Check if the currently released mutex is also the most recently locked one.
-  // If not, report a lock reversal bug.
-  // NOTE: MutexEvents stores events in reverse order as the ImmutableList data
-  // structure grows towards its Head element.
+  // Check if the currently released mutex is also the most recently locked
+  // one. If not, report a lock reversal bug. NOTE: MutexEvents stores events
+  // in reverse order as the ImmutableList data structure grows towards its
+  // Head element.
   const auto &Events = State->get<MutexEvents>();
   bool IsLockReversal = false;
   for (const auto &Event : Events) {
@@ -292,6 +306,54 @@ ProgramStateRef MutexModeling::handleRelease(const EventDescriptor &Event,
   return State->set<LockStates>(MTX, LockStateKind::Unlocked);
 }
 
+ProgramStateRef MutexModeling::handleDestroy(const EventDescriptor &Event,
+                                             const MemRegion *MTX,
+                                             const CallEvent &Call,
+                                             ProgramStateRef State,
+                                             CheckerContext &C) const {
+
+  // TODO: Check if calling resolvePossiblyDestroyedMutex is necessary.
+  const SymbolRef *LockReturnSym = State->get<DestroyedRetVals>(MTX);
+  if (LockReturnSym)
+    State = resolvePossiblyDestroyedMutex(State, MTX, LockReturnSym);
+
+  const LockStateKind *LState = State->get<LockStates>(MTX);
+
+  if (!LState || *LState == LockStateKind::Unlocked) {
+    if (Event.Semantics != SemanticsKind::PthreadSemantics) {
+      return State->set<LockStates>(MTX, LockStateKind::Destroyed);
+    }
+
+    // In PthreadSemantics we reason about the return value of the destroy
+    // calls.
+    SymbolRef Sym = Call.getReturnValue().getAsSymbol();
+    if (!Sym) {
+      return State->remove<LockStates>(MTX);
+    }
+
+    State = State->set<DestroyedRetVals>(MTX, Sym);
+    return State->set<LockStates>(
+        MTX, LState && *LState == LockStateKind::Unlocked
+                 ? LockStateKind::UnlockedAndPossiblyDestroyed
+                 : LockStateKind::UntouchedAndPossiblyDestroyed);
+  }
+
+  if (*LState == LockStateKind::Locked) {
+    return State->set<LockStates>(MTX, LockStateKind::Error_DestroyLocked);
+  }
+
+  if (*LState == LockStateKind::Destroyed) {
+    return State->set<LockStates>(MTX, LockStateKind::Error_DoubleDestroy);
+  }
+
+  assert(LState && *LState != LockStateKind::Unlocked &&
+         *LState != LockStateKind::Locked &&
+         *LState != LockStateKind::Destroyed &&
+         "Only error states should remain if we get here");
+
+  return State;
+}
+
 ProgramStateRef MutexModeling::handleEvent(const EventDescriptor &Event,
                                            const MemRegion *MTX,
                                            const CallEvent &Call,
@@ -306,19 +368,14 @@ ProgramStateRef MutexModeling::handleEvent(const EventDescriptor &Event,
   switch (Event.Kind) {
   case EventKind::Init:
     return handleInit(Event, MTX, Call, State, C);
-    break;
   case EventKind::Acquire:
-    handleAcquire(Event, MTX, Call, State, C);
-    break;
+    return handleAcquire(Event, MTX, Call, State, C);
   case EventKind::TryAcquire:
-    handleTryAcquire(Event, MTX, Call, State, C);
-    break;
+    return handleTryAcquire(Event, MTX, Call, State, C);
   case EventKind::Release:
-    handleRelease(Event, MTX, Call, State, C);
-    break;
+    return handleRelease(Event, MTX, Call, State, C);
   case EventKind::Destroy:
-    handleDestroy(Event, MTX, Call, State, C);
-    break;
+    return handleDestroy(Event, MTX, Call, State, C);
   default:
     llvm_unreachable("Unhandled event kind!");
   }
@@ -328,7 +385,6 @@ void MutexModeling::checkPostCall(const CallEvent &Call,
                                   CheckerContext &C) const {
   // FIXME: Try to handle cases when the implementation was inlined rather
   // than just giving up.
-
   if (C.wasInlined)
     return;
 
@@ -345,6 +401,71 @@ void MutexModeling::checkPostCall(const CallEvent &Call,
   }
 }
 
+void MutexModeling::checkDeadSymbols(SymbolReaper &SymReaper,
+                                     CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+
+  for (auto I : State->get<DestroyedRetVals>()) {
+    // Once the return value symbol dies, no more checks can be performed
+    // against it. See if the return value was checked before this point.
+    // This would remove the symbol from the map as well.
+    if (SymReaper.isDead(I.second))
+      State = resolvePossiblyDestroyedMutex(State, I.first, &I.second);
+  }
+
+  for (auto I : State->get<LockStates>()) {
+    // Stop tracking dead mutex regions as well.
+    if (!SymReaper.isLiveRegion(I.first)) {
+      State = State->remove<LockStates>(I.first);
+      State = State->remove<DestroyedRetVals>(I.first);
+    }
+  }
+
+  // TODO: We probably need to clean up the lock stack as well.
+  // It is tricky though: even if the mutex cannot be unlocked anymore,
+  // it can still participate in lock order reversal resolution.
+
+  C.addTransition(State);
+}
+
+ProgramStateRef MutexModeling::checkRegionChanges(
+    ProgramStateRef State, const InvalidatedSymbols *Symbols,
+    ArrayRef<const MemRegion *> ExplicitRegions,
+    ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
+    const CallEvent *Call) const {
+
+  bool IsLibraryFunction = false;
+  if (Call && Call->isGlobalCFunction()) {
+    // Avoid invalidating mutex state when a known supported function is
+    // called.
+    for (auto &&Event : RegisteredEvents) {
+      if (matches(Event.Trigger, *Call)) {
+        return State;
+      }
+    }
+
+    if (Call->isInSystemHeader())
+      IsLibraryFunction = true;
+  }
+
+  for (auto R : Regions) {
+    // We assume that system library function wouldn't touch the mutex unless
+    // it takes the mutex explicitly as an argument.
+    // FIXME: This is a bit quadratic.
+    if (IsLibraryFunction && !llvm::is_contained(ExplicitRegions, R))
+      continue;
+
+    State = State->remove<LockStates>(R);
+    State = State->remove<DestroyedRetVals>(R);
+
+    // TODO: We need to invalidate the lock stack as well. This is tricky
+    // to implement correctly and efficiently though, because the effects
+    // of mutex escapes on lock order may be fairly varied.
+  }
+
+  return State;
+}
+
 namespace clang {
 namespace ento {
 // Checker registration
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h
index 088d486fe48635..9dcb3b967dd5ec 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h
@@ -48,7 +48,12 @@ enum class LockStateKind {
   Error_DoubleInit,
   Error_DoubleInitWhileLocked,
   Error_DoubleLock,
-  Error_LockDestroyed
+  Error_LockDestroyed,
+  Error_DoubleUnlock,
+  Error_UnlockDestroyed,
+  Error_LockReversal,
+  Error_DestroyLocked,
+  Error_DoubleDestroy
 };
 
 /// This class is intended for describing the list of events to detect.
diff --git a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index e1688a244f1645..6bcfafc8544bcf 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -32,44 +32,6 @@ using namespace mutex_modeling;
 
 namespace {
 
-struct LockState {
-  enum Kind {
-    Destroyed,
-    Locked,
-    Unlocked,
-    UntouchedAndPossiblyDestroyed,
-    UnlockedAndPossiblyDestroyed
-  } K;
-
-private:
-  LockState(Kind K) : K(K) {}
-
-public:
-  static LockState getLocked() { return LockState(Locked); }
-  static LockState getUnlocked() { return LockState(Unlocked); }
-  static LockState getDestroyed() { return LockState(Destroyed); }
-  static LockState getUntouchedAndPossiblyDestroyed() {
-    return LockState(UntouchedAndPossiblyDestroyed);
-  }
-  static LockState getUnlockedAndPossiblyDestroyed() {
-    return LockState(UnlockedAndPossiblyDestroyed);
-  }
-
-  bool operator==(const LockState &X) const { return K == X.K; }
-
-  bool isLocked() const { return K == Locked; }
-  bool isUnlocked() const { return K == Unlocked; }
-  bool isDestroyed() const { return K == Destroyed; }
-  bool isUntouchedAndPossiblyDestroyed() const {
-    return K == UntouchedAndPossiblyDestroyed;
-  }
-  bool isUnlockedAndPossiblyDestroyed() const {
-    return K == UnlockedAndPossiblyDestroyed;
-  }
-
-  void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
-};
-
 class PthreadLockChecker : public Checker<check::PostCall> {
   // , check::DeadSymbols,
   //                                          check::RegionChanges> {
@@ -90,7 +52,12 @@ class PthreadLockChecker : public Checker<check::PostCall> {
 private:
   std::vector<EventDescriptor> EventsToModel{
       EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_init"}, 2),
-                      EventKind::Init, LibraryKind::Pthread}};
+                      EventKind::Init, LibraryKind::Pthread},
+      EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_lock"}),
+                      EventKind::Acquire, LibraryKind::Pthread,
+                      SemanticsKind::PthreadSemantics},
+      EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_unlock"}),
+                      EventKind::Release, LibraryKind::Pthread}};
   void RegisterEvents() const {
     for (auto &&Event : EventsToModel) {
       RegisterEvent(Event);
@@ -152,8 +119,6 @@ class PthreadLockChecker : public Checker<check::PostCall> {
 
   void checkInitEvent(const EventMarker &LastEvent, CheckerContext &C) const;
   void checkAcquireEvent(const EventMarker &LastEvent, CheckerContext &C) const;
-  void checkTryAcquireEvent(const EventMarker &LastEvent,
-                            CheckerContext &C) const;
   void checkReleaseEvent(const EventMarker &LastEvent, CheckerContext &C) const;
   void checkDestroyEvent(const EventMarker &LastEvent, CheckerContext &C) const;
 };
@@ -166,41 +131,84 @@ void PthreadLockChecker::checkInitEvent(const EventMarker &LastEvent,
   const LockStateKind *const LockState =
       State->get<LockStates>(LastEvent.MutexRegion);
 
-  if (!LockState || *LockState == LockStateKind::Destroyed) {
+  if (!LockState) {
     return;
   }
 
-  bool IsError = false;
-  StringRef Message;
-  switch (*LockState) {
-  case LockStateKind::Error_DoubleInit: {
-    IsError = true;
-    Message = "This lock has already been initialized";
-    break;
+  if (*LockState == LockStateKind::Error_DoubleInit) {
+    reportBug(C, BT_initlock, LastEvent.EventExpr, detectCheckerKind(LastEvent),
+              "This lock has already been initialized");
+  } else if (*LockState == LockStateKind::Error_DoubleInitWhileLocked) {
+    reportBug(C, BT_initlock, LastEvent.EventExpr, detectCheckerKind(LastEvent),
+              "This lock is still being held");
   }
-  case LockStateKind::Error_DoubleInitWhileLocked: {
-    IsError = true;
-    Message = "This lock is still being held";
-    break;
-  }
-  default: {
+}
+
+void PthreadLockChecker::checkAcquireEvent(const EventMarker &LastEvent,
+                                           CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+
+  const LockStateKind *const LockState =
+      State->get<LockStates>(LastEvent.MutexRegion);
+
+  if (!LockState) {
+    return;
   }
+
+  if (*LockState == LockStateKind::Error_DoubleLock) {
+    reportBug(C, BT_doublelock, LastEvent.EventExpr,
+              detectCheckerKind(LastEvent),
+              "This lock has already been acquired");
+  } else if (*LockState == LockStateKind::Error_LockDestroyed) {
+    reportBug(C, BT_destroylock, LastEvent.EventExpr,
+              detectCheckerKind(LastEvent),
+              "This lock has already been destroyed");
   }
+}
 
-  if (!IsError) {
+void PthreadLockChecker::checkReleaseEvent(const EventMarker &LastEvent,
+                                           CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+
+  const LockStateKind *const LockState =
+      State->get<LockStates>(LastEvent.MutexRegion);
+
+  if (!LockState) {
     return;
   }
 
-  reportBug(C, BT_initlock, LastEvent.EventExpr, detectCheckerKind(LastEvent),
-            Message);
+  if (*LockState == LockStateKind::Error_DoubleUnlock) {
+    reportBug(C, BT_doubleunlock, LastEvent.EventExpr,
+              detectCheckerKind(LastEvent),
+              "This lock has already been released");
+  } else if (*LockState == LockStateKind::Error_UnlockDestroyed) {
+    reportBug(C, BT_destroylock, LastEvent.EventExpr,
+              detectCheckerKind(LastEvent),
+              "This lock has already been destroyed");
+  } else if (*LockState == LockStateKind::Error_LockReversal) {
+    reportBug(C, BT_lor, LastEvent.EventExpr, detectCheckerKind(LastEvent),
+              "Lock order reversal");
+  }
 }
 
-void PthreadLockChecker::checkAcquireEvent(const EventMarker &LastEvent,
+void PthreadLockChecker::checkDestroyEvent(const EventMarker &LastEvent,
                                            CheckerContext &C) const {
   ProgramStateRef State = C.getState();
 
-  const LockStateKind *const LockState =
+  const LockStateKind *const LState =
       State->get<LockStates>(LastEvent.MutexRegion);
+
+  if (!LState || *LState == LockStateKind::Destroyed) {
+    return;
+  }
+
+  if (*LState == LockStateKind::Error_DestroyLocked) {
+    reportBug(C, BT_destroylock, LastEvent.EventExpr,
+              detectCheckerKind(LastEvent), "This lock is still locked");
+  } else if (*LState == LockStateKind::Error_DoubleDestroy)
+    reportBug(C, BT_destroylock, LastEvent.EventExpr,
+              detectCheckerKind(LastEvent),
+              "This lock has already been destroyed");
 }
 
 void PthreadLockChecker::checkPostCall(const CallEvent &Call,
@@ -221,10 +229,8 @@ void PthreadLockChecker::checkPostCall(const CallEvent &Call,
     checkInitEvent(LastEvent, C);
     break;
   case EventKind::Acquire:
-    checkAcquireEvent(LastEvent, C);
-    break;
   case EventKind::TryAcquire:
-    checkTryAcquireEvent(LastEvent, C);
+    checkAcquireEvent(LastEvent, C);
     break;
   case EventKind::Release:
     checkReleaseEvent(LastEvent, C);
@@ -256,338 +262,6 @@ void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State,
   mutex_modeling::printState(Out, State, NL, Sep);
 }
 
-#if 0
-void PthreadLockChecker::AcquirePthreadLock(const CallEvent &Call,
-                                            CheckerContext &C,
-                                            CheckerKind CheckKind) const {
-  AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false,
-                 PthreadSemantics, CheckKind);
-}
-
-void PthreadLockChecker::AcquireXNULock(const CallEvent &Call,
-                                        CheckerContext &C,
-                                        CheckerKind CheckKind) const {
-  AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false,
-                 XNUSemantics, CheckKind);
-}
-
-void PthreadLockChecker::TryPthreadLock(const CallEvent &Call,
-                                        CheckerContext &C,
-                                        CheckerKind CheckKind) const {
-  AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
-                 PthreadSemantics, CheckKind);
-}
-
-void PthreadLockChecker::TryXNULock(const CallEvent &Call, CheckerContext &C,
-                                    CheckerKind CheckKind) const {
-  AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
-                 PthreadSemantics, CheckKind);
-}
-
-void PthreadLockChecker::TryFuchsiaLock(const CallEvent &Call,
-                                        CheckerContext &C,
-                                        CheckerKind CheckKind) const {
-  AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
-                 PthreadSemantics, CheckKind);
-}
-
-void PthreadLockChecker::TryC11Lock(const CallEvent &Call, CheckerContext &C,
-                                    CheckerKind CheckKind) const {
-  AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
-                 PthreadSemantics, CheckKind);
-}
-
-void PthreadLockChecker::AcquireLockAux(const CallEvent &Call,
-                                        CheckerContext &C, const Expr *MtxExpr,
-                                        SVal MtxVal, bool IsTryLock,
-                                        enum LockingSemantics Semantics,
-                                        CheckerKind CheckKind) const {
-  if (!ChecksEnabled[CheckKind])
-    return;
-
-  const MemRegion *lockR = MtxVal.getAsRegion();
-  if (!lockR)
-    return;
-
-  ProgramStateRef state = C.getState();
-  const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
-  if (sym)
-    state = resolvePossiblyDestroyedMutex(state, lockR, sym);
-
-  if (const LockState *LState = state->get<LockMap>(lockR)) {
-    if (LState->isLocked()) {
-      reportBug(C, BT_doublelock, MtxExpr, CheckKind,
-                "This lock has already been acquired");
-      return;
-    } else if (LState->isDestroyed()) {
-      reportBug(C, BT_destroylock, MtxExpr, CheckKind,
-                "This lock has already been destroyed");
-      return;
-    }
-  }
-
-  ProgramStateRef lockSucc = state;
-  if (IsTryLock) {
-    // Bifurcate the state, and allow a mode where the lock acquisition fails.
-    SVal RetVal = Call.getReturnValue();
-    if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
-      ProgramStateRef lockFail;
-      switch (Semantics) {
-      case PthreadSemantics:
-        std::tie(lockFail, lockSucc) = state->assume(*DefinedRetVal);
-        break;
-      case XNUSemantics:
-        std::tie(lockSucc, lockFail) = state->assume(*DefinedRetVal);
-        break;
-      default:
-        llvm_unreachable("Unknown tryLock locking semantics");
-      }
-      assert(lockFail && lockSucc);
-      C.addTransition(lockFail);
-    }
-    // We might want to handle the case when the mutex lock function was inlined
-    // and returned an Unknown or Undefined value.
-  } else if (Semantics == PthreadSemantics) {
-    // Assume that the return value was 0.
-    SVal RetVal = Call.getReturnValue();
-    if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
-      // FIXME: If the lock function was inlined and returned true,
-      // we need to behave sanely - at least generate sink.
-      lockSucc = state->assume(*DefinedRetVal, false);
-      assert(lockSucc);
-    }
-    // We might want to handle the case when the mutex lock function was inlined
-    // and returned an Unknown or Undefined value.
-  } else {
-    // XNU locking semantics return void on non-try locks
-    assert((Semantics == XNUSemantics) && "Unknown locking semantics");
-    lockSucc = state;
-  }
-
-  // Record that the lock was acquired.
-  lockSucc = lockSucc->add<LockSet>(lockR);
-  lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked());
-  C.addTransition(lockSucc);
-}
-
-void PthreadLockChecker::ReleaseAnyLock(const CallEvent &Call,
-                                        CheckerContext &C,
-                                        CheckerKind CheckKind) const {
-  ReleaseLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), CheckKind);
-}
-
-void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call,
-                                        CheckerContext &C, const Expr *MtxExpr,
-                                        SVal MtxVal,
-                                        CheckerKind CheckKind) const {
-  if (!ChecksEnabled[CheckKind])
-    return;
-
-  const MemRegion *lockR = MtxVal.getAsRegion();
-  if (!lockR)
-    return;
-
-  ProgramStateRef state = C.getState();
-  const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
-  if (sym)
-    state = resolvePossiblyDestroyedMutex(state, lockR, sym);
-
-  if (const LockState *LState = state->get<LockMap>(lockR)) {
-    if (LState->isUnlocked()) {
-      reportBug(C, BT_doubleunlock, MtxExpr, CheckKind,
-                "This lock has already been unlocked");
-      return;
-    } else if (LState->isDestroyed()) {
-      reportBug(C, BT_destroylock, MtxExpr, CheckKind,
-                "This lock has already been destroyed");
-      return;
-    }
-  }
-
-  LockSetTy LS = state->get<LockSet>();
-
-  if (!LS.isEmpty()) {
-    const MemRegion *firstLockR = LS.getHead();
-    if (firstLockR != lockR) {
-      reportBug(C, BT_lor, MtxExpr, CheckKind,
-                "This was not the most recently acquired lock. Possible lock "
-                "order reversal");
-      return;
-    }
-    // Record that the lock was released.
-    state = state->set<LockSet>(LS.getTail());
-  }
-
-  state = state->set<LockMap>(lockR, LockState::getUnlocked());
-  C.addTransition(state);
-}
-
-void PthreadLockChecker::DestroyPthreadLock(const CallEvent &Call,
-                                            CheckerContext &C,
-                                            CheckerKind CheckKind) const {
-  DestroyLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0),
-                 PthreadSemantics, CheckKind);
-}
-
-void PthreadLockChecker::DestroyXNULock(const CallEvent &Call,
-                                        CheckerContext &C,
-                                        CheckerKind CheckKind) const {
-  DestroyLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), XNUSemantics,
-                 CheckKind);
-}
-
-void PthreadLockChecker::DestroyLockAux(const CallEvent &Call,
-                                        CheckerContext &C, const Expr *MtxExpr,
-                                        SVal MtxVal,
-                                        enum LockingSemantics Semantics,
-                                        CheckerKind CheckKind) const {
-  if (!ChecksEnabled[CheckKind])
-    return;
-
-  const MemRegion *LockR = MtxVal.getAsRegion();
-  if (!LockR)
-    return;
-
-  ProgramStateRef State = C.getState();
-
-  const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
-  if (sym)
-    State = resolvePossiblyDestroyedMutex(State, LockR, sym);
-
-  const LockState *LState = State->get<LockMap>(LockR);
-  // Checking the return value of the destroy method only in the case of
-  // PthreadSemantics
-  if (Semantics == PthreadSemantics) {
-    if (!LState || LState->isUnlocked()) {
-      SymbolRef sym = Call.getReturnValue().getAsSymbol();
-      if (!sym) {
-        State = State->remove<LockMap>(LockR);
-        C.addTransition(State);
-        return;
-      }
-      State = State->set<DestroyRetVal>(LockR, sym);
-      if (LState && LState->isUnlocked())
-        State = State->set<LockMap>(
-            LockR, LockState::getUnlockedAndPossiblyDestroyed());
-      else
-        State = State->set<LockMap>(
-            LockR, LockState::getUntouchedAndPossiblyDestroyed());
-      C.addTransition(State);
-      return;
-    }
-  } else {
-    if (!LState || LState->isUnlocked()) {
-      State = State->set<LockMap>(LockR, LockState::getDestroyed());
-      C.addTransition(State);
-      return;
-    }
-  }
-
-  StringRef Message = LState->isLocked()
-                          ? "This lock is still locked"
-                          : "This lock has already been destroyed";
-
-  reportBug(C, BT_destroylock, MtxExpr, CheckKind, Message);
-}
-
-void PthreadLockChecker::InitAnyLock(const CallEvent &Call, CheckerContext &C,
-                                     CheckerKind CheckKind) const {
-  InitLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), CheckKind);
-}
-
-void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C,
-                                     const Expr *MtxExpr, SVal MtxVal,
-                                     CheckerKind CheckKind) const {
-  if (!ChecksEnabled[CheckKind])
-    return;
-
-  const MemRegion *LockR = MtxVal.getAsRegion();
-  if (!LockR)
-    return;
-
-  ProgramStateRef State = C.getState();
-
-  const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
-  if (sym)
-    State = resolvePossiblyDestroyedMutex(State, LockR, sym);
-
-  const struct LockState *LState = State->get<LockMap>(LockR);
-  if (!LState || LState->isDestroyed()) {
-    State = State->set<LockMap>(LockR, LockState::getUnlocked());
-    C.addTransition(State);
-    return;
-  }
-
-  StringRef Message = LState->isLocked()
-                          ? "This lock is still being held"
-                          : "This lock has already been initialized";
-
-  reportBug(C, BT_initlock, MtxExpr, CheckKind, Message);
-}
-
-void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper,
-                                          CheckerContext &C) const {
-  ProgramStateRef State = C.getState();
-
-  for (auto I : State->get<DestroyRetVal>()) {
-    // Once the return value symbol dies, no more checks can be performed
-    // against it. See if the return value was checked before this point.
-    // This would remove the symbol from the map as well.
-    if (SymReaper.isDead(I.second))
-      State = resolvePossiblyDestroyedMutex(State, I.first, &I.second);
-  }
-
-  for (auto I : State->get<LockMap>()) {
-    // Stop tracking dead mutex regions as well.
-    if (!SymReaper.isLiveRegion(I.first)) {
-      State = State->remove<LockMap>(I.first);
-      State = State->remove<DestroyRetVal>(I.first);
-    }
-  }
-
-  // TODO: We probably need to clean up the lock stack as well.
-  // It is tricky though: even if the mutex cannot be unlocked anymore,
-  // it can still participate in lock order reversal resolution.
-
-  C.addTransition(State);
-}
-
-ProgramStateRef PthreadLockChecker::checkRegionChanges(
-    ProgramStateRef State, const InvalidatedSymbols *Symbols,
-    ArrayRef<const MemRegion *> ExplicitRegions,
-    ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
-    const CallEvent *Call) const {
-
-  bool IsLibraryFunction = false;
-  if (Call && Call->isGlobalCFunction()) {
-    // Avoid invalidating mutex state when a known supported function is called.
-    if (PThreadCallbacks.lookup(*Call) || FuchsiaCallbacks.lookup(*Call) ||
-        C11Callbacks.lookup(*Call))
-      return State;
-
-    if (Call->isInSystemHeader())
-      IsLibraryFunction = true;
-  }
-
-  for (auto R : Regions) {
-    // We assume that system library function wouldn't touch the mutex unless
-    // it takes the mutex explicitly as an argument.
-    // FIXME: This is a bit quadratic.
-    if (IsLibraryFunction && !llvm::is_contained(ExplicitRegions, R))
-      continue;
-
-    State = State->remove<LockMap>(R);
-    State = State->remove<DestroyRetVal>(R);
-
-    // TODO: We need to invalidate the lock stack as well. This is tricky
-    // to implement correctly and efficiently though, because the effects
-    // of mutex escapes on lock order may be fairly varied.
-  }
-
-  return State;
-}
-#endif
-
 void ento::registerPthreadLockBase(CheckerManager &mgr) {
   mgr.registerChecker<PthreadLockChecker>();
 }

>From 148dbfa46f7e4e4abbdcc86dd807b491a7806125 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 Jul 2024 17:12:53 +0200
Subject: [PATCH 14/30] reword variables, move initialization of descriptors

---
 .../StaticAnalyzer/Checkers/MutexModeling.cpp | 93 +++++++++++-------
 .../Checkers/PthreadLockChecker.cpp           | 97 +++++++++++++------
 2 files changed, 125 insertions(+), 65 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
index 2d7d18364a90f5..e553234e6b73f3 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
@@ -39,18 +39,18 @@ namespace {
 ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef State,
                                               const MemRegion *LockReg,
                                               const SymbolRef *LockReturnSym) {
-  const LockStateKind *LState = State->get<LockStates>(LockReg);
+  const LockStateKind *LockState = State->get<LockStates>(LockReg);
   // Existence in DestroyRetVal ensures existence in LockMap.
   // Existence in Destroyed also ensures that the lock state for lockR is either
   // UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed.
-  assert(LState);
-  assert(*LState == LockStateKind::UntouchedAndPossiblyDestroyed ||
-         *LState == LockStateKind::UnlockedAndPossiblyDestroyed);
+  assert(LockState);
+  assert(*LockState == LockStateKind::UntouchedAndPossiblyDestroyed ||
+         *LockState == LockStateKind::UnlockedAndPossiblyDestroyed);
 
   ConstraintManager &CMgr = State->getConstraintManager();
   ConditionTruthVal RetZero = CMgr.isNull(State, *LockReturnSym);
   if (RetZero.isConstrainedFalse()) {
-    switch (*LState) {
+    switch (*LockState) {
     case LockStateKind::UntouchedAndPossiblyDestroyed: {
       State = State->remove<LockStates>(LockReg);
       break;
@@ -163,12 +163,12 @@ ProgramStateRef MutexModeling::handleInit(const EventDescriptor &Event,
                                           CheckerContext &C) const {
   assert(MTX && "should only be called with a mutex region");
 
-  const LockStateKind *LState = State->get<LockStates>(MTX);
+  const LockStateKind *LockState = State->get<LockStates>(MTX);
 
-  if (!LState)
+  if (!LockState)
     return State->set<LockStates>(MTX, LockStateKind::Unlocked);
 
-  switch (*LState) {
+  switch (*LockState) {
   case (LockStateKind::Destroyed): {
     return State->set<LockStates>(MTX, LockStateKind::Unlocked);
   }
@@ -184,12 +184,12 @@ ProgramStateRef MutexModeling::handleInit(const EventDescriptor &Event,
 
 static ProgramStateRef doAcquireCommonLogic(ProgramStateRef State,
                                             const MemRegion *MTX) {
-  const LockStateKind *LState = State->get<LockStates>(MTX);
+  const LockStateKind *LockState = State->get<LockStates>(MTX);
 
-  if (!LState)
+  if (!LockState)
     return State->set<LockStates>(MTX, LockStateKind::Locked);
 
-  switch (*LState) {
+  switch (*LockState) {
   case LockStateKind::Unlocked:
     return State->set<LockStates>(MTX, LockStateKind::Locked);
   case LockStateKind::Locked:
@@ -275,31 +275,53 @@ ProgramStateRef MutexModeling::handleRelease(const EventDescriptor &Event,
                                              ProgramStateRef State,
                                              CheckerContext &C) const {
 
-  const LockStateKind *LState = State->get<LockStates>(MTX);
+  const LockStateKind *LockState = State->get<LockStates>(MTX);
 
-  if (!LState)
-    return State->set<LockStates>(MTX, LockStateKind::Unlocked);
+  // FIXME: to be more conservative we shoud maybe just assume, that the lock
+  // is was in a locked state? This would assume that before we start modeling
+  // the mutex, the state was correct.
+  // return State->set<LockStates>(MTX, LockStateKind::Unlocked);
+  if (!LockState)
+    return State->set<LockStates>(MTX, LockStateKind::Error_DoubleUnlock);
 
-  if (*LState == LockStateKind::Unlocked)
+  if (*LockState == LockStateKind::Unlocked)
     return State->set<LockStates>(MTX, LockStateKind::Error_DoubleUnlock);
 
-  if (*LState == LockStateKind::Destroyed)
+  if (*LockState == LockStateKind::Destroyed)
     return State->set<LockStates>(MTX, LockStateKind::Error_UnlockDestroyed);
 
   // Check if the currently released mutex is also the most recently locked
-  // one. If not, report a lock reversal bug. NOTE: MutexEvents stores events
-  // in reverse order as the ImmutableList data structure grows towards its
-  // Head element.
+  // one. If not, report a lock reversal bug.
+  //
+  // NOTE: MutexEvents stores events in reverse order as the ImmutableList data
+  // structure grows towards its Head element.
+  //
+  // Every time a Release event is met in the reverse order of Mutex events it
+  // is pushed to a stack. Every time an Acquire-like event is met, if the top
+  // of the stack matches the Acquire event's mutex region, it is popped
+  // (signifying that the order of lock/unlocks was matching), but if a
+  // non-matching item is found, we are sure that there is a reversal.
+  //
+  // FIXME: A stack of mutexes could be used instead of computing the reverse
+  // critical sections every time, use the critical section modeling aggregated
+  // data for this check, and rewrite the comment paragraph above.
   const auto &Events = State->get<MutexEvents>();
-  bool IsLockReversal = false;
+  llvm::SmallVector<const EventMarker *, 4> InverseLockStack;
   for (const auto &Event : Events) {
-    if (Event.Kind == EventKind::Acquire) {
-      IsLockReversal = Event.MutexRegion != MTX;
-      break;
+    if (Event.Kind == EventKind::Release) {
+      InverseLockStack.push_back(&Event);
+    } else if (Event.Kind == EventKind::Acquire ||
+               Event.Kind == EventKind::TryAcquire) {
+      if (InverseLockStack.empty()) {
+        continue;
+      }
+      if (InverseLockStack.back()->MutexRegion == Event.MutexRegion) {
+        InverseLockStack.pop_back();
+      }
     }
   }
 
-  if (IsLockReversal) {
+  if (!InverseLockStack.empty()) {
     return State->set<LockStates>(MTX, LockStateKind::Error_LockReversal);
   }
 
@@ -317,9 +339,9 @@ ProgramStateRef MutexModeling::handleDestroy(const EventDescriptor &Event,
   if (LockReturnSym)
     State = resolvePossiblyDestroyedMutex(State, MTX, LockReturnSym);
 
-  const LockStateKind *LState = State->get<LockStates>(MTX);
+  const LockStateKind *LockState = State->get<LockStates>(MTX);
 
-  if (!LState || *LState == LockStateKind::Unlocked) {
+  if (!LockState || *LockState == LockStateKind::Unlocked) {
     if (Event.Semantics != SemanticsKind::PthreadSemantics) {
       return State->set<LockStates>(MTX, LockStateKind::Destroyed);
     }
@@ -333,22 +355,22 @@ ProgramStateRef MutexModeling::handleDestroy(const EventDescriptor &Event,
 
     State = State->set<DestroyedRetVals>(MTX, Sym);
     return State->set<LockStates>(
-        MTX, LState && *LState == LockStateKind::Unlocked
+        MTX, LockState && *LockState == LockStateKind::Unlocked
                  ? LockStateKind::UnlockedAndPossiblyDestroyed
                  : LockStateKind::UntouchedAndPossiblyDestroyed);
   }
 
-  if (*LState == LockStateKind::Locked) {
+  if (*LockState == LockStateKind::Locked) {
     return State->set<LockStates>(MTX, LockStateKind::Error_DestroyLocked);
   }
 
-  if (*LState == LockStateKind::Destroyed) {
+  if (*LockState == LockStateKind::Destroyed) {
     return State->set<LockStates>(MTX, LockStateKind::Error_DoubleDestroy);
   }
 
-  assert(LState && *LState != LockStateKind::Unlocked &&
-         *LState != LockStateKind::Locked &&
-         *LState != LockStateKind::Destroyed &&
+  assert(LockState && *LockState != LockStateKind::Unlocked &&
+         *LockState != LockStateKind::Locked &&
+         *LockState != LockStateKind::Destroyed &&
          "Only error states should remain if we get here");
 
   return State;
@@ -469,11 +491,8 @@ ProgramStateRef MutexModeling::checkRegionChanges(
 namespace clang {
 namespace ento {
 // Checker registration
-void registerMutexModeling(CheckerManager &mgr) {
-  mgr.registerChecker<MutexModeling>();
-  RegisterEvent(
-      EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_init"}, 2),
-                      EventKind::Init, LibraryKind::Pthread});
+void registerMutexModeling(CheckerManager &CM) {
+  CM.registerChecker<MutexModeling>();
 }
 bool shouldRegisterMutexModeling(const CheckerManager &) { return true; }
 } // namespace ento
diff --git a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index 6bcfafc8544bcf..325b0c168c9196 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -33,8 +33,6 @@ using namespace mutex_modeling;
 namespace {
 
 class PthreadLockChecker : public Checker<check::PostCall> {
-  // , check::DeadSymbols,
-  //                                          check::RegionChanges> {
 
 public:
   enum CheckerKind {
@@ -50,14 +48,7 @@ class PthreadLockChecker : public Checker<check::PostCall> {
   PthreadLockChecker() { RegisterEvents(); }
 
 private:
-  std::vector<EventDescriptor> EventsToModel{
-      EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_init"}, 2),
-                      EventKind::Init, LibraryKind::Pthread},
-      EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_lock"}),
-                      EventKind::Acquire, LibraryKind::Pthread,
-                      SemanticsKind::PthreadSemantics},
-      EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_unlock"}),
-                      EventKind::Release, LibraryKind::Pthread}};
+  std::vector<EventDescriptor> EventsToModel{};
   void RegisterEvents() const {
     for (auto &&Event : EventsToModel) {
       RegisterEvent(Event);
@@ -180,14 +171,14 @@ void PthreadLockChecker::checkReleaseEvent(const EventMarker &LastEvent,
   if (*LockState == LockStateKind::Error_DoubleUnlock) {
     reportBug(C, BT_doubleunlock, LastEvent.EventExpr,
               detectCheckerKind(LastEvent),
-              "This lock has already been released");
+              "This lock has already been unlocked");
   } else if (*LockState == LockStateKind::Error_UnlockDestroyed) {
     reportBug(C, BT_destroylock, LastEvent.EventExpr,
               detectCheckerKind(LastEvent),
               "This lock has already been destroyed");
   } else if (*LockState == LockStateKind::Error_LockReversal) {
     reportBug(C, BT_lor, LastEvent.EventExpr, detectCheckerKind(LastEvent),
-              "Lock order reversal");
+              "This was not the most recently acquired lock");
   }
 }
 
@@ -195,17 +186,17 @@ void PthreadLockChecker::checkDestroyEvent(const EventMarker &LastEvent,
                                            CheckerContext &C) const {
   ProgramStateRef State = C.getState();
 
-  const LockStateKind *const LState =
+  const LockStateKind *const LockState =
       State->get<LockStates>(LastEvent.MutexRegion);
 
-  if (!LState || *LState == LockStateKind::Destroyed) {
+  if (!LockState || *LockState == LockStateKind::Destroyed) {
     return;
   }
 
-  if (*LState == LockStateKind::Error_DestroyLocked) {
+  if (*LockState == LockStateKind::Error_DestroyLocked) {
     reportBug(C, BT_destroylock, LastEvent.EventExpr,
               detectCheckerKind(LastEvent), "This lock is still locked");
-  } else if (*LState == LockStateKind::Error_DoubleDestroy)
+  } else if (*LockState == LockStateKind::Error_DoubleDestroy)
     reportBug(C, BT_destroylock, LastEvent.EventExpr,
               detectCheckerKind(LastEvent),
               "This lock has already been destroyed");
@@ -224,6 +215,9 @@ void PthreadLockChecker::checkPostCall(const CallEvent &Call,
 
   const auto &LastEvent = MTXEvents.getHead();
 
+  if (!ChecksEnabled[detectCheckerKind(LastEvent)])
+    return;
+
   switch (LastEvent.Kind) {
   case EventKind::Init:
     checkInitEvent(LastEvent, C);
@@ -270,15 +264,62 @@ bool ento::shouldRegisterPthreadLockBase(const CheckerManager &mgr) {
   return true;
 }
 
-#define REGISTER_CHECKER(name, library)                                        \
-  void ento::register##name(CheckerManager &mgr) {                             \
-    PthreadLockChecker *checker = mgr.getChecker<PthreadLockChecker>();        \
-    checker->ChecksEnabled[PthreadLockChecker::CK_##name] = true;              \
-    checker->CheckNames[PthreadLockChecker::CK_##name] =                       \
-        mgr.getCurrentCheckerName();                                           \
-  }                                                                            \
-  bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
-
-REGISTER_CHECKER(PthreadLockChecker, LibraryKind::Pthread)
-REGISTER_CHECKER(FuchsiaLockChecker, LibraryKind::Fuchsia)
-REGISTER_CHECKER(C11LockChecker, LibraryKind::C11)
+void ento::registerPthreadLockChecker(CheckerManager &CM) {
+  PthreadLockChecker *ImplChecker = CM.getChecker<PthreadLockChecker>();
+  ImplChecker->ChecksEnabled[PthreadLockChecker::CK_PthreadLockChecker] = true;
+  ImplChecker->CheckNames[PthreadLockChecker::CK_PthreadLockChecker] =
+      CM.getCurrentCheckerName();
+
+  RegisterEvent(
+      EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_init"}, 2),
+                      EventKind::Init, LibraryKind::Pthread});
+  RegisterEvent(
+      EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_init"}, 2),
+                      EventKind::Init, LibraryKind::Pthread});
+  RegisterEvent(EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_lock"}),
+                                EventKind::Acquire, LibraryKind::Pthread,
+                                SemanticsKind::PthreadSemantics});
+  RegisterEvent(EventDescriptor{
+      MakeFirstArgExtractor({"pthread_mutex_trylock"}), EventKind::TryAcquire,
+      LibraryKind::Pthread, SemanticsKind::PthreadSemantics});
+  RegisterEvent(EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_unlock"}),
+                                EventKind::Release, LibraryKind::Pthread});
+  RegisterEvent(EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_destroy"}),
+                                EventKind::Destroy, LibraryKind::Pthread});
+}
+
+bool ento::shouldRegisterPthreadLockChecker(const CheckerManager &CM) {
+  return true;
+}
+
+void ento::registerFuchsiaLockChecker(CheckerManager &CM) {
+  PthreadLockChecker *ImplChecker = CM.getChecker<PthreadLockChecker>();
+  ImplChecker->ChecksEnabled[PthreadLockChecker::CK_FuchsiaLockChecker] = true;
+  ImplChecker->CheckNames[PthreadLockChecker::CK_FuchsiaLockChecker] =
+      CM.getCurrentCheckerName();
+}
+
+bool ento::shouldRegisterFuchsiaLockChecker(const CheckerManager &CM) {
+  return true;
+}
+
+void ento::registerC11LockChecker(CheckerManager &CM) {
+  PthreadLockChecker *ImplChecker = CM.getChecker<PthreadLockChecker>();
+  ImplChecker->ChecksEnabled[PthreadLockChecker::CK_C11LockChecker] = true;
+  ImplChecker->CheckNames[PthreadLockChecker::CK_C11LockChecker] =
+      CM.getCurrentCheckerName();
+
+  RegisterEvent(EventDescriptor{MakeFirstArgExtractor({"lck_mtx_lock"}),
+                                EventKind::Acquire, LibraryKind::C11,
+                                SemanticsKind::XNUSemantics});
+  RegisterEvent(EventDescriptor{MakeFirstArgExtractor({"lck_mtx_try_lock"}),
+                                EventKind::TryAcquire, LibraryKind::C11,
+                                SemanticsKind::XNUSemantics});
+  RegisterEvent(EventDescriptor{MakeFirstArgExtractor({"lck_mtx_unlock"}),
+                                EventKind::Release, LibraryKind::C11,
+                                SemanticsKind::XNUSemantics});
+}
+
+bool ento::shouldRegisterC11LockChecker(const CheckerManager &CM) {
+  return true;
+}

>From 83de9778ae6e2c5e56b58e52877cc883f80c828f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Thu, 1 Aug 2024 13:50:20 +0200
Subject: [PATCH 15/30] add pthread and c11 destroy

---
 clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index 325b0c168c9196..e2b5b9868a7025 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -284,8 +284,9 @@ void ento::registerPthreadLockChecker(CheckerManager &CM) {
       LibraryKind::Pthread, SemanticsKind::PthreadSemantics});
   RegisterEvent(EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_unlock"}),
                                 EventKind::Release, LibraryKind::Pthread});
-  RegisterEvent(EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_destroy"}),
-                                EventKind::Destroy, LibraryKind::Pthread});
+  RegisterEvent(
+      EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_destroy"}),
+                      EventKind::Destroy, LibraryKind::Pthread});
 }
 
 bool ento::shouldRegisterPthreadLockChecker(const CheckerManager &CM) {
@@ -318,6 +319,8 @@ void ento::registerC11LockChecker(CheckerManager &CM) {
   RegisterEvent(EventDescriptor{MakeFirstArgExtractor({"lck_mtx_unlock"}),
                                 EventKind::Release, LibraryKind::C11,
                                 SemanticsKind::XNUSemantics});
+  RegisterEvent(EventDescriptor{MakeFirstArgExtractor({"lck_mtx_destroy"}, 2),
+                                EventKind::Destroy, LibraryKind::C11});
 }
 
 bool ento::shouldRegisterC11LockChecker(const CheckerManager &CM) {

>From 9b549d9c291b06669fbfa51785ce20b537940c44 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Thu, 1 Aug 2024 18:01:10 +0200
Subject: [PATCH 16/30] add all previously modelled events

---
 .../Checkers/PthreadLockChecker.cpp           | 281 +++++++++++++++---
 1 file changed, 245 insertions(+), 36 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index e2b5b9868a7025..903df9a7ca86b4 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -60,14 +60,6 @@ class PthreadLockChecker : public Checker<check::PostCall> {
 
 public:
   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
-  // void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
-  // ProgramStateRef
-  // checkRegionChanges(ProgramStateRef State, const InvalidatedSymbols
-  // *Symbols,
-  //                    ArrayRef<const MemRegion *> ExplicitRegions,
-  //                    ArrayRef<const MemRegion *> Regions,
-  //                    const LocationContext *LCtx, const CallEvent *Call)
-  //                    const;
   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
                   const char *Sep) const override;
 
@@ -270,23 +262,136 @@ void ento::registerPthreadLockChecker(CheckerManager &CM) {
   ImplChecker->CheckNames[PthreadLockChecker::CK_PthreadLockChecker] =
       CM.getCurrentCheckerName();
 
-  RegisterEvent(
-      EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_init"}, 2),
-                      EventKind::Init, LibraryKind::Pthread});
-  RegisterEvent(
-      EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_init"}, 2),
-                      EventKind::Init, LibraryKind::Pthread});
-  RegisterEvent(EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_lock"}),
-                                EventKind::Acquire, LibraryKind::Pthread,
-                                SemanticsKind::PthreadSemantics});
-  RegisterEvent(EventDescriptor{
-      MakeFirstArgExtractor({"pthread_mutex_trylock"}), EventKind::TryAcquire,
-      LibraryKind::Pthread, SemanticsKind::PthreadSemantics});
-  RegisterEvent(EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_unlock"}),
-                                EventKind::Release, LibraryKind::Pthread});
-  RegisterEvent(
-      EventDescriptor{MakeFirstArgExtractor({"pthread_mutex_destroy"}),
-                      EventKind::Destroy, LibraryKind::Pthread});
+  // clang-format off
+  // Init
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor(
+      {"pthread_mutex_init"}, 2),
+      EventKind::Init,
+      LibraryKind::Pthread
+  });
+
+  // Acquire
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor(
+      {"pthread_mutex_lock"}),
+      EventKind::Acquire,
+      LibraryKind::Pthread,
+      SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"pthread_rwlock_rdlock"}),
+    EventKind::Acquire,
+    LibraryKind::Pthread,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"pthread_rwlock_wrlock"}),
+    EventKind::Acquire,
+    LibraryKind::Pthread,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_mtx_lock"}),
+    EventKind::Acquire,
+    LibraryKind::Pthread,
+    SemanticsKind::XNUSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_rw_lock_exclusive"}),
+    EventKind::Acquire,
+    LibraryKind::Pthread,
+    SemanticsKind::XNUSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_rw_lock_shared"}),
+    EventKind::Acquire,
+    LibraryKind::Pthread,
+    SemanticsKind::XNUSemantics
+  });
+
+  // TryAcquire
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"pthread_mutex_trylock"}),
+    EventKind::TryAcquire,
+    LibraryKind::Pthread,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"pthread_rwlock_tryrdlock"}),
+    EventKind::Acquire,
+    LibraryKind::Pthread,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"pthread_rwlock_trywrlock"}),
+    EventKind::Acquire,
+    LibraryKind::Pthread,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_mtx_try_lock"}),
+    EventKind::Acquire,
+    LibraryKind::Pthread,
+    SemanticsKind::XNUSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_rw_try_lock_exclusive"}),
+    EventKind::Acquire,
+    LibraryKind::Pthread,
+    SemanticsKind::XNUSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_rw_try_lock_shared"}),
+    EventKind::Acquire,
+    LibraryKind::Pthread,
+    SemanticsKind::XNUSemantics
+  });
+
+  // Release
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"pthread_mutex_unlock"}),
+    EventKind::Release,
+    LibraryKind::Pthread
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"pthread_rwlock_unlock"}),
+    EventKind::Release,
+    LibraryKind::Pthread
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_mtx_unlock"}),
+    EventKind::Release,
+    LibraryKind::Pthread
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_rw_unlock_exclusive"}),
+    EventKind::Release,
+    LibraryKind::Pthread
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_rw_unlock_shared"}),
+    EventKind::Release,
+    LibraryKind::Pthread
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_rw_done"}),
+    EventKind::Release,
+    LibraryKind::Pthread
+  });
+
+  // Destroy
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"pthread_mutex_destroy"}),
+    EventKind::Destroy,
+    LibraryKind::Pthread
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_mtx_destroy"}, 2),
+    EventKind::Destroy,
+    LibraryKind::Pthread
+  });
+  // clang-format on
 }
 
 bool ento::shouldRegisterPthreadLockChecker(const CheckerManager &CM) {
@@ -298,6 +403,77 @@ void ento::registerFuchsiaLockChecker(CheckerManager &CM) {
   ImplChecker->ChecksEnabled[PthreadLockChecker::CK_FuchsiaLockChecker] = true;
   ImplChecker->CheckNames[PthreadLockChecker::CK_FuchsiaLockChecker] =
       CM.getCurrentCheckerName();
+  // clang-format off
+  // Init
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"spin_lock_init"}),
+    EventKind::Init,
+    LibraryKind::Fuchsia
+  });
+
+  // Acquire
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"spin_lock"}),
+    EventKind::Acquire,
+    LibraryKind::Fuchsia,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"spin_lock_save"}, 3),
+    EventKind::Acquire,
+    LibraryKind::Fuchsia,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"sync_mutex_lock"}),
+    EventKind::Acquire,
+    LibraryKind::Fuchsia,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"sync_mutex_lock_with_waiter"}),
+    EventKind::Acquire,
+    LibraryKind::Fuchsia,
+    SemanticsKind::PthreadSemantics
+  });
+
+  // TryAcquire
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"spin_try_lock"}),
+    EventKind::TryAcquire,
+    LibraryKind::Fuchsia,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"sync_mutex_trylock"}),
+    EventKind::TryAcquire,
+    LibraryKind::Fuchsia,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"sync_mutex_timedlock"}, 2),
+    EventKind::TryAcquire,
+    LibraryKind::Fuchsia,
+    SemanticsKind::PthreadSemantics
+  });
+
+  // Release
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"spin_unlock"}),
+    EventKind::Release,
+    LibraryKind::Fuchsia
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"spin_unlock_restore"}, 3),
+    EventKind::Release,
+    LibraryKind::Fuchsia
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"sync_mutex_unlock"}),
+    EventKind::Release,
+    LibraryKind::Fuchsia
+  });
+  // clang-format on
 }
 
 bool ento::shouldRegisterFuchsiaLockChecker(const CheckerManager &CM) {
@@ -310,17 +486,50 @@ void ento::registerC11LockChecker(CheckerManager &CM) {
   ImplChecker->CheckNames[PthreadLockChecker::CK_C11LockChecker] =
       CM.getCurrentCheckerName();
 
-  RegisterEvent(EventDescriptor{MakeFirstArgExtractor({"lck_mtx_lock"}),
-                                EventKind::Acquire, LibraryKind::C11,
-                                SemanticsKind::XNUSemantics});
-  RegisterEvent(EventDescriptor{MakeFirstArgExtractor({"lck_mtx_try_lock"}),
-                                EventKind::TryAcquire, LibraryKind::C11,
-                                SemanticsKind::XNUSemantics});
-  RegisterEvent(EventDescriptor{MakeFirstArgExtractor({"lck_mtx_unlock"}),
-                                EventKind::Release, LibraryKind::C11,
-                                SemanticsKind::XNUSemantics});
-  RegisterEvent(EventDescriptor{MakeFirstArgExtractor({"lck_mtx_destroy"}, 2),
-                                EventKind::Destroy, LibraryKind::C11});
+  // clang-format off
+  // Init
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"mtx_init"}, 2),
+    EventKind::Init,
+    LibraryKind::C11
+  });
+
+  // Acquire
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"mtx_lock"}),
+    EventKind::Acquire,
+    LibraryKind::C11,
+    SemanticsKind::PthreadSemantics
+  });
+
+  // TryAcquire
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"mtx_trylock"}),
+    EventKind::TryAcquire,
+    LibraryKind::C11,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"mtx_timedlock"}, 2),
+    EventKind::TryAcquire,
+    LibraryKind::C11,
+    SemanticsKind::PthreadSemantics
+  });
+
+  // Release
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"mtx_unlock"}),
+    EventKind::Release,
+    LibraryKind::C11
+  });
+
+  // Destroy
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"mtx_destroy"}),
+    EventKind::Destroy,
+    LibraryKind::C11
+  });
+  // clang-format on
 }
 
 bool ento::shouldRegisterC11LockChecker(const CheckerManager &CM) {

>From 53934ebcfe62a9ceba7ccf40b5cee4ea5d07b4bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Fri, 2 Aug 2024 15:37:00 +0200
Subject: [PATCH 17/30] make all tests pass in pthread.c

---
 .../StaticAnalyzer/Checkers/MutexModeling.cpp | 422 ++++++++++--------
 .../Checkers/PthreadLockChecker.cpp           |  24 +-
 2 files changed, 247 insertions(+), 199 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
index e553234e6b73f3..3b1f45aa4dd896 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
@@ -11,7 +11,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "MutexModeling/MutexModelingAPI.h"
-#include "MutexModeling/MutexModelingDefs.h"
 #include "MutexModeling/MutexModelingDomain.h"
 #include "MutexModeling/MutexRegionExtractor.h"
 
@@ -59,10 +58,11 @@ ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef State,
       State = State->set<LockStates>(LockReg, LockStateKind::Unlocked);
       break;
     }
-    default: {
-      State = State->set<LockStates>(LockReg, LockStateKind::Destroyed);
-    }
+    default:
+      llvm_unreachable("Unknown lock state for a lock inside DestroyRetVal");
     }
+  } else {
+    State = State->set<LockStates>(LockReg, LockStateKind::Destroyed);
   }
 
   // Removing the map entry (LockReg, sym) from DestroyRetVal as the lock
@@ -79,45 +79,6 @@ ProgramStateRef doResolvePossiblyDestroyedMutex(ProgramStateRef State,
   return State;
 }
 
-void updateCritSectionOnLock(const MutexRegionExtractor &LockDescriptor,
-                             const CallEvent &Call, CheckerContext &C) {
-  const MemRegion *MutexRegion = getRegion(LockDescriptor, Call);
-  if (!MutexRegion)
-    return;
-
-  const CritSectionMarker MarkToAdd{Call.getOriginExpr(), MutexRegion};
-  ProgramStateRef StateWithLockEvent =
-      C.getState()->add<CritSections>(MarkToAdd);
-  C.addTransition(StateWithLockEvent, CreateMutexCritSectionNote(MarkToAdd, C));
-}
-
-void updateCriticalSectionOnUnlock(const EventDescriptor &UnlockDescriptor,
-                                   const CallEvent &Call, CheckerContext &C) {
-  const MemRegion *MutexRegion = getRegion(UnlockDescriptor.Trigger, Call);
-  if (!MutexRegion)
-    return;
-
-  ProgramStateRef State = C.getState();
-  const auto ActiveSections = State->get<CritSections>();
-  const auto MostRecentLock =
-      llvm::find_if(ActiveSections, [MutexRegion](auto &&Marker) {
-        return Marker.MutexRegion == MutexRegion;
-      });
-  if (MostRecentLock == ActiveSections.end())
-    return;
-
-  // Build a new ImmutableList without this element.
-  auto &Factory = State->get_context<CritSections>();
-  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);
-  }
-
-  C.addTransition(State->set<CritSections>(NewList));
-}
-
 class MutexModeling : public Checker<check::PostCall, check::DeadSymbols,
                                      check::RegionChanges> {
 public:
@@ -133,81 +94,121 @@ class MutexModeling : public Checker<check::PostCall, check::DeadSymbols,
 
 private:
   mutable std::unique_ptr<BugType> BT_initlock;
-  ProgramStateRef handleInit(const EventDescriptor &Event, const MemRegion *MTX,
+
+  // When handling events, NoteTags can be placed on ProgramPoints. This struct
+  // supports returning both the resulting ProgramState and a NoteTag.
+  struct ModelingResult {
+    ProgramStateRef State;
+    const NoteTag *Note = nullptr;
+  };
+
+  ModelingResult handleInit(const EventDescriptor &Event, const MemRegion *MTX,
+                            const CallEvent &Call, ProgramStateRef State,
+                            CheckerContext &C) const;
+  ModelingResult onSuccessfullAcquire(const MemRegion *MTX,
+                                      const CallEvent &Call,
+                                      ProgramStateRef State,
+                                      CheckerContext &C) const;
+  ModelingResult markCritSection(ModelingResult InputState,
+                                 const MemRegion *MTX, const CallEvent &Call,
+                                 CheckerContext &C) const;
+  ModelingResult handleAcquire(const EventDescriptor &Event,
+                               const MemRegion *MTX, const CallEvent &Call,
+                               ProgramStateRef State, CheckerContext &C) const;
+  ModelingResult handleTryAcquire(const EventDescriptor &Event,
+                                  const MemRegion *MTX, const CallEvent &Call,
+                                  ProgramStateRef State,
+                                  CheckerContext &C) const;
+  ModelingResult handleRelease(const EventDescriptor &Event,
+                               const MemRegion *MTX, const CallEvent &Call,
+                               ProgramStateRef State, CheckerContext &C) const;
+  ModelingResult handleDestroy(const EventDescriptor &Event,
+                               const MemRegion *MTX, const CallEvent &Call,
+                               ProgramStateRef State, CheckerContext &C) const;
+  ModelingResult handleEvent(const EventDescriptor &Event, const MemRegion *MTX,
                              const CallEvent &Call, ProgramStateRef State,
                              CheckerContext &C) const;
-  ProgramStateRef handleAcquire(const EventDescriptor &Event,
-                                const MemRegion *MTX, const CallEvent &Call,
-                                ProgramStateRef State, CheckerContext &C) const;
-  ProgramStateRef handleTryAcquire(const EventDescriptor &Event,
-                                   const MemRegion *MTX, const CallEvent &Call,
-                                   ProgramStateRef State,
-                                   CheckerContext &C) const;
-  ProgramStateRef handleRelease(const EventDescriptor &Event,
-                                const MemRegion *MTX, const CallEvent &Call,
-                                ProgramStateRef State, CheckerContext &C) const;
-  ProgramStateRef handleDestroy(const EventDescriptor &Event,
-                                const MemRegion *MTX, const CallEvent &Call,
-                                ProgramStateRef State, CheckerContext &C) const;
-  ProgramStateRef handleEvent(const EventDescriptor &Event,
-                              const MemRegion *MTX, const CallEvent &Call,
-                              ProgramStateRef State, CheckerContext &C) const;
 };
 
 } // namespace
 
-ProgramStateRef MutexModeling::handleInit(const EventDescriptor &Event,
-                                          const MemRegion *MTX,
-                                          const CallEvent &Call,
-                                          ProgramStateRef State,
-                                          CheckerContext &C) const {
-  assert(MTX && "should only be called with a mutex region");
+MutexModeling::ModelingResult
+MutexModeling::handleInit(const EventDescriptor &Event, const MemRegion *MTX,
+                          const CallEvent &Call, ProgramStateRef State,
+                          CheckerContext &C) const {
+  ModelingResult Result{State->set<LockStates>(MTX, LockStateKind::Unlocked)};
 
   const LockStateKind *LockState = State->get<LockStates>(MTX);
 
   if (!LockState)
-    return State->set<LockStates>(MTX, LockStateKind::Unlocked);
+    return Result;
 
   switch (*LockState) {
   case (LockStateKind::Destroyed): {
-    return State->set<LockStates>(MTX, LockStateKind::Unlocked);
+    Result.State = State->set<LockStates>(MTX, LockStateKind::Unlocked);
+    break;
   }
   case (LockStateKind::Locked): {
-    return State->set<LockStates>(MTX,
-                                  LockStateKind::Error_DoubleInitWhileLocked);
+    Result.State =
+        State->set<LockStates>(MTX, LockStateKind::Error_DoubleInitWhileLocked);
+    break;
   }
   default: {
-    return State->set<LockStates>(MTX, LockStateKind::Error_DoubleInit);
+    Result.State = State->set<LockStates>(MTX, LockStateKind::Error_DoubleInit);
+    break;
   }
   }
+
+  return Result;
 }
 
-static ProgramStateRef doAcquireCommonLogic(ProgramStateRef State,
-                                            const MemRegion *MTX) {
+MutexModeling::ModelingResult
+MutexModeling::markCritSection(MutexModeling::ModelingResult InputState,
+                               const MemRegion *MTX, const CallEvent &Call,
+                               CheckerContext &C) const {
+  const CritSectionMarker MarkToAdd{Call.getOriginExpr(), MTX};
+  return {InputState.State->add<CritSections>(MarkToAdd),
+          CreateMutexCritSectionNote(MarkToAdd, C)};
+}
+
+MutexModeling::ModelingResult
+MutexModeling::onSuccessfullAcquire(const MemRegion *MTX, const CallEvent &Call,
+                                    ProgramStateRef State,
+                                    CheckerContext &C) const {
+  ModelingResult Result{State};
+
   const LockStateKind *LockState = State->get<LockStates>(MTX);
 
-  if (!LockState)
-    return State->set<LockStates>(MTX, LockStateKind::Locked);
+  if (!LockState) {
+    Result.State = Result.State->set<LockStates>(MTX, LockStateKind::Locked);
+    Result = markCritSection(Result, MTX, Call, C);
+    return Result;
+  }
 
   switch (*LockState) {
   case LockStateKind::Unlocked:
-    return State->set<LockStates>(MTX, LockStateKind::Locked);
+    Result.State = Result.State->set<LockStates>(MTX, LockStateKind::Locked);
+    Result = markCritSection(Result, MTX, Call, C);
+    break;
   case LockStateKind::Locked:
-    return State->set<LockStates>(MTX, LockStateKind::Error_DoubleLock);
+    Result.State =
+        Result.State->set<LockStates>(MTX, LockStateKind::Error_DoubleLock);
+    break;
   case LockStateKind::Destroyed:
-    return State->set<LockStates>(MTX, LockStateKind::Error_LockDestroyed);
+    Result.State =
+        Result.State->set<LockStates>(MTX, LockStateKind::Error_LockDestroyed);
+    break;
   default:
-    return State;
+    break;
   }
-}
 
-ProgramStateRef MutexModeling::handleAcquire(const EventDescriptor &Event,
-                                             const MemRegion *MTX,
-                                             const CallEvent &Call,
-                                             ProgramStateRef State,
-                                             CheckerContext &C) const {
+  return Result;
+}
 
-  State = doAcquireCommonLogic(State, MTX);
+MutexModeling::ModelingResult
+MutexModeling::handleAcquire(const EventDescriptor &Event, const MemRegion *MTX,
+                             const CallEvent &Call, ProgramStateRef State,
+                             CheckerContext &C) const {
 
   switch (Event.Semantics) {
   case SemanticsKind::PthreadSemantics: {
@@ -219,6 +220,8 @@ ProgramStateRef MutexModeling::handleAcquire(const EventDescriptor &Event,
       State = State->assume(*DefinedRetVal, false);
       assert(State);
     }
+    // We might want to handle the case when the mutex lock function was
+    // inlined and returned an Unknown or Undefined value.
     break;
   }
   case SemanticsKind::XNUSemantics:
@@ -229,17 +232,14 @@ ProgramStateRef MutexModeling::handleAcquire(const EventDescriptor &Event,
         "Acquire events should have either Pthread or XNU semantics");
   }
 
-  return State;
+  return onSuccessfullAcquire(MTX, Call, State, C);
 }
 
-ProgramStateRef MutexModeling::handleTryAcquire(const EventDescriptor &Event,
-                                                const MemRegion *MTX,
-                                                const CallEvent &Call,
-                                                ProgramStateRef State,
-                                                CheckerContext &C) const {
-
-  State = doAcquireCommonLogic(State, MTX);
+MutexModeling::ModelingResult MutexModeling::handleTryAcquire(
+    const EventDescriptor &Event, const MemRegion *MTX, const CallEvent &Call,
+    ProgramStateRef State, CheckerContext &C) const {
 
+  ModelingResult Result{State};
   // Bifurcate the state, and allow a mode where the lock acquisition fails.
   ProgramStateRef LockSucc;
   SVal RetVal = Call.getReturnValue();
@@ -253,134 +253,178 @@ ProgramStateRef MutexModeling::handleTryAcquire(const EventDescriptor &Event,
       std::tie(LockSucc, LockFail) = State->assume(*DefinedRetVal);
       break;
     default:
-      llvm_unreachable("Unknown tryLock locking semantics");
+      llvm_unreachable("Unknown TryLock locking semantics");
     }
+    assert(LockFail && LockSucc && "Bifurcation point in ExplodedGraph");
 
     // This is the bifurcation point in the ExplodedGraph, we do not need to
-    // pass the new ExplodedGraph node because we do not plan on building this
-    // failed case part forward in this checker.
+    // return the new ExplodedGraph node because we do not plan on building this
+    // lock-failed case path in this checker.
     C.addTransition(LockFail);
-
-    // Pass the state where the locking succeeded onwards.
-    State = LockSucc;
-    // We might want to handle the case when the mutex lock function was
-    // inlined and returned an Unknown or Undefined value.
   }
-  return State;
-}
 
-ProgramStateRef MutexModeling::handleRelease(const EventDescriptor &Event,
-                                             const MemRegion *MTX,
-                                             const CallEvent &Call,
-                                             ProgramStateRef State,
-                                             CheckerContext &C) const {
+  // Pass the state where the locking succeeded onwards.
+  Result = onSuccessfullAcquire(MTX, Call, LockSucc, C);
+  return Result;
+}
 
-  const LockStateKind *LockState = State->get<LockStates>(MTX);
+MutexModeling::ModelingResult
+MutexModeling::handleRelease(const EventDescriptor &Event, const MemRegion *MTX,
+                             const CallEvent &Call, ProgramStateRef State,
+                             CheckerContext &C) const {
 
-  // FIXME: to be more conservative we shoud maybe just assume, that the lock
-  // is was in a locked state? This would assume that before we start modeling
-  // the mutex, the state was correct.
-  // return State->set<LockStates>(MTX, LockStateKind::Unlocked);
-  if (!LockState)
-    return State->set<LockStates>(MTX, LockStateKind::Error_DoubleUnlock);
+  ModelingResult Result{State};
 
-  if (*LockState == LockStateKind::Unlocked)
-    return State->set<LockStates>(MTX, LockStateKind::Error_DoubleUnlock);
+  const LockStateKind *LockState = Result.State->get<LockStates>(MTX);
 
-  if (*LockState == LockStateKind::Destroyed)
-    return State->set<LockStates>(MTX, LockStateKind::Error_UnlockDestroyed);
+  if (!LockState) {
+    Result.State = Result.State->set<LockStates>(MTX, LockStateKind::Unlocked);
+    return Result;
+  }
 
-  // Check if the currently released mutex is also the most recently locked
-  // one. If not, report a lock reversal bug.
-  //
-  // NOTE: MutexEvents stores events in reverse order as the ImmutableList data
-  // structure grows towards its Head element.
-  //
-  // Every time a Release event is met in the reverse order of Mutex events it
-  // is pushed to a stack. Every time an Acquire-like event is met, if the top
-  // of the stack matches the Acquire event's mutex region, it is popped
-  // (signifying that the order of lock/unlocks was matching), but if a
-  // non-matching item is found, we are sure that there is a reversal.
-  //
-  // FIXME: A stack of mutexes could be used instead of computing the reverse
-  // critical sections every time, use the critical section modeling aggregated
-  // data for this check, and rewrite the comment paragraph above.
-  const auto &Events = State->get<MutexEvents>();
-  llvm::SmallVector<const EventMarker *, 4> InverseLockStack;
-  for (const auto &Event : Events) {
-    if (Event.Kind == EventKind::Release) {
-      InverseLockStack.push_back(&Event);
-    } else if (Event.Kind == EventKind::Acquire ||
-               Event.Kind == EventKind::TryAcquire) {
-      if (InverseLockStack.empty()) {
-        continue;
-      }
-      if (InverseLockStack.back()->MutexRegion == Event.MutexRegion) {
-        InverseLockStack.pop_back();
-      }
-    }
+  if (*LockState == LockStateKind::Unlocked) {
+    Result.State =
+        State->set<LockStates>(MTX, LockStateKind::Error_DoubleUnlock);
+    return Result;
   }
 
-  if (!InverseLockStack.empty()) {
-    return State->set<LockStates>(MTX, LockStateKind::Error_LockReversal);
+  if (*LockState == LockStateKind::Destroyed) {
+    Result.State =
+        State->set<LockStates>(MTX, LockStateKind::Error_UnlockDestroyed);
+    return Result;
   }
 
-  return State->set<LockStates>(MTX, LockStateKind::Unlocked);
-}
+  const auto ActiveSections = State->get<CritSections>();
+  const auto MostRecentLockForMTX =
+      llvm::find_if(ActiveSections,
+                    [MTX](auto &&Marker) { return Marker.MutexRegion == MTX; });
+
+  // In a non-empty critical section list, if the most recent lock is for
+  // another mutex, then ther is a lock reversal.
+  bool IsLockInversion = MostRecentLockForMTX != ActiveSections.begin();
+
+  // NOTE: IsLockInversion -> !ActiveSections.isEmpty()
+  assert((!IsLockInversion || !ActiveSections.isEmpty()) &&
+         "The existance of an inversion implies that the list is not empty");
+
+  if (IsLockInversion) {
+    Result.State =
+        State->set<LockStates>(MTX, LockStateKind::Error_LockReversal);
+    // Build a new ImmutableList without this element.
+    auto &Factory = Result.State->get_context<CritSections>();
+    llvm::ImmutableList<CritSectionMarker> WithoutThisLock =
+        Factory.getEmptyList();
+    for (auto It = ActiveSections.begin(), End = ActiveSections.end();
+         It != End; ++It) {
+      if (It != MostRecentLockForMTX)
+        WithoutThisLock = Factory.add(*It, WithoutThisLock);
+    }
+    Result.State = Result.State->set<CritSections>(WithoutThisLock);
+    return Result;
+  }
 
-ProgramStateRef MutexModeling::handleDestroy(const EventDescriptor &Event,
-                                             const MemRegion *MTX,
-                                             const CallEvent &Call,
-                                             ProgramStateRef State,
-                                             CheckerContext &C) const {
+  Result.State = Result.State->set<LockStates>(MTX, LockStateKind::Unlocked);
+  // If there is no lock inversion, we can just remove the last crit section.
+  // NOTE: It should be safe to call getTail on an empty list
+  Result.State = Result.State->set<CritSections>(ActiveSections.getTail());
 
-  // TODO: Check if calling resolvePossiblyDestroyedMutex is necessary.
-  const SymbolRef *LockReturnSym = State->get<DestroyedRetVals>(MTX);
-  if (LockReturnSym)
-    State = resolvePossiblyDestroyedMutex(State, MTX, LockReturnSym);
+  return Result;
+}
 
-  const LockStateKind *LockState = State->get<LockStates>(MTX);
+MutexModeling::ModelingResult
+MutexModeling::handleDestroy(const EventDescriptor &Event, const MemRegion *MTX,
+                             const CallEvent &Call, ProgramStateRef State,
+                             CheckerContext &C) const {
 
-  if (!LockState || *LockState == LockStateKind::Unlocked) {
-    if (Event.Semantics != SemanticsKind::PthreadSemantics) {
-      return State->set<LockStates>(MTX, LockStateKind::Destroyed);
+  // Original implementation:
+  //
+  // const LockState *LState = State->get<LockMap>(LockR);
+  // // Checking the return value of the destroy method only in the case of
+  // // PthreadSemantics
+  // if (Semantics == PthreadSemantics) {
+  //   if (!LState || LState->isUnlocked()) {
+  //     SymbolRef sym = Call.getReturnValue().getAsSymbol();
+  //     if (!sym) {
+  //       State = State->remove<LockMap>(LockR);
+  //       C.addTransition(State);
+  //       return;
+  //     }
+  //     State = State->set<DestroyRetVal>(LockR, sym);
+  //     if (LState && LState->isUnlocked())
+  //       State = State->set<LockMap>(
+  //           LockR, LockState::getUnlockedAndPossiblyDestroyed());
+  //     else
+  //       State = State->set<LockMap>(
+  //           LockR, LockState::getUntouchedAndPossiblyDestroyed());
+  //     C.addTransition(State);
+  //     return;
+  //   }
+  // } else {
+  //   if (!LState || LState->isUnlocked()) {
+  //     State = State->set<LockMap>(LockR, LockState::getDestroyed());
+  //     C.addTransition(State);
+  //     return;
+  //   }
+  // }
+
+  // StringRef Message = LState->isLocked()
+  //                         ? "This lock is still locked"
+  //                         : "This lock has already been destroyed";
+
+  // reportBug(C, BT_destroylock, MtxExpr, CheckKind, Message);
+
+  // New implementation:
+
+  ModelingResult Result{State};
+
+  const LockStateKind *LockState = Result.State->get<LockStates>(MTX);
+
+  if (Event.Semantics == SemanticsKind::PthreadSemantics) {
+    if (!LockState || *LockState == LockStateKind::Unlocked) {
+      SymbolRef Sym = Call.getReturnValue().getAsSymbol();
+      if (!Sym) {
+        Result.State = Result.State->remove<LockStates>(MTX);
+        return Result;
+      }
+      Result.State = Result.State->set<DestroyedRetVals>(MTX, Sym);
+      Result.State = Result.State->set<LockStates>(
+          MTX, LockState && *LockState == LockStateKind::Unlocked
+                   ? LockStateKind::UnlockedAndPossiblyDestroyed
+                   : LockStateKind::UntouchedAndPossiblyDestroyed);
+      return Result;
     }
-
-    // In PthreadSemantics we reason about the return value of the destroy
-    // calls.
-    SymbolRef Sym = Call.getReturnValue().getAsSymbol();
-    if (!Sym) {
-      return State->remove<LockStates>(MTX);
+  } else {
+    if (!LockState || *LockState == LockStateKind::Unlocked) {
+      Result.State =
+          Result.State->set<LockStates>(MTX, LockStateKind::Destroyed);
+      return Result;
     }
-
-    State = State->set<DestroyedRetVals>(MTX, Sym);
-    return State->set<LockStates>(
-        MTX, LockState && *LockState == LockStateKind::Unlocked
-                 ? LockStateKind::UnlockedAndPossiblyDestroyed
-                 : LockStateKind::UntouchedAndPossiblyDestroyed);
   }
 
   if (*LockState == LockStateKind::Locked) {
-    return State->set<LockStates>(MTX, LockStateKind::Error_DestroyLocked);
+    Result.State =
+        Result.State->set<LockStates>(MTX, LockStateKind::Error_DestroyLocked);
+    return Result;
   }
 
   if (*LockState == LockStateKind::Destroyed) {
-    return State->set<LockStates>(MTX, LockStateKind::Error_DoubleDestroy);
+    Result.State =
+        Result.State->set<LockStates>(MTX, LockStateKind::Error_DoubleDestroy);
+    return Result;
   }
 
   assert(LockState && *LockState != LockStateKind::Unlocked &&
          *LockState != LockStateKind::Locked &&
          *LockState != LockStateKind::Destroyed &&
-         "Only error states should remain if we get here");
+         "We can only get here if we came from an error-state to begin with");
 
-  return State;
+  return Result;
 }
 
-ProgramStateRef MutexModeling::handleEvent(const EventDescriptor &Event,
-                                           const MemRegion *MTX,
-                                           const CallEvent &Call,
-                                           ProgramStateRef State,
-                                           CheckerContext &C) const {
+MutexModeling::ModelingResult
+MutexModeling::handleEvent(const EventDescriptor &Event, const MemRegion *MTX,
+                           const CallEvent &Call, ProgramStateRef State,
+                           CheckerContext &C) const {
   assert(MTX && "should only be called with a mutex region");
 
   State = State->add<MutexEvents>(
@@ -417,8 +461,8 @@ void MutexModeling::checkPostCall(const CallEvent &Call,
       if (!MTX)
         continue;
       State = doResolvePossiblyDestroyedMutex(State, MTX);
-      State = handleEvent(Event, MTX, Call, State, C);
-      C.addTransition(State);
+      ModelingResult Result = handleEvent(Event, MTX, Call, State, C);
+      C.addTransition(Result.State, Result.Note);
     }
   }
 }
diff --git a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index 903df9a7ca86b4..f857a1246712b6 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -170,7 +170,8 @@ void PthreadLockChecker::checkReleaseEvent(const EventMarker &LastEvent,
               "This lock has already been destroyed");
   } else if (*LockState == LockStateKind::Error_LockReversal) {
     reportBug(C, BT_lor, LastEvent.EventExpr, detectCheckerKind(LastEvent),
-              "This was not the most recently acquired lock");
+              "This was not the most recently acquired lock. Possible lock "
+              "order reversal");
   }
 }
 
@@ -319,31 +320,31 @@ void ento::registerPthreadLockChecker(CheckerManager &CM) {
   });
   RegisterEvent(EventDescriptor{
     MakeFirstArgExtractor({"pthread_rwlock_tryrdlock"}),
-    EventKind::Acquire,
+    EventKind::TryAcquire,
     LibraryKind::Pthread,
     SemanticsKind::PthreadSemantics
   });
   RegisterEvent(EventDescriptor{
     MakeFirstArgExtractor({"pthread_rwlock_trywrlock"}),
-    EventKind::Acquire,
+    EventKind::TryAcquire,
     LibraryKind::Pthread,
     SemanticsKind::PthreadSemantics
   });
   RegisterEvent(EventDescriptor{
     MakeFirstArgExtractor({"lck_mtx_try_lock"}),
-    EventKind::Acquire,
+    EventKind::TryAcquire,
     LibraryKind::Pthread,
     SemanticsKind::XNUSemantics
   });
   RegisterEvent(EventDescriptor{
     MakeFirstArgExtractor({"lck_rw_try_lock_exclusive"}),
-    EventKind::Acquire,
+    EventKind::TryAcquire,
     LibraryKind::Pthread,
     SemanticsKind::XNUSemantics
   });
   RegisterEvent(EventDescriptor{
     MakeFirstArgExtractor({"lck_rw_try_lock_shared"}),
-    EventKind::Acquire,
+    EventKind::TryAcquire,
     LibraryKind::Pthread,
     SemanticsKind::XNUSemantics
   });
@@ -384,12 +385,14 @@ void ento::registerPthreadLockChecker(CheckerManager &CM) {
   RegisterEvent(EventDescriptor{
     MakeFirstArgExtractor({"pthread_mutex_destroy"}),
     EventKind::Destroy,
-    LibraryKind::Pthread
+    LibraryKind::Pthread,
+    SemanticsKind::PthreadSemantics
   });
   RegisterEvent(EventDescriptor{
     MakeFirstArgExtractor({"lck_mtx_destroy"}, 2),
     EventKind::Destroy,
-    LibraryKind::Pthread
+    LibraryKind::Pthread,
+    SemanticsKind::XNUSemantics
   });
   // clang-format on
 }
@@ -439,7 +442,7 @@ void ento::registerFuchsiaLockChecker(CheckerManager &CM) {
 
   // TryAcquire
   RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"spin_try_lock"}),
+    MakeFirstArgExtractor({"spin_trylock"}),
     EventKind::TryAcquire,
     LibraryKind::Fuchsia,
     SemanticsKind::PthreadSemantics
@@ -527,7 +530,8 @@ void ento::registerC11LockChecker(CheckerManager &CM) {
   RegisterEvent(EventDescriptor{
     MakeFirstArgExtractor({"mtx_destroy"}),
     EventKind::Destroy,
-    LibraryKind::C11
+    LibraryKind::C11,
+    SemanticsKind::PthreadSemantics
   });
   // clang-format on
 }

>From aa03701648011decdd985fd9e25a466b302205a2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Fri, 2 Aug 2024 17:07:25 +0200
Subject: [PATCH 18/30] move modeling to mutexmodeling, remove default cases
 where warnings were given

---
 .../clang/StaticAnalyzer/Checkers/Checkers.td |  10 +-
 .../StaticAnalyzer/Checkers/MutexModeling.cpp | 292 +++++++++++++++---
 .../Checkers/MutexModeling/MutexModelingAPI.h |   4 -
 .../Checkers/PthreadLockChecker.cpp           | 252 ---------------
 .../test/Analysis/analyzer-enabled-checkers.c |   2 +-
 ...c-library-functions-arg-enabled-checkers.c |   2 +-
 6 files changed, 257 insertions(+), 305 deletions(-)

diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index a3b3ec742e458b..aed08ad558891f 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -220,6 +220,11 @@ def DereferenceChecker : Checker<"NullDereference">,
   ]>,
   Documentation<HasDocumentation>;
 
+def MutexModeling : Checker<"MutexModeling">,
+  HelpText<"Model mutexes lock and unlock events">,
+  Documentation<NotDocumented>,
+  Hidden;
+
 def NonNullParamChecker : Checker<"NonNullParamChecker">,
   HelpText<"Check for null pointers passed as arguments to a function whose "
            "arguments are references or marked with the 'nonnull' attribute">,
@@ -261,11 +266,6 @@ def NonnullGlobalConstantsChecker: Checker<"NonnilStringConstants">,
 
 let ParentPackage = CoreAlpha in {
 
-def MutexModeling : Checker<"MutexModeling">,
-  HelpText<"Model mutexes lock and unlock events">,
-  Documentation<NotDocumented>,
-  Hidden;
-
 def BoolAssignmentChecker : Checker<"BoolAssignment">,
   HelpText<"Warn about assigning non-{0,1} values to Boolean variables">,
   Documentation<HasDocumentation>;
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
index 3b1f45aa4dd896..f9653ad790b873 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
@@ -335,46 +335,6 @@ MutexModeling::ModelingResult
 MutexModeling::handleDestroy(const EventDescriptor &Event, const MemRegion *MTX,
                              const CallEvent &Call, ProgramStateRef State,
                              CheckerContext &C) const {
-
-  // Original implementation:
-  //
-  // const LockState *LState = State->get<LockMap>(LockR);
-  // // Checking the return value of the destroy method only in the case of
-  // // PthreadSemantics
-  // if (Semantics == PthreadSemantics) {
-  //   if (!LState || LState->isUnlocked()) {
-  //     SymbolRef sym = Call.getReturnValue().getAsSymbol();
-  //     if (!sym) {
-  //       State = State->remove<LockMap>(LockR);
-  //       C.addTransition(State);
-  //       return;
-  //     }
-  //     State = State->set<DestroyRetVal>(LockR, sym);
-  //     if (LState && LState->isUnlocked())
-  //       State = State->set<LockMap>(
-  //           LockR, LockState::getUnlockedAndPossiblyDestroyed());
-  //     else
-  //       State = State->set<LockMap>(
-  //           LockR, LockState::getUntouchedAndPossiblyDestroyed());
-  //     C.addTransition(State);
-  //     return;
-  //   }
-  // } else {
-  //   if (!LState || LState->isUnlocked()) {
-  //     State = State->set<LockMap>(LockR, LockState::getDestroyed());
-  //     C.addTransition(State);
-  //     return;
-  //   }
-  // }
-
-  // StringRef Message = LState->isLocked()
-  //                         ? "This lock is still locked"
-  //                         : "This lock has already been destroyed";
-
-  // reportBug(C, BT_destroylock, MtxExpr, CheckKind, Message);
-
-  // New implementation:
-
   ModelingResult Result{State};
 
   const LockStateKind *LockState = Result.State->get<LockStates>(MTX);
@@ -442,8 +402,6 @@ MutexModeling::handleEvent(const EventDescriptor &Event, const MemRegion *MTX,
     return handleRelease(Event, MTX, Call, State, C);
   case EventKind::Destroy:
     return handleDestroy(Event, MTX, Call, State, C);
-  default:
-    llvm_unreachable("Unhandled event kind!");
   }
 }
 
@@ -537,6 +495,256 @@ namespace ento {
 // Checker registration
 void registerMutexModeling(CheckerManager &CM) {
   CM.registerChecker<MutexModeling>();
+
+  // clang-format off
+  // Pthread-related events
+  // Init
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor(
+      {"pthread_mutex_init"}, 2),
+      EventKind::Init,
+      LibraryKind::Pthread
+  });
+
+  // Acquire
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor(
+      {"pthread_mutex_lock"}),
+      EventKind::Acquire,
+      LibraryKind::Pthread,
+      SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"pthread_rwlock_rdlock"}),
+    EventKind::Acquire,
+    LibraryKind::Pthread,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"pthread_rwlock_wrlock"}),
+    EventKind::Acquire,
+    LibraryKind::Pthread,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_mtx_lock"}),
+    EventKind::Acquire,
+    LibraryKind::Pthread,
+    SemanticsKind::XNUSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_rw_lock_exclusive"}),
+    EventKind::Acquire,
+    LibraryKind::Pthread,
+    SemanticsKind::XNUSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_rw_lock_shared"}),
+    EventKind::Acquire,
+    LibraryKind::Pthread,
+    SemanticsKind::XNUSemantics
+  });
+
+  // TryAcquire
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"pthread_mutex_trylock"}),
+    EventKind::TryAcquire,
+    LibraryKind::Pthread,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"pthread_rwlock_tryrdlock"}),
+    EventKind::TryAcquire,
+    LibraryKind::Pthread,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"pthread_rwlock_trywrlock"}),
+    EventKind::TryAcquire,
+    LibraryKind::Pthread,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_mtx_try_lock"}),
+    EventKind::TryAcquire,
+    LibraryKind::Pthread,
+    SemanticsKind::XNUSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_rw_try_lock_exclusive"}),
+    EventKind::TryAcquire,
+    LibraryKind::Pthread,
+    SemanticsKind::XNUSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_rw_try_lock_shared"}),
+    EventKind::TryAcquire,
+    LibraryKind::Pthread,
+    SemanticsKind::XNUSemantics
+  });
+
+  // Release
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"pthread_mutex_unlock"}),
+    EventKind::Release,
+    LibraryKind::Pthread
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"pthread_rwlock_unlock"}),
+    EventKind::Release,
+    LibraryKind::Pthread
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_mtx_unlock"}),
+    EventKind::Release,
+    LibraryKind::Pthread
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_rw_unlock_exclusive"}),
+    EventKind::Release,
+    LibraryKind::Pthread
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_rw_unlock_shared"}),
+    EventKind::Release,
+    LibraryKind::Pthread
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_rw_done"}),
+    EventKind::Release,
+    LibraryKind::Pthread
+  });
+
+  // Destroy
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"pthread_mutex_destroy"}),
+    EventKind::Destroy,
+    LibraryKind::Pthread,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"lck_mtx_destroy"}, 2),
+    EventKind::Destroy,
+    LibraryKind::Pthread,
+    SemanticsKind::XNUSemantics
+  });
+
+  // Fuchsia-related events
+  // Init
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"spin_lock_init"}),
+    EventKind::Init,
+    LibraryKind::Fuchsia
+  });
+
+  // Acquire
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"spin_lock"}),
+    EventKind::Acquire,
+    LibraryKind::Fuchsia,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"spin_lock_save"}, 3),
+    EventKind::Acquire,
+    LibraryKind::Fuchsia,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"sync_mutex_lock"}),
+    EventKind::Acquire,
+    LibraryKind::Fuchsia,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"sync_mutex_lock_with_waiter"}),
+    EventKind::Acquire,
+    LibraryKind::Fuchsia,
+    SemanticsKind::PthreadSemantics
+  });
+
+  // TryAcquire
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"spin_trylock"}),
+    EventKind::TryAcquire,
+    LibraryKind::Fuchsia,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"sync_mutex_trylock"}),
+    EventKind::TryAcquire,
+    LibraryKind::Fuchsia,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"sync_mutex_timedlock"}, 2),
+    EventKind::TryAcquire,
+    LibraryKind::Fuchsia,
+    SemanticsKind::PthreadSemantics
+  });
+
+  // Release
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"spin_unlock"}),
+    EventKind::Release,
+    LibraryKind::Fuchsia
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"spin_unlock_restore"}, 3),
+    EventKind::Release,
+    LibraryKind::Fuchsia
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"sync_mutex_unlock"}),
+    EventKind::Release,
+    LibraryKind::Fuchsia
+  });
+
+  // C11-related events
+  // Init
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"mtx_init"}, 2),
+    EventKind::Init,
+    LibraryKind::C11
+  });
+
+  // Acquire
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"mtx_lock"}),
+    EventKind::Acquire,
+    LibraryKind::C11,
+    SemanticsKind::PthreadSemantics
+  });
+
+  // TryAcquire
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"mtx_trylock"}),
+    EventKind::TryAcquire,
+    LibraryKind::C11,
+    SemanticsKind::PthreadSemantics
+  });
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"mtx_timedlock"}, 2),
+    EventKind::TryAcquire,
+    LibraryKind::C11,
+    SemanticsKind::PthreadSemantics
+  });
+
+  // Release
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"mtx_unlock"}),
+    EventKind::Release,
+    LibraryKind::C11
+  });
+
+  // Destroy
+  RegisterEvent(EventDescriptor{
+    MakeFirstArgExtractor({"mtx_destroy"}),
+    EventKind::Destroy,
+    LibraryKind::C11,
+    SemanticsKind::PthreadSemantics
+  });
+  // clang-format on
 }
 bool shouldRegisterMutexModeling(const CheckerManager &) { return true; }
 } // namespace ento
diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h
index 2e53b71c932eb4..f420920634a87c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h
@@ -141,8 +141,6 @@ inline void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
       case (EventKind::Destroy):
         Out << "Destroy";
         break;
-      default:
-        llvm_unreachable("Unknown event kind");
       }
       Out << NL;
       Out << Sep << "Semantics: ";
@@ -156,8 +154,6 @@ inline void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
       case (SemanticsKind::XNUSemantics):
         Out << "XNUSemantics";
         break;
-      default:
-        llvm_unreachable("Unknown semantics");
       }
       Out << NL;
       Out << Sep << "Library: ";
diff --git a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index f857a1246712b6..f1430dd3e7aea8 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -225,8 +225,6 @@ void PthreadLockChecker::checkPostCall(const CallEvent &Call,
   case EventKind::Destroy:
     checkDestroyEvent(LastEvent, C);
     break;
-  default:
-    llvm_unreachable("Unknown event kind");
   }
 }
 
@@ -262,139 +260,6 @@ void ento::registerPthreadLockChecker(CheckerManager &CM) {
   ImplChecker->ChecksEnabled[PthreadLockChecker::CK_PthreadLockChecker] = true;
   ImplChecker->CheckNames[PthreadLockChecker::CK_PthreadLockChecker] =
       CM.getCurrentCheckerName();
-
-  // clang-format off
-  // Init
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor(
-      {"pthread_mutex_init"}, 2),
-      EventKind::Init,
-      LibraryKind::Pthread
-  });
-
-  // Acquire
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor(
-      {"pthread_mutex_lock"}),
-      EventKind::Acquire,
-      LibraryKind::Pthread,
-      SemanticsKind::PthreadSemantics
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"pthread_rwlock_rdlock"}),
-    EventKind::Acquire,
-    LibraryKind::Pthread,
-    SemanticsKind::PthreadSemantics
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"pthread_rwlock_wrlock"}),
-    EventKind::Acquire,
-    LibraryKind::Pthread,
-    SemanticsKind::PthreadSemantics
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"lck_mtx_lock"}),
-    EventKind::Acquire,
-    LibraryKind::Pthread,
-    SemanticsKind::XNUSemantics
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"lck_rw_lock_exclusive"}),
-    EventKind::Acquire,
-    LibraryKind::Pthread,
-    SemanticsKind::XNUSemantics
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"lck_rw_lock_shared"}),
-    EventKind::Acquire,
-    LibraryKind::Pthread,
-    SemanticsKind::XNUSemantics
-  });
-
-  // TryAcquire
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"pthread_mutex_trylock"}),
-    EventKind::TryAcquire,
-    LibraryKind::Pthread,
-    SemanticsKind::PthreadSemantics
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"pthread_rwlock_tryrdlock"}),
-    EventKind::TryAcquire,
-    LibraryKind::Pthread,
-    SemanticsKind::PthreadSemantics
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"pthread_rwlock_trywrlock"}),
-    EventKind::TryAcquire,
-    LibraryKind::Pthread,
-    SemanticsKind::PthreadSemantics
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"lck_mtx_try_lock"}),
-    EventKind::TryAcquire,
-    LibraryKind::Pthread,
-    SemanticsKind::XNUSemantics
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"lck_rw_try_lock_exclusive"}),
-    EventKind::TryAcquire,
-    LibraryKind::Pthread,
-    SemanticsKind::XNUSemantics
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"lck_rw_try_lock_shared"}),
-    EventKind::TryAcquire,
-    LibraryKind::Pthread,
-    SemanticsKind::XNUSemantics
-  });
-
-  // Release
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"pthread_mutex_unlock"}),
-    EventKind::Release,
-    LibraryKind::Pthread
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"pthread_rwlock_unlock"}),
-    EventKind::Release,
-    LibraryKind::Pthread
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"lck_mtx_unlock"}),
-    EventKind::Release,
-    LibraryKind::Pthread
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"lck_rw_unlock_exclusive"}),
-    EventKind::Release,
-    LibraryKind::Pthread
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"lck_rw_unlock_shared"}),
-    EventKind::Release,
-    LibraryKind::Pthread
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"lck_rw_done"}),
-    EventKind::Release,
-    LibraryKind::Pthread
-  });
-
-  // Destroy
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"pthread_mutex_destroy"}),
-    EventKind::Destroy,
-    LibraryKind::Pthread,
-    SemanticsKind::PthreadSemantics
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"lck_mtx_destroy"}, 2),
-    EventKind::Destroy,
-    LibraryKind::Pthread,
-    SemanticsKind::XNUSemantics
-  });
-  // clang-format on
 }
 
 bool ento::shouldRegisterPthreadLockChecker(const CheckerManager &CM) {
@@ -406,77 +271,6 @@ void ento::registerFuchsiaLockChecker(CheckerManager &CM) {
   ImplChecker->ChecksEnabled[PthreadLockChecker::CK_FuchsiaLockChecker] = true;
   ImplChecker->CheckNames[PthreadLockChecker::CK_FuchsiaLockChecker] =
       CM.getCurrentCheckerName();
-  // clang-format off
-  // Init
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"spin_lock_init"}),
-    EventKind::Init,
-    LibraryKind::Fuchsia
-  });
-
-  // Acquire
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"spin_lock"}),
-    EventKind::Acquire,
-    LibraryKind::Fuchsia,
-    SemanticsKind::PthreadSemantics
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"spin_lock_save"}, 3),
-    EventKind::Acquire,
-    LibraryKind::Fuchsia,
-    SemanticsKind::PthreadSemantics
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"sync_mutex_lock"}),
-    EventKind::Acquire,
-    LibraryKind::Fuchsia,
-    SemanticsKind::PthreadSemantics
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"sync_mutex_lock_with_waiter"}),
-    EventKind::Acquire,
-    LibraryKind::Fuchsia,
-    SemanticsKind::PthreadSemantics
-  });
-
-  // TryAcquire
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"spin_trylock"}),
-    EventKind::TryAcquire,
-    LibraryKind::Fuchsia,
-    SemanticsKind::PthreadSemantics
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"sync_mutex_trylock"}),
-    EventKind::TryAcquire,
-    LibraryKind::Fuchsia,
-    SemanticsKind::PthreadSemantics
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"sync_mutex_timedlock"}, 2),
-    EventKind::TryAcquire,
-    LibraryKind::Fuchsia,
-    SemanticsKind::PthreadSemantics
-  });
-
-  // Release
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"spin_unlock"}),
-    EventKind::Release,
-    LibraryKind::Fuchsia
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"spin_unlock_restore"}, 3),
-    EventKind::Release,
-    LibraryKind::Fuchsia
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"sync_mutex_unlock"}),
-    EventKind::Release,
-    LibraryKind::Fuchsia
-  });
-  // clang-format on
 }
 
 bool ento::shouldRegisterFuchsiaLockChecker(const CheckerManager &CM) {
@@ -488,52 +282,6 @@ void ento::registerC11LockChecker(CheckerManager &CM) {
   ImplChecker->ChecksEnabled[PthreadLockChecker::CK_C11LockChecker] = true;
   ImplChecker->CheckNames[PthreadLockChecker::CK_C11LockChecker] =
       CM.getCurrentCheckerName();
-
-  // clang-format off
-  // Init
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"mtx_init"}, 2),
-    EventKind::Init,
-    LibraryKind::C11
-  });
-
-  // Acquire
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"mtx_lock"}),
-    EventKind::Acquire,
-    LibraryKind::C11,
-    SemanticsKind::PthreadSemantics
-  });
-
-  // TryAcquire
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"mtx_trylock"}),
-    EventKind::TryAcquire,
-    LibraryKind::C11,
-    SemanticsKind::PthreadSemantics
-  });
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"mtx_timedlock"}, 2),
-    EventKind::TryAcquire,
-    LibraryKind::C11,
-    SemanticsKind::PthreadSemantics
-  });
-
-  // Release
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"mtx_unlock"}),
-    EventKind::Release,
-    LibraryKind::C11
-  });
-
-  // Destroy
-  RegisterEvent(EventDescriptor{
-    MakeFirstArgExtractor({"mtx_destroy"}),
-    EventKind::Destroy,
-    LibraryKind::C11,
-    SemanticsKind::PthreadSemantics
-  });
-  // clang-format on
 }
 
 bool ento::shouldRegisterC11LockChecker(const CheckerManager &CM) {
diff --git a/clang/test/Analysis/analyzer-enabled-checkers.c b/clang/test/Analysis/analyzer-enabled-checkers.c
index 5fe24318a807b3..d02e88def01457 100644
--- a/clang/test/Analysis/analyzer-enabled-checkers.c
+++ b/clang/test/Analysis/analyzer-enabled-checkers.c
@@ -16,6 +16,7 @@
 // CHECK-NEXT: core.CallAndMessage
 // CHECK-NEXT: core.DivideZero
 // CHECK-NEXT: core.DynamicTypePropagation
+// CHECK-NEXT: core.MutexModeling
 // CHECK-NEXT: core.NonNullParamChecker
 // CHECK-NEXT: core.NonnilStringConstants
 // CHECK-NEXT: core.NullDereference
@@ -42,7 +43,6 @@
 // CHECK-NEXT: security.insecureAPI.mktemp
 // CHECK-NEXT: security.insecureAPI.vfork
 // CHECK-NEXT: unix.API
-// CHECK-NEXT: unix.MutexModeling
 // CHECK-NEXT: unix.BlockInCriticalSection
 // CHECK-NEXT: unix.cstring.CStringModeling
 // CHECK-NEXT: unix.DynamicMemoryModeling
diff --git a/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c b/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c
index c3bacb0706782b..711567671613b3 100644
--- a/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c
+++ b/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c
@@ -24,6 +24,7 @@
 // CHECK-NEXT: core.CallAndMessage
 // CHECK-NEXT: core.DivideZero
 // CHECK-NEXT: core.DynamicTypePropagation
+// CHECK-NEXT: core.MutexModeling
 // CHECK-NEXT: core.NonNullParamChecker
 // CHECK-NEXT: core.NonnilStringConstants
 // CHECK-NEXT: core.NullDereference
@@ -50,7 +51,6 @@
 // CHECK-NEXT: security.insecureAPI.mktemp
 // CHECK-NEXT: security.insecureAPI.vfork
 // CHECK-NEXT: unix.API
-// CHECK-NEXT: unix.MutexModeling
 // CHECK-NEXT: unix.BlockInCriticalSection
 // CHECK-NEXT: unix.cstring.CStringModeling
 // CHECK-NEXT: unix.DynamicMemoryModeling

>From cfb28dd5eedf8a533a05a16f1724b71119e8bdd3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Fri, 2 Aug 2024 20:40:27 +0200
Subject: [PATCH 19/30] fix default args for MakeMemberExtractor

---
 .../StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h
index f420920634a87c..632670a473426e 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingAPI.h
@@ -54,8 +54,8 @@ MakeFirstArgExtractor(ArrayRef<StringRef> NameParts, int NumArgsRequired = 1,
 }
 
 inline auto MakeMemberExtractor(ArrayRef<StringRef> NameParts,
-                                int NumArgsRequired = 1,
-                                CallDescription::Mode MatchAs = CDM::CLibrary) {
+                                int NumArgsRequired = 0,
+                                CallDescription::Mode MatchAs = CDM::CXXMethod) {
   return MemberMutexExtractor{
       CallDescription{MatchAs, NameParts, NumArgsRequired}};
 }

>From da9bbd02851b5b079b6f1dcd3c48d76f9b98d52b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Fri, 2 Aug 2024 20:40:50 +0200
Subject: [PATCH 20/30] allow events without library

---
 .../Checkers/MutexModeling/MutexModelingDomain.h              | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h
index 9dcb3b967dd5ec..5da4c277187359 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling/MutexModelingDomain.h
@@ -33,9 +33,9 @@ enum class EventKind { Init, Acquire, TryAcquire, Release, Destroy };
 // checked mutex event the library that event came from. In order to keep the
 // external API of multiple distinct checkers (PthreadLockChecker,
 // FuchsiaLockChecker and C11LockChecker), this mapping is done here, but if
-// more consumers of this modeling arise, adding all of them here may note be
+// more consumers of this modeling arise, adding all of them here may not be
 // feasible and we may need to make this modeling more flexible.
-enum class LibraryKind { Pthread = 0, Fuchsia, C11, NumLibraryKinds };
+enum class LibraryKind { NotApplicable = 0, Pthread, Fuchsia, C11 };
 
 enum class SemanticsKind { NotApplicable = 0, PthreadSemantics, XNUSemantics };
 

>From 7460d23ee95479e0c150eb06f0be4d635a3a746e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Fri, 2 Aug 2024 20:41:27 +0200
Subject: [PATCH 21/30] update pthreadlockchecker to support events without a
 library

---
 .../Checkers/PthreadLockChecker.cpp           | 85 +++++++++----------
 1 file changed, 39 insertions(+), 46 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index f1430dd3e7aea8..c2ed0d14187fa2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -45,15 +45,7 @@ class PthreadLockChecker : public Checker<check::PostCall> {
   bool ChecksEnabled[CK_NumCheckKinds] = {false};
   CheckerNameRef CheckNames[CK_NumCheckKinds];
 
-  PthreadLockChecker() { RegisterEvents(); }
-
 private:
-  std::vector<EventDescriptor> EventsToModel{};
-  void RegisterEvents() const {
-    for (auto &&Event : EventsToModel) {
-      RegisterEvent(Event);
-    }
-  }
   void reportBug(CheckerContext &C, std::unique_ptr<BugType> BT[],
                  const Expr *MtxExpr, CheckerKind CheckKind,
                  StringRef Desc) const;
@@ -86,28 +78,33 @@ class PthreadLockChecker : public Checker<check::PostCall> {
                                         "Lock order reversal", "Lock checker"});
   }
 
-  [[nodiscard]] constexpr PthreadLockChecker::CheckerKind
+  [[nodiscard]] constexpr std::optional<PthreadLockChecker::CheckerKind>
   detectCheckerKind(mutex_modeling::EventMarker EV) const noexcept {
     switch (EV.Library) {
+    default:
+      return std::nullopt;
     case mutex_modeling::LibraryKind::Pthread:
-      return PthreadLockChecker::CK_PthreadLockChecker;
+      return {PthreadLockChecker::CK_PthreadLockChecker};
     case mutex_modeling::LibraryKind::Fuchsia:
-      return PthreadLockChecker::CK_FuchsiaLockChecker;
+      return {PthreadLockChecker::CK_FuchsiaLockChecker};
     case mutex_modeling::LibraryKind::C11:
-      return PthreadLockChecker::CK_C11LockChecker;
-    default:
-      llvm_unreachable("Unknown locking library");
+      return {PthreadLockChecker::CK_C11LockChecker};
     }
   }
 
-  void checkInitEvent(const EventMarker &LastEvent, CheckerContext &C) const;
-  void checkAcquireEvent(const EventMarker &LastEvent, CheckerContext &C) const;
-  void checkReleaseEvent(const EventMarker &LastEvent, CheckerContext &C) const;
-  void checkDestroyEvent(const EventMarker &LastEvent, CheckerContext &C) const;
+  void checkInitEvent(const EventMarker &LastEvent, CheckerKind Checker,
+                      CheckerContext &C) const;
+  void checkAcquireEvent(const EventMarker &LastEvent, CheckerKind Checker,
+                         CheckerContext &C) const;
+  void checkReleaseEvent(const EventMarker &LastEvent, CheckerKind Checker,
+                         CheckerContext &C) const;
+  void checkDestroyEvent(const EventMarker &LastEvent, CheckerKind Checker,
+                         CheckerContext &C) const;
 };
 } // end anonymous namespace
 
 void PthreadLockChecker::checkInitEvent(const EventMarker &LastEvent,
+                                        CheckerKind Checker,
                                         CheckerContext &C) const {
   ProgramStateRef State = C.getState();
 
@@ -119,79 +116,74 @@ void PthreadLockChecker::checkInitEvent(const EventMarker &LastEvent,
   }
 
   if (*LockState == LockStateKind::Error_DoubleInit) {
-    reportBug(C, BT_initlock, LastEvent.EventExpr, detectCheckerKind(LastEvent),
+    reportBug(C, BT_initlock, LastEvent.EventExpr, Checker,
               "This lock has already been initialized");
   } else if (*LockState == LockStateKind::Error_DoubleInitWhileLocked) {
-    reportBug(C, BT_initlock, LastEvent.EventExpr, detectCheckerKind(LastEvent),
+    reportBug(C, BT_initlock, LastEvent.EventExpr, Checker,
               "This lock is still being held");
   }
 }
 
 void PthreadLockChecker::checkAcquireEvent(const EventMarker &LastEvent,
+                                           CheckerKind Checker,
                                            CheckerContext &C) const {
   ProgramStateRef State = C.getState();
 
   const LockStateKind *const LockState =
       State->get<LockStates>(LastEvent.MutexRegion);
 
-  if (!LockState) {
+  if (!LockState)
     return;
-  }
 
   if (*LockState == LockStateKind::Error_DoubleLock) {
-    reportBug(C, BT_doublelock, LastEvent.EventExpr,
-              detectCheckerKind(LastEvent),
+    reportBug(C, BT_doublelock, LastEvent.EventExpr, Checker,
               "This lock has already been acquired");
   } else if (*LockState == LockStateKind::Error_LockDestroyed) {
-    reportBug(C, BT_destroylock, LastEvent.EventExpr,
-              detectCheckerKind(LastEvent),
+    reportBug(C, BT_destroylock, LastEvent.EventExpr, Checker,
               "This lock has already been destroyed");
   }
 }
 
 void PthreadLockChecker::checkReleaseEvent(const EventMarker &LastEvent,
+                                           CheckerKind Checker,
                                            CheckerContext &C) const {
   ProgramStateRef State = C.getState();
 
   const LockStateKind *const LockState =
       State->get<LockStates>(LastEvent.MutexRegion);
 
-  if (!LockState) {
+  if (!LockState)
     return;
-  }
 
   if (*LockState == LockStateKind::Error_DoubleUnlock) {
-    reportBug(C, BT_doubleunlock, LastEvent.EventExpr,
-              detectCheckerKind(LastEvent),
+    reportBug(C, BT_doubleunlock, LastEvent.EventExpr, Checker,
               "This lock has already been unlocked");
   } else if (*LockState == LockStateKind::Error_UnlockDestroyed) {
-    reportBug(C, BT_destroylock, LastEvent.EventExpr,
-              detectCheckerKind(LastEvent),
+    reportBug(C, BT_destroylock, LastEvent.EventExpr, Checker,
               "This lock has already been destroyed");
   } else if (*LockState == LockStateKind::Error_LockReversal) {
-    reportBug(C, BT_lor, LastEvent.EventExpr, detectCheckerKind(LastEvent),
+    reportBug(C, BT_lor, LastEvent.EventExpr, Checker,
               "This was not the most recently acquired lock. Possible lock "
               "order reversal");
   }
 }
 
 void PthreadLockChecker::checkDestroyEvent(const EventMarker &LastEvent,
+                                           CheckerKind Checker,
                                            CheckerContext &C) const {
   ProgramStateRef State = C.getState();
 
   const LockStateKind *const LockState =
       State->get<LockStates>(LastEvent.MutexRegion);
 
-  if (!LockState || *LockState == LockStateKind::Destroyed) {
+  if (!LockState || *LockState == LockStateKind::Destroyed)
     return;
-  }
 
   if (*LockState == LockStateKind::Error_DestroyLocked) {
-    reportBug(C, BT_destroylock, LastEvent.EventExpr,
-              detectCheckerKind(LastEvent), "This lock is still locked");
+    reportBug(C, BT_destroylock, LastEvent.EventExpr, Checker,
+              "This lock is still locked");
   } else if (*LockState == LockStateKind::Error_DoubleDestroy)
-    reportBug(C, BT_destroylock, LastEvent.EventExpr,
-              detectCheckerKind(LastEvent),
+    reportBug(C, BT_destroylock, LastEvent.EventExpr, Checker,
               "This lock has already been destroyed");
 }
 
@@ -202,28 +194,29 @@ void PthreadLockChecker::checkPostCall(const CallEvent &Call,
 
   const auto &MTXEvents = State->get<MutexEvents>();
 
-  if (MTXEvents.isEmpty()) {
+  if (MTXEvents.isEmpty())
     return;
-  }
 
   const auto &LastEvent = MTXEvents.getHead();
 
-  if (!ChecksEnabled[detectCheckerKind(LastEvent)])
+  std::optional<CheckerKind> Checker = detectCheckerKind(LastEvent);
+
+  if (!Checker || !ChecksEnabled[*Checker])
     return;
 
   switch (LastEvent.Kind) {
   case EventKind::Init:
-    checkInitEvent(LastEvent, C);
+    checkInitEvent(LastEvent, *Checker, C);
     break;
   case EventKind::Acquire:
   case EventKind::TryAcquire:
-    checkAcquireEvent(LastEvent, C);
+    checkAcquireEvent(LastEvent, *Checker, C);
     break;
   case EventKind::Release:
-    checkReleaseEvent(LastEvent, C);
+    checkReleaseEvent(LastEvent, *Checker, C);
     break;
   case EventKind::Destroy:
-    checkDestroyEvent(LastEvent, C);
+    checkDestroyEvent(LastEvent, *Checker, C);
     break;
   }
 }

>From b5a88464f8da21732fad839d1761135b9a0b387e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Fri, 2 Aug 2024 20:41:45 +0200
Subject: [PATCH 22/30] add events for blockincritical

---
 .../BlockInCriticalSectionChecker.cpp         | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 12b61a36cf934c..9ff543a2fc8d39 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -88,6 +88,25 @@ void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
 // Checker registration
 void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
   mgr.registerChecker<BlockInCriticalSectionChecker>();
+  RegisterEvent(EventDescriptor{
+      mutex_modeling::MakeMemberExtractor({"std", "mutex", "lock"}),
+      EventKind::Acquire, LibraryKind::NotApplicable,
+      SemanticsKind::XNUSemantics});
+  RegisterEvent(EventDescriptor{
+      mutex_modeling::MakeMemberExtractor({"std", "mutex", "unlock"}),
+      EventKind::Release});
+  RegisterEvent(EventDescriptor{
+      mutex_modeling::MakeRAIILockExtractor("lock_guard"), EventKind::Acquire,
+      LibraryKind::NotApplicable, SemanticsKind::XNUSemantics});
+  RegisterEvent(
+      EventDescriptor{mutex_modeling::MakeRAIIReleaseExtractor("lock_guard"),
+                      EventKind::Release});
+  RegisterEvent(EventDescriptor{
+      mutex_modeling::MakeRAIILockExtractor("scoped_lock"), EventKind::Acquire,
+      LibraryKind::NotApplicable, SemanticsKind::XNUSemantics});
+  RegisterEvent(
+      EventDescriptor{mutex_modeling::MakeRAIIReleaseExtractor("scoped_lock"),
+                      EventKind::Release});
 }
 
 bool ento::shouldRegisterBlockInCriticalSectionChecker(

>From 59b522eb154cf867acb3c253a914e6253cf4a265 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Fri, 2 Aug 2024 20:44:42 +0200
Subject: [PATCH 23/30] remove wasinlined limitation

---
 clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
index f9653ad790b873..8a04c51a9e15ce 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
@@ -407,11 +407,6 @@ MutexModeling::handleEvent(const EventDescriptor &Event, const MemRegion *MTX,
 
 void MutexModeling::checkPostCall(const CallEvent &Call,
                                   CheckerContext &C) const {
-  // FIXME: Try to handle cases when the implementation was inlined rather
-  // than just giving up.
-  if (C.wasInlined)
-    return;
-
   ProgramStateRef State = C.getState();
   for (auto &&Event : RegisteredEvents) {
     if (matches(Event.Trigger, Call)) {

>From 70fd567ced9ff010ce57cc95714674f1dc5c4ffd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Wed, 11 Sep 2024 10:52:32 +0200
Subject: [PATCH 24/30] fixup! add trylock stub

---
 clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
index 8a04c51a9e15ce..e0918f86221224 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
@@ -239,9 +239,8 @@ MutexModeling::ModelingResult MutexModeling::handleTryAcquire(
     const EventDescriptor &Event, const MemRegion *MTX, const CallEvent &Call,
     ProgramStateRef State, CheckerContext &C) const {
 
-  ModelingResult Result{State};
   // Bifurcate the state, and allow a mode where the lock acquisition fails.
-  ProgramStateRef LockSucc;
+  ProgramStateRef LockSucc{State};
   SVal RetVal = Call.getReturnValue();
   if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
     ProgramStateRef LockFail;
@@ -264,8 +263,7 @@ MutexModeling::ModelingResult MutexModeling::handleTryAcquire(
   }
 
   // Pass the state where the locking succeeded onwards.
-  Result = onSuccessfullAcquire(MTX, Call, LockSucc, C);
-  return Result;
+  return onSuccessfullAcquire(MTX, Call, LockSucc, C);
 }
 
 MutexModeling::ModelingResult

>From 540c80f5949c50d57e3dacf90e5e4b55016b7cb8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Fri, 13 Sep 2024 23:17:39 +0200
Subject: [PATCH 25/30] always add mark for critical section, not just on first
 lock

---
 .../StaticAnalyzer/Checkers/MutexModeling.cpp | 47 ++++++++++---------
 1 file changed, 25 insertions(+), 22 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
index e0918f86221224..1dc51efb1bd847 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
@@ -181,27 +181,25 @@ MutexModeling::onSuccessfullAcquire(const MemRegion *MTX, const CallEvent &Call,
 
   if (!LockState) {
     Result.State = Result.State->set<LockStates>(MTX, LockStateKind::Locked);
-    Result = markCritSection(Result, MTX, Call, C);
-    return Result;
-  }
-
-  switch (*LockState) {
-  case LockStateKind::Unlocked:
-    Result.State = Result.State->set<LockStates>(MTX, LockStateKind::Locked);
-    Result = markCritSection(Result, MTX, Call, C);
-    break;
-  case LockStateKind::Locked:
-    Result.State =
-        Result.State->set<LockStates>(MTX, LockStateKind::Error_DoubleLock);
-    break;
-  case LockStateKind::Destroyed:
-    Result.State =
-        Result.State->set<LockStates>(MTX, LockStateKind::Error_LockDestroyed);
-    break;
-  default:
-    break;
+  } else {
+    switch (*LockState) {
+    case LockStateKind::Unlocked:
+      Result.State = Result.State->set<LockStates>(MTX, LockStateKind::Locked);
+      break;
+    case LockStateKind::Locked:
+      Result.State =
+          Result.State->set<LockStates>(MTX, LockStateKind::Error_DoubleLock);
+      break;
+    case LockStateKind::Destroyed:
+      Result.State = Result.State->set<LockStates>(
+          MTX, LockStateKind::Error_LockDestroyed);
+      break;
+    default:
+      break;
+    }
   }
 
+  Result = markCritSection(Result, MTX, Call, C);
   return Result;
 }
 
@@ -239,10 +237,13 @@ MutexModeling::ModelingResult MutexModeling::handleTryAcquire(
     const EventDescriptor &Event, const MemRegion *MTX, const CallEvent &Call,
     ProgramStateRef State, CheckerContext &C) const {
 
-  // Bifurcate the state, and allow a mode where the lock acquisition fails.
   ProgramStateRef LockSucc{State};
+  // Bifurcate the state, and allow a mode where the lock acquisition fails.
   SVal RetVal = Call.getReturnValue();
-  if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
+  std::optional<DefinedSVal> DefinedRetVal = RetVal.getAs<DefinedSVal>();
+  // Bifurcating the state is only meaningful if the call was not inlined, but
+  // we can still reason about the return value.
+  if (!C.wasInlined && DefinedRetVal) {
     ProgramStateRef LockFail;
     switch (Event.Semantics) {
     case SemanticsKind::PthreadSemantics:
@@ -254,7 +255,6 @@ MutexModeling::ModelingResult MutexModeling::handleTryAcquire(
     default:
       llvm_unreachable("Unknown TryLock locking semantics");
     }
-    assert(LockFail && LockSucc && "Bifurcation point in ExplodedGraph");
 
     // This is the bifurcation point in the ExplodedGraph, we do not need to
     // return the new ExplodedGraph node because we do not plan on building this
@@ -262,6 +262,9 @@ MutexModeling::ModelingResult MutexModeling::handleTryAcquire(
     C.addTransition(LockFail);
   }
 
+  if (!LockSucc)
+    LockSucc = State;
+
   // Pass the state where the locking succeeded onwards.
   return onSuccessfullAcquire(MTX, Call, LockSucc, C);
 }

>From 07f20911ec60570cadb406ff5a7b961b260a0561 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Fri, 13 Sep 2024 23:17:52 +0200
Subject: [PATCH 26/30] add modeling for unique_lock

---
 .../Checkers/BlockInCriticalSectionChecker.cpp              | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 9ff543a2fc8d39..9a48f38dee4050 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -101,6 +101,12 @@ void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
   RegisterEvent(
       EventDescriptor{mutex_modeling::MakeRAIIReleaseExtractor("lock_guard"),
                       EventKind::Release});
+  RegisterEvent(EventDescriptor{
+      mutex_modeling::MakeRAIILockExtractor("unique_lock"), EventKind::Acquire,
+      LibraryKind::NotApplicable, SemanticsKind::XNUSemantics});
+  RegisterEvent(
+      EventDescriptor{mutex_modeling::MakeRAIIReleaseExtractor("unique_lock"),
+                      EventKind::Release});
   RegisterEvent(EventDescriptor{
       mutex_modeling::MakeRAIILockExtractor("scoped_lock"), EventKind::Acquire,
       LibraryKind::NotApplicable, SemanticsKind::XNUSemantics});

>From 002e5e02512d52de18ec1a1007275bdfa9c2314f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Sat, 14 Sep 2024 00:21:31 +0200
Subject: [PATCH 27/30] silence warning about not handling all event kinds

---
 clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
index 1dc51efb1bd847..11ff22bd009c6b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MutexModeling.cpp
@@ -403,6 +403,8 @@ MutexModeling::handleEvent(const EventDescriptor &Event, const MemRegion *MTX,
     return handleRelease(Event, MTX, Call, State, C);
   case EventKind::Destroy:
     return handleDestroy(Event, MTX, Call, State, C);
+  default:
+    llvm_unreachable("Unknown event kind");
   }
 }
 

>From 079f2f0125e991581eaec250c628c1dc35bef71c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Fri, 2 Aug 2024 20:45:05 +0200
Subject: [PATCH 28/30] [test fix-testcase] fix block_in_critical_section
 testcase

the signature of mtx_timedlock is different according to the
documentation
---
 clang/test/Analysis/block-in-critical-section.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/clang/test/Analysis/block-in-critical-section.cpp b/clang/test/Analysis/block-in-critical-section.cpp
index ee9a708f231a86..5424e8eb409f00 100644
--- a/clang/test/Analysis/block-in-critical-section.cpp
+++ b/clang/test/Analysis/block-in-critical-section.cpp
@@ -41,8 +41,9 @@ int pthread_mutex_trylock(pthread_mutex_t *mutex);
 int pthread_mutex_unlock(pthread_mutex_t *mutex);
 
 struct mtx_t;
+struct timespec;
 int mtx_lock(mtx_t *mutex);
-int mtx_timedlock(mtx_t *mutex);
+int mtx_timedlock(mtx_t *mutex, const struct timespec *ts);
 int mtx_trylock(mtx_t *mutex);
 int mtx_unlock(mtx_t *mutex);
 
@@ -100,7 +101,7 @@ void testBlockInCriticalSectionWithPthreadMutex(pthread_mutex_t *mutex) {
   pthread_mutex_unlock(mutex);
 }
 
-void testBlockInCriticalSectionC11Locks(mtx_t *mutex) {
+void testBlockInCriticalSectionC11Locks(mtx_t *mutex, timespec *ts) {
   mtx_lock(mutex); // expected-note 5{{Entering critical section here}}
   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}}
@@ -114,7 +115,7 @@ void testBlockInCriticalSectionC11Locks(mtx_t *mutex) {
           // expected-note at -1 {{Call to blocking function 'recv' inside of critical section}}
   mtx_unlock(mutex);
 
-  mtx_timedlock(mutex); // expected-note 5{{Entering critical section here}}
+  mtx_timedlock(mutex, ts); // expected-note 5{{Entering critical section here}}
   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}}
   getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}

>From acae3638112af0d12dc8d4ccadbbc0afdbfa5e26 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Wed, 11 Sep 2024 10:36:02 +0200
Subject: [PATCH 29/30] [test] update pthreadlock_state printing tests to
 contain the list of events

---
 clang/test/Analysis/pthreadlock_state.c       | 53 +++++++++++++++++--
 .../Analysis/pthreadlock_state_nottracked.c   |  5 ++
 2 files changed, 53 insertions(+), 5 deletions(-)

diff --git a/clang/test/Analysis/pthreadlock_state.c b/clang/test/Analysis/pthreadlock_state.c
index ffbb33f6beaf7a..eab1b468aceeb9 100644
--- a/clang/test/Analysis/pthreadlock_state.c
+++ b/clang/test/Analysis/pthreadlock_state.c
@@ -15,24 +15,49 @@ void test(void) {
   pthread_mutex_init(&mtx, NULL);
   clang_analyzer_printState();
   // CHECK:    { "checker": "alpha.core.PthreadLockBase", "messages": [
-  // CHECK-NEXT:      "Mutex states:",
-  // CHECK-NEXT:      "mtx: unlocked",
+  // CHECK-NEXT:      "Mutex event:", 
+  // CHECK-NEXT:      "Kind: : Init", 
+  // CHECK-NEXT:      "Semantics: NotApplicable", 
+  // CHECK-NEXT:      "Library: Pthread", 
+  // CHECK-NEXT:      "Mutex region: mtx", 
+  // CHECK-NEXT:      "Mutex states:", 
+  // CHECK-NEXT:      "mtx: unlocked", 
   // CHECK-NEXT:      ""
   // CHECK-NEXT:    ]}
 
   pthread_mutex_lock(&mtx);
   clang_analyzer_printState();
   // CHECK:    { "checker": "alpha.core.PthreadLockBase", "messages": [
+  // CHECK-NEXT:      "Mutex event:", 
+  // CHECK-NEXT:      "Kind: : Acquire", 
+  // CHECK-NEXT:      "Semantics: PthreadSemantics", 
+  // CHECK-NEXT:      "Library: Pthread", 
+  // CHECK-NEXT:      "Mutex region: mtx", 
+  // CHECK-NEXT:      "Kind: : Init", 
+  // CHECK-NEXT:      "Semantics: NotApplicable", 
+  // CHECK-NEXT:      "Library: Pthread", 
+  // CHECK-NEXT:      "Mutex region: mtx", 
   // CHECK-NEXT:      "Mutex states:",
   // CHECK-NEXT:      "mtx: locked",
-  // CHECK-NEXT:      "Mutex lock order:",
-  // CHECK-NEXT:      "mtx",
   // CHECK-NEXT:      ""
   // CHECK-NEXT:    ]}
 
   pthread_mutex_unlock(&mtx);
   clang_analyzer_printState();
   // CHECK:    { "checker": "alpha.core.PthreadLockBase", "messages": [
+  // CHECK-NEXT:      "Mutex event:", 
+  // CHECK-NEXT:      "Kind: : Release", 
+  // CHECK-NEXT:      "Semantics: NotApplicable",
+  // CHECK-NEXT:      "Library: Pthread", 
+  // CHECK-NEXT:      "Mutex region: mtx", 
+  // CHECK-NEXT:      "Kind: : Acquire", 
+  // CHECK-NEXT:      "Semantics: PthreadSemantics", 
+  // CHECK-NEXT:      "Library: Pthread", 
+  // CHECK-NEXT:      "Mutex region: mtx", 
+  // CHECK-NEXT:      "Kind: : Init", 
+  // CHECK-NEXT:      "Semantics: NotApplicable", 
+  // CHECK-NEXT:      "Library: Pthread", 
+  // CHECK-NEXT:      "Mutex region: mtx", 
   // CHECK-NEXT:      "Mutex states:",
   // CHECK-NEXT:      "mtx: unlocked",
   // CHECK-NEXT:      ""
@@ -41,6 +66,23 @@ void test(void) {
   int ret = pthread_mutex_destroy(&mtx);
   clang_analyzer_printState();
   // CHECK:    { "checker": "alpha.core.PthreadLockBase", "messages": [
+  // CHECK-NEXT:      "Mutex event:", 
+  // CHECK-NEXT:      "Kind: : Destroy", 
+  // CHECK-NEXT:      "Semantics: PthreadSemantics",
+  // CHECK-NEXT:      "Library: Pthread", 
+  // CHECK-NEXT:      "Mutex region: mtx", 
+  // CHECK-NEXT:      "Kind: : Release", 
+  // CHECK-NEXT:      "Semantics: NotApplicable",
+  // CHECK-NEXT:      "Library: Pthread", 
+  // CHECK-NEXT:      "Mutex region: mtx", 
+  // CHECK-NEXT:      "Kind: : Acquire", 
+  // CHECK-NEXT:      "Semantics: PthreadSemantics", 
+  // CHECK-NEXT:      "Library: Pthread", 
+  // CHECK-NEXT:      "Mutex region: mtx", 
+  // CHECK-NEXT:      "Kind: : Init", 
+  // CHECK-NEXT:      "Semantics: NotApplicable", 
+  // CHECK-NEXT:      "Library: Pthread", 
+  // CHECK-NEXT:      "Mutex region: mtx", 
   // CHECK-NEXT:      "Mutex states:",
   // CHECK-NEXT:      "mtx: unlocked, possibly destroyed",
   // CHECK-NEXT:      "Mutexes in unresolved possibly destroyed state:",
@@ -51,9 +93,10 @@ void test(void) {
   if (ret)
     return;
 
+  // Assert that the 'possibly destroyed' state is now resolved to 'destroyed'.
   clang_analyzer_printState();
   // CHECK:    { "checker": "alpha.core.PthreadLockBase", "messages": [
-  // CHECK-NEXT:      "Mutex states:",
+  // CHECK:           "Mutex states:",
   // CHECK-NEXT:      "mtx: destroyed",
   // CHECK-NEXT:      ""
   // CHECK-NEXT:    ]}
diff --git a/clang/test/Analysis/pthreadlock_state_nottracked.c b/clang/test/Analysis/pthreadlock_state_nottracked.c
index 44023e7fc15c47..7788377069eb88 100644
--- a/clang/test/Analysis/pthreadlock_state_nottracked.c
+++ b/clang/test/Analysis/pthreadlock_state_nottracked.c
@@ -10,6 +10,11 @@ void test(pthread_mutex_t *mtx) {
   int ret = pthread_mutex_destroy(mtx);
   clang_analyzer_printState();
   // CHECK:    { "checker": "alpha.core.PthreadLockBase", "messages": [
+  // CHECK-NEXT:      "Mutex event:", 
+  // CHECK-NEXT:      "Kind: : Destroy", 
+  // CHECK-NEXT:      "Semantics: PthreadSemantics", 
+  // CHECK-NEXT:      "Library: Pthread", 
+  // CHECK-NEXT:      "Mutex region: SymRegion{reg_$[[REG:[0-9]+]]<pthread_mutex_t * mtx>}", 
   // CHECK-NEXT:      "Mutex states:",
   // CHECK-NEXT:      "SymRegion{reg_$[[REG:[0-9]+]]<pthread_mutex_t * mtx>}: not tracked, possibly destroyed",
   // CHECK-NEXT:      "Mutexes in unresolved possibly destroyed state:",

>From b660744080b432bd70b75f0177fbe6445c7defee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Fri, 13 Sep 2024 23:17:12 +0200
Subject: [PATCH 30/30] [test] false positive is fixed

---
 clang/test/Analysis/block-in-critical-section.cpp | 14 ++++----------
 1 file changed, 4 insertions(+), 10 deletions(-)

diff --git a/clang/test/Analysis/block-in-critical-section.cpp b/clang/test/Analysis/block-in-critical-section.cpp
index 5424e8eb409f00..cd5fa1edcbea71 100644
--- a/clang/test/Analysis/block-in-critical-section.cpp
+++ b/clang/test/Analysis/block-in-critical-section.cpp
@@ -294,19 +294,13 @@ void testBlockInCriticalSectionUniqueLockNested() {
   sleep(1); // no-warning
 }
 
-void testTrylockCurrentlyFalsePositive(pthread_mutex_t *m) {
-                                       // expected-note at +4 {{Assuming the condition is true}}
-                                       // expected-note at +3 {{Taking true branch}}
-                                       // expected-note at +2 {{Assuming the condition is false}}
-                                       // expected-note at +1 {{Taking false branch}}
-  if (pthread_mutex_trylock(m) == 0) { // expected-note 2 {{Entering critical section here}}
-                                       // FIXME: we are entering the critical section only in the true branch
+void testTrylockStateSplitting(pthread_mutex_t *m) {
+                                       // expected-note at +1 {{Taking true branch}}
+  if (pthread_mutex_trylock(m) == 0) { // expected-note {{Entering critical section here}}
     sleep(10); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
                // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
     pthread_mutex_unlock(m);
   } else {
-    sleep(10); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
-               // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
-               // FIXME: this is a false positive, the lock was not acquired
+    sleep(10); // no-warning
   }
 }



More information about the cfe-commits mailing list