[libcxx-commits] [libcxx] [libc++][hardening] In production hardening modes, trap rather than abort (PR #78561)

Konstantin Varlamov via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jan 19 03:20:55 PST 2024


https://github.com/var-const updated https://github.com/llvm/llvm-project/pull/78561

>From 9308d0520ea99f806682b62b1659f4c327da1ca6 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconsteq at gmail.com>
Date: Thu, 18 Jan 2024 02:38:55 -0800
Subject: [PATCH 1/9] [libc++][hardening] In production hardening modes, trap
 instead of abort

TODO
---
 ...tomize_verbose_abort.compile-time.pass.cpp |   1 +
 ...customize_verbose_abort.link-time.pass.cpp |   1 +
 .../assertions/default_verbose_abort.pass.cpp |   1 +
 libcxx/test/support/check_assertion.h         | 401 ++++++++++++------
 .../test_check_assertion.pass.cpp             | 111 +++--
 .../vendor/llvm/default_assertion_handler.in  |  11 +-
 6 files changed, 357 insertions(+), 169 deletions(-)

diff --git a/libcxx/test/libcxx/assertions/customize_verbose_abort.compile-time.pass.cpp b/libcxx/test/libcxx/assertions/customize_verbose_abort.compile-time.pass.cpp
index d09881e8cecb9f..d176d8a12875e6 100644
--- a/libcxx/test/libcxx/assertions/customize_verbose_abort.compile-time.pass.cpp
+++ b/libcxx/test/libcxx/assertions/customize_verbose_abort.compile-time.pass.cpp
@@ -8,6 +8,7 @@
 
 // This compile-time customization requires cross-file macros, which doesn't work with modules.
 // UNSUPPORTED: clang-modules-build
+// REQUIRES: libcpp-hardening-mode=debug
 
 // Make sure that we can customize the verbose termination function at compile-time by
 // defining _LIBCPP_VERBOSE_ABORT ourselves. Note that this does not have any
diff --git a/libcxx/test/libcxx/assertions/customize_verbose_abort.link-time.pass.cpp b/libcxx/test/libcxx/assertions/customize_verbose_abort.link-time.pass.cpp
index 219c43874e77db..c82aaea6ba30df 100644
--- a/libcxx/test/libcxx/assertions/customize_verbose_abort.link-time.pass.cpp
+++ b/libcxx/test/libcxx/assertions/customize_verbose_abort.link-time.pass.cpp
@@ -11,6 +11,7 @@
 // We flag uses of the verbose termination function in older dylibs at compile-time to avoid runtime
 // failures when back-deploying.
 // XFAIL: availability-verbose_abort-missing
+// REQUIRES: libcpp-hardening-mode=debug
 
 #include <cstdlib>
 
diff --git a/libcxx/test/libcxx/assertions/default_verbose_abort.pass.cpp b/libcxx/test/libcxx/assertions/default_verbose_abort.pass.cpp
index 870f43da4b8f02..29ef10016c1a37 100644
--- a/libcxx/test/libcxx/assertions/default_verbose_abort.pass.cpp
+++ b/libcxx/test/libcxx/assertions/default_verbose_abort.pass.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // Test that the default verbose termination function aborts the program.
+// REQUIRES: libcpp-hardening-mode=debug
 
 #include <csignal>
 #include <cstdlib>
diff --git a/libcxx/test/support/check_assertion.h b/libcxx/test/support/check_assertion.h
index 83a46548fa9250..8e1493b70a66a6 100644
--- a/libcxx/test/support/check_assertion.h
+++ b/libcxx/test/support/check_assertion.h
@@ -16,6 +16,7 @@
 #include <cstdlib>
 #include <exception>
 #include <regex>
+#include <sstream>
 #include <string>
 #include <string_view>
 #include <utility>
@@ -27,37 +28,70 @@
 #include "test_allocator.h"
 
 #if TEST_STD_VER < 11
-# error "C++11 or greater is required to use this header"
+#  error "C++11 or greater is required to use this header"
 #endif
 
 struct AssertionInfoMatcher {
-  static const int any_line = -1;
+  // When printing the assertion message to `stderr`, delimit it with a marker to make it easier to match the message
+  // later.
+  static constexpr const char* Marker = "###";
+
+  static const int any_line             = -1;
   static constexpr const char* any_file = "*";
-  static constexpr const char* any_msg = "*";
+  static constexpr const char* any_msg  = "*";
 
-  constexpr AssertionInfoMatcher() : is_empty_(true), msg_(any_msg, __builtin_strlen(any_msg)), file_(any_file, __builtin_strlen(any_file)), line_(any_line) { }
+  constexpr AssertionInfoMatcher()
+      : msg_(any_msg, __builtin_strlen(any_msg)), file_(any_file, __builtin_strlen(any_file)), line_(any_line) {}
   constexpr AssertionInfoMatcher(const char* msg, const char* file = any_file, int line = any_line)
-    : is_empty_(false), msg_(msg, __builtin_strlen(msg)), file_(file, __builtin_strlen(file)), line_(line) {}
+      : is_empty_(false), msg_(msg, __builtin_strlen(msg)), file_(file, __builtin_strlen(file)), line_(line) {}
+
+  bool CheckMatchInOutput(const std::string& output, std::string& error) const {
+    // Extract information from the error message. This has to stay synchronized with how we format assertions in the
+    // library.
+    std::regex message_format(".*###\\n(.*):(\\d+): assertion (.*) failed: (.*)\\n###");
+
+    std::smatch match_result;
+    bool has_match = std::regex_match(output, match_result, message_format);
+    assert(has_match);
+    assert(match_result.size() == 5);
+
+    const std::string& file = match_result[1];
+    int line                = std::stoi(match_result[2]);
+    // Omitting `expression` in `match_result[3]`
+    const std::string& failure_reason = match_result[4];
+
+    bool result = Matches(file, line, failure_reason);
+    if (!result) {
+      error = FormatMatchingError(file, line, failure_reason);
+    }
+    return result;
+  }
 
-  bool Matches(char const* file, int line, char const* message) const {
-    assert(!empty() && "empty matcher");
+  bool Matches(const std::string& file, int line, const std::string& message) const {
+    assert(!empty() && "Empty matcher");
+    return CheckLineMatches(line) && CheckFileMatches(file) && CheckMessageMatches(message);
+  }
 
-    if (CheckLineMatches(line) && CheckFileMatches(file) && CheckMessageMatches(message))
-        return true;
-    // Write to stdout because that's the file descriptor captured by the parent
-    // process.
-    std::printf("Failed to match assertion info!\n%s\nVS\n%s:%d (%s)\n", ToString().data(), file, line, message);
-    return false;
+  std::string FormatMatchingError(const std::string& file, int line, const std::string& message) const {
+    std::stringstream output;
+    output                                                                 //
+        << "Expected message:   '" << msg_.data() << "'\n"                 //
+        << "Actual message:     '" << message.c_str() << "'\n"             //
+        << "Expected location:   " << FormatLocation(file_, line_) << "\n" //
+        << "Actual location:     " << FormatLocation(file, line) << "\n";
+    return output.str();
   }
 
-  std::string ToString() const {
-    std::string result = "msg = \""; result += msg_; result += "\"\n";
-    result += "line = " + (line_ == any_line ? "'*'" : std::to_string(line_)) + "\n";
-    result += "file = " + (file_ == any_file ? "'*'" : std::string(file_));
+  static std::string FormatLocation(std::string_view file, int line) {
+    std::string result;
+    result += (file == any_file ? "*" : std::string(file)) + ":";
+    result += (line == any_line ? "*" : std::to_string(line));
     return result;
   }
 
   bool empty() const { return is_empty_; }
+  bool IsAnyMatcher() const { return msg_ == any_msg && file_ == any_file && line_ == any_line; }
+
 private:
   bool CheckLineMatches(int got_line) const {
     if (line_ == any_line)
@@ -90,10 +124,13 @@ struct AssertionInfoMatcher {
     std::size_t found_at = got_msg.find(msg_);
     if (found_at == std::string_view::npos)
       return false;
-    return found_at == 0 && got_msg.size() == msg_.size();
+    // Allow any match
+    return true;
   }
+
 private:
-  bool is_empty_;
+  bool is_empty_ = true;
+  ;
   std::string_view msg_;
   std::string_view file_;
   int line_;
@@ -101,44 +138,175 @@ struct AssertionInfoMatcher {
 
 static constexpr AssertionInfoMatcher AnyMatcher(AssertionInfoMatcher::any_msg);
 
-inline AssertionInfoMatcher& GlobalMatcher() {
-  static AssertionInfoMatcher GMatch;
-  return GMatch;
+enum class DeathCause {
+  // Valid causes
+  VerboseAbort = 1,
+  StdTerminate,
+  Trap,
+  // Invalid causes
+  DidNotDie,
+  SetupFailure,
+  Unknown
+};
+
+bool IsValidCause(DeathCause cause) {
+  switch (cause) {
+  case DeathCause::VerboseAbort:
+  case DeathCause::StdTerminate:
+  case DeathCause::Trap:
+    return true;
+  default:
+    return false;
+  }
 }
 
-struct DeathTest {
-  enum ResultKind {
-    RK_DidNotDie, RK_MatchFound, RK_MatchFailure, RK_Terminate, RK_SetupFailure, RK_Unknown
-  };
+std::string ToString(DeathCause cause) {
+  switch (cause) {
+  case DeathCause::VerboseAbort:
+    return "verbose abort";
+  case DeathCause::StdTerminate:
+    return "`std::terminate`";
+  case DeathCause::Trap:
+    return "trap";
+  case DeathCause::DidNotDie:
+    return "<invalid cause (did not die)>";
+  case DeathCause::SetupFailure:
+    return "<invalid cause (setup failure)>";
+  case DeathCause::Unknown:
+    return "<invalid cause (unknown)>";
+  }
+}
 
-  static const char* ResultKindToString(ResultKind RK) {
-#define CASE(K) case K: return #K
-    switch (RK) {
-    CASE(RK_MatchFailure);
-    CASE(RK_DidNotDie);
-    CASE(RK_SetupFailure);
-    CASE(RK_MatchFound);
-    CASE(RK_Unknown);
-    CASE(RK_Terminate);
-    }
-    return "not a result kind";
+TEST_NORETURN void StopChildProcess(DeathCause cause) { std::exit(static_cast<int>(cause)); }
+
+DeathCause ConvertToDeathCause(int val) {
+  if (val < static_cast<int>(DeathCause::VerboseAbort) || val > static_cast<int>(DeathCause::Unknown)) {
+    return DeathCause::Unknown;
   }
+  return static_cast<DeathCause>(val);
+}
+
+enum class Outcome {
+  Success,
+  UnexpectedCause,
+  UnexpectedAbortMessage,
+  InvalidCause,
+};
 
-  static bool IsValidResultKind(int val) {
-    return val >= RK_DidNotDie && val <= RK_Unknown;
+std::string ToString(Outcome outcome) {
+  switch (outcome) {
+  case Outcome::Success:
+    return "success";
+  case Outcome::UnexpectedCause:
+    return "unexpected death cause";
+  case Outcome::UnexpectedAbortMessage:
+    return "unexpected verbose abort message";
+  case Outcome::InvalidCause:
+    return "invalid death cause";
   }
+}
+
+class DeathTestResult {
+  public:
+    DeathTestResult() = default;
+    DeathTestResult(Outcome set_outcome, DeathCause set_cause, const std::string& set_failure_description = "")
+        : outcome_(set_outcome), cause_(set_cause), failure_description_(set_failure_description) {}
+
+    bool success() const { return outcome() == Outcome::Success; }
+    Outcome outcome() const { return outcome_; }
+    DeathCause cause() const { return cause_; }
+    const std::string& failure_description() const { return failure_description_; }
+
+  private:
+    Outcome outcome_ = Outcome::Success;
+    DeathCause cause_ = DeathCause::Unknown;
+    std::string failure_description_;
+};
 
-  DeathTest(AssertionInfoMatcher const& Matcher) : matcher_(Matcher) {}
+class DeathTest {
+  public:
+  DeathTest()                            = default;
+  DeathTest(DeathTest const&)            = delete;
+  DeathTest& operator=(DeathTest const&) = delete;
 
   template <class Func>
-  ResultKind Run(Func&& f) {
+  DeathTestResult Run(DeathCause expected_cause, Func&& func, const AssertionInfoMatcher& matcher) {
+    std::set_terminate([] {
+      StopChildProcess(DeathCause::StdTerminate);
+    });
+
+    DeathCause cause = Run(func);
+
+    auto DescribeDeathCauseMismatch = [](DeathCause expected, DeathCause actual) {
+      std::stringstream output;
+      output                                                    //
+          << "Child died, but with a different death cause\n"   //
+          << "Expected cause:   " << ToString(expected) << "\n" //
+          << "Actual cause:     " << ToString(actual) << "\n";
+      return output.str();
+    };
+
+    switch (cause) {
+    case DeathCause::StdTerminate:
+    case DeathCause::Trap:
+      if (expected_cause != cause) {
+        auto failure_description = DescribeDeathCauseMismatch(expected_cause, cause);
+        return DeathTestResult(Outcome::UnexpectedCause, cause, failure_description);
+      }
+      return DeathTestResult(Outcome::Success, cause);
+
+    case DeathCause::VerboseAbort: {
+      if (expected_cause != cause) {
+        auto failure_description = DescribeDeathCauseMismatch(expected_cause, cause);
+        return DeathTestResult(Outcome::UnexpectedCause, cause, failure_description);
+      }
+
+      std::string maybe_error;
+      if (matcher.CheckMatchInOutput(getChildStdErr(), maybe_error)) {
+        return DeathTestResult(Outcome::Success, cause);
+      }
+      auto failure_description = std::string("Child died, but with a different verbose abort message\n") + maybe_error;
+      return DeathTestResult(Outcome::UnexpectedAbortMessage, cause, failure_description);
+    }
+
+    // Invalid causes.
+    case DeathCause::DidNotDie:
+      return DeathTestResult(Outcome::InvalidCause, cause, "Child did not die");
+    case DeathCause::SetupFailure:
+      return DeathTestResult(Outcome::InvalidCause, cause, "Child failed to set up test environment");
+    case DeathCause::Unknown:
+      return DeathTestResult(Outcome::InvalidCause, cause, "Cause unknown");
+    }
+
+    assert(false && "Unreachable");
+  }
+
+  void PrintFailureDetails(std::string_view failure_description, std::string_view stmt, DeathCause cause) const {
+    std::fprintf(
+        stderr, "Failure: EXPECT_DEATH( %s ) failed!\n(reason: %s)\n\n", stmt.data(), failure_description.data());
+
+    if (cause != DeathCause::Unknown) {
+      std::fprintf(stderr, "child exit code: %d\n", getChildExitCode());
+    }
+    std::fprintf(stderr, "---------- standard err ----------\n%s", getChildStdErr().c_str());
+    std::fprintf(stderr, "\n----------------------------------\n");
+    std::fprintf(stderr, "---------- standard out ----------\n%s", getChildStdOut().c_str());
+    std::fprintf(stderr, "\n----------------------------------\n");
+  };
+
+  int getChildExitCode() const { return exit_code_; }
+  std::string const& getChildStdOut() const { return stdout_from_child_; }
+  std::string const& getChildStdErr() const { return stderr_from_child_; }
+
+private:
+  template <class Func>
+  DeathCause Run(Func&& f) {
     int pipe_res = pipe(stdout_pipe_fd_);
     assert(pipe_res != -1 && "failed to create pipe");
     pipe_res = pipe(stderr_pipe_fd_);
     assert(pipe_res != -1 && "failed to create pipe");
     pid_t child_pid = fork();
-    assert(child_pid != -1 &&
-        "failed to fork a process to perform a death test");
+    assert(child_pid != -1 && "failed to fork a process to perform a death test");
     child_pid_ = child_pid;
     if (child_pid_ == 0) {
       RunForChild(std::forward<Func>(f));
@@ -147,10 +315,6 @@ struct DeathTest {
     return RunForParent();
   }
 
-  int getChildExitCode() const { return exit_code_; }
-  std::string const& getChildStdOut() const { return stdout_from_child_; }
-  std::string const& getChildStdErr() const { return stderr_from_child_; }
-private:
   template <class Func>
   TEST_NORETURN void RunForChild(Func&& f) {
     close(GetStdOutReadFD()); // don't need to read from the pipe in the child.
@@ -158,14 +322,13 @@ struct DeathTest {
     auto DupFD = [](int DestFD, int TargetFD) {
       int dup_result = dup2(DestFD, TargetFD);
       if (dup_result == -1)
-        std::exit(RK_SetupFailure);
+        StopChildProcess(DeathCause::SetupFailure);
     };
     DupFD(GetStdOutWriteFD(), STDOUT_FILENO);
     DupFD(GetStdErrWriteFD(), STDERR_FILENO);
 
-    GlobalMatcher() = matcher_;
     f();
-    std::exit(RK_DidNotDie);
+    StopChildProcess(DeathCause::DidNotDie);
   }
 
   static std::string ReadChildIOUntilEnd(int FD) {
@@ -190,7 +353,7 @@ struct DeathTest {
     close(GetStdErrReadFD());
   }
 
-  ResultKind RunForParent() {
+  DeathCause RunForParent() {
     CaptureIOFromChild();
 
     int status_value;
@@ -199,35 +362,29 @@ struct DeathTest {
 
     if (WIFEXITED(status_value)) {
       exit_code_ = WEXITSTATUS(status_value);
-      if (!IsValidResultKind(exit_code_))
-        return RK_Unknown;
-      return static_cast<ResultKind>(exit_code_);
+      return ConvertToDeathCause(exit_code_);
     }
-    return RK_Unknown;
-  }
 
-  DeathTest(DeathTest const&) = delete;
-  DeathTest& operator=(DeathTest const&) = delete;
+    if (WIFSIGNALED(status_value)) {
+      exit_code_ = WTERMSIG(status_value);
+      if (exit_code_ == SIGILL || exit_code_ == SIGTRAP) {
+        return DeathCause::Trap;
+      }
+    }
 
-  int GetStdOutReadFD() const {
-    return stdout_pipe_fd_[0];
+    return DeathCause::Unknown;
   }
 
-  int GetStdOutWriteFD() const {
-    return stdout_pipe_fd_[1];
-  }
+  int GetStdOutReadFD() const { return stdout_pipe_fd_[0]; }
 
-  int GetStdErrReadFD() const {
-    return stderr_pipe_fd_[0];
-  }
+  int GetStdOutWriteFD() const { return stdout_pipe_fd_[1]; }
+
+  int GetStdErrReadFD() const { return stderr_pipe_fd_[0]; }
+
+  int GetStdErrWriteFD() const { return stderr_pipe_fd_[1]; }
 
-  int GetStdErrWriteFD() const {
-    return stderr_pipe_fd_[1];
-  }
-private:
-  AssertionInfoMatcher matcher_;
   pid_t child_pid_ = -1;
-  int exit_code_ = -1;
+  int exit_code_   = -1;
   int stdout_pipe_fd_[2];
   int stderr_pipe_fd_[2];
   std::string stdout_from_child_;
@@ -235,82 +392,56 @@ struct DeathTest {
 };
 
 #ifdef _LIBCPP_VERSION
-void std::__libcpp_verbose_abort(char const* printf_format, ...) {
-  // Extract information from the error message. This has to stay synchronized with how we format assertions in the
-  // library.
+void std::__libcpp_verbose_abort(char const* format, ...) {
   va_list args;
-  va_start(args, printf_format);
-  char const* message = va_arg(args, char const*);
-
-  std::regex message_format("(.*):(\\d+): assertion (.*) failed: (.*)\\n");
+  va_start(args, format);
 
-  std::cmatch match_result;
-  bool has_match = std::regex_match(message, match_result, message_format);
-  assert(has_match);
-  assert(match_result.size() == 5);
+  std::fprintf(stderr, "%s\n", AssertionInfoMatcher::Marker);
+  std::vfprintf(stderr, format, args);
+  std::fprintf(stderr, "%s", AssertionInfoMatcher::Marker);
 
-  std::string file = match_result[1];
-  int line         = std::stoi(match_result[2]);
-  // Omitting `expression` in `match_result[3]`
-  std::string failure_reason = match_result[4];
+  va_end(args);
 
-  if (GlobalMatcher().Matches(file.c_str(), line, failure_reason.c_str())) {
-    std::exit(DeathTest::RK_MatchFound);
-  }
-  std::exit(DeathTest::RK_MatchFailure);
+  StopChildProcess(DeathCause::VerboseAbort);
 }
 #endif // _LIBCPP_VERSION
 
-[[noreturn]] inline void terminate_handler() {
-  std::exit(DeathTest::RK_Terminate);
-}
-
 template <class Func>
-inline bool ExpectDeath(const char* stmt, Func&& func, AssertionInfoMatcher Matcher) {
-  std::set_terminate(terminate_handler);
-  DeathTest DT(Matcher);
-  DeathTest::ResultKind RK = DT.Run(func);
-  auto OnFailure = [&](const char* msg) {
-    std::fprintf(stderr, "EXPECT_DEATH( %s ) failed! (%s)\n\n", stmt, msg);
-    if (RK != DeathTest::RK_Unknown) {
-      std::fprintf(stderr, "child exit code: %d\n", DT.getChildExitCode());
-    }
-    if (!DT.getChildStdErr().empty()) {
-      std::fprintf(stderr, "---------- standard err ----------\n%s\n", DT.getChildStdErr().c_str());
-    }
-    if (!DT.getChildStdOut().empty()) {
-      std::fprintf(stderr, "---------- standard out ----------\n%s\n", DT.getChildStdOut().c_str());
-    }
-    return false;
-  };
-  switch (RK) {
-  case DeathTest::RK_MatchFound:
-  case DeathTest::RK_Terminate:
-    return true;
-  case DeathTest::RK_SetupFailure:
-    return OnFailure("child failed to setup test environment");
-  case DeathTest::RK_Unknown:
-      return OnFailure("reason unknown");
-  case DeathTest::RK_DidNotDie:
-      return OnFailure("child did not die");
-  case DeathTest::RK_MatchFailure:
-      return OnFailure("matcher failed");
+inline bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func, AssertionInfoMatcher matcher) {
+  assert(IsValidCause(expected_cause));
+
+  DeathTest test_case;
+  DeathTestResult test_result = test_case.Run(expected_cause, func, matcher);
+  if (!test_result.success()) {
+    test_case.PrintFailureDetails(test_result.failure_description(), stmt, test_result.cause());
   }
-  assert(false && "unreachable");
+
+  return test_result.success();
 }
 
 template <class Func>
-inline bool ExpectDeath(const char* stmt, Func&& func) {
-  return ExpectDeath(stmt, func, AnyMatcher);
+inline bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func) {
+  return ExpectDeath(expected_cause, stmt, func, AnyMatcher);
 }
 
-/// Assert that the specified expression throws a libc++ debug exception.
-#define EXPECT_DEATH(...) assert((ExpectDeath(#__VA_ARGS__, [&]() { __VA_ARGS__; } )))
-
-#define EXPECT_STD_TERMINATE(...) assert(ExpectDeath(#__VA_ARGS__, __VA_ARGS__))
-
-#define EXPECT_DEATH_MATCHES(Matcher, ...) assert((ExpectDeath(#__VA_ARGS__, [&]() { __VA_ARGS__; }, Matcher)))
-
-#define TEST_LIBCPP_ASSERT_FAILURE(expr, message) assert((ExpectDeath(#expr, [&]() { (void)(expr); }, AssertionInfoMatcher(message))))
+// clang-format off
+
+/// Assert that the specified expression aborts with the expected cause and, optionally, error message.
+#define EXPECT_DEATH(...)                         \
+    assert(( ExpectDeath(DeathCause::VerboseAbort, #__VA_ARGS__, [&]() { __VA_ARGS__; } ) ))
+#define EXPECT_DEATH_MATCHES(matcher, ...)        \
+    assert(( ExpectDeath(DeathCause::VerboseAbort, #__VA_ARGS__, [&]() { __VA_ARGS__; }, matcher) ))
+#define EXPECT_STD_TERMINATE(...)                 \
+    assert(  ExpectDeath(DeathCause::StdTerminate, #__VA_ARGS__, __VA_ARGS__)  )
+
+#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+#define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
+    assert(( ExpectDeath(DeathCause::VerboseAbort, #expr, [&]() { (void)(expr); }, AssertionInfoMatcher(message)) ))
+#else
+#define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
+    assert(( ExpectDeath(DeathCause::Trap,         #expr, [&]() { (void)(expr); }) ))
+#endif // _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+
+// clang-format on
 
 #endif // TEST_SUPPORT_CHECK_ASSERTION_H
diff --git a/libcxx/test/support/test.support/test_check_assertion.pass.cpp b/libcxx/test/support/test.support/test_check_assertion.pass.cpp
index 7cf0e0966ce89d..b45c101f567cda 100644
--- a/libcxx/test/support/test.support/test_check_assertion.pass.cpp
+++ b/libcxx/test/support/test.support/test_check_assertion.pass.cpp
@@ -18,47 +18,94 @@
 #include "check_assertion.h"
 
 template <class Func>
-inline bool TestDeathTest(const char* stmt, Func&& func, DeathTest::ResultKind ExpectResult, AssertionInfoMatcher Matcher = AnyMatcher) {
-  DeathTest DT(Matcher);
-  DeathTest::ResultKind RK = DT.Run(func);
-  auto OnFailure = [&](std::string msg) {
-    std::fprintf(stderr, "EXPECT_DEATH( %s ) failed! (%s)\n\n", stmt, msg.c_str());
-    if (!DT.getChildStdErr().empty()) {
-      std::fprintf(stderr, "---------- standard err ----------\n%s\n", DT.getChildStdErr().c_str());
-    }
-    if (!DT.getChildStdOut().empty()) {
-      std::fprintf(stderr, "---------- standard out ----------\n%s\n", DT.getChildStdOut().c_str());
+inline bool TestDeathTest(
+    Outcome expected_outcome, DeathCause expected_cause, const char* stmt, Func&& func, AssertionInfoMatcher matcher) {
+  DeathTest test_case;
+  DeathTestResult test_result = test_case.Run(expected_cause, func, matcher);
+  std::string maybe_failure_description;
+
+  Outcome outcome = test_result.outcome();
+  if (expected_outcome != outcome) {
+    maybe_failure_description +=
+        std::string("Test outcome was different from expected; expected ") + ToString(expected_outcome) +
+        ", got: " + ToString(outcome);
+  }
+
+  DeathCause cause = test_result.cause();
+  if (expected_cause != cause) {
+    auto failure_description =
+        std::string("Cause of death was different from expected; expected ") + ToString(expected_cause) +
+        ", got: " + ToString(cause);
+    if (maybe_failure_description.empty()) {
+      maybe_failure_description = failure_description;
+    } else {
+      maybe_failure_description += std::string("; ") + failure_description;
     }
+  }
+
+  if (!maybe_failure_description.empty()) {
+    test_case.PrintFailureDetails(maybe_failure_description, stmt, test_result.cause());
     return false;
-  };
-  if (RK != ExpectResult)
-    return OnFailure(std::string("expected result did not occur: expected ") + DeathTest::ResultKindToString(ExpectResult) + " got: " + DeathTest::ResultKindToString(RK));
+  }
+
   return true;
 }
-#define TEST_DEATH_TEST(RK, ...) assert((TestDeathTest(#__VA_ARGS__, [&]() { __VA_ARGS__; }, RK, AnyMatcher )))
 
-#define TEST_DEATH_TEST_MATCHES(RK, Matcher, ...) assert((TestDeathTest(#__VA_ARGS__, [&]() { __VA_ARGS__; }, RK, Matcher)))
+// clang-format off
 
-void my_libcpp_assert() {
-  _LIBCPP_ASSERT(false, "other");
-}
+#define TEST_DEATH_TEST(outcome, cause, ...)                   \
+  assert(( TestDeathTest(outcome, cause, #__VA_ARGS__, [&]() { __VA_ARGS__; }, AnyMatcher) ))
+#define TEST_DEATH_TEST_MATCHES(outcome, cause, matcher, ...)  \
+  assert(( TestDeathTest(outcome, cause, #__VA_ARGS__, [&]() { __VA_ARGS__; }, matcher) ))
 
-void test_no_match_found() {
-  AssertionInfoMatcher ExpectMatch("my message");
-  TEST_DEATH_TEST_MATCHES(DeathTest::RK_MatchFailure, ExpectMatch, my_libcpp_assert());
-}
+// clang-format on
 
-void test_did_not_die() {
-  TEST_DEATH_TEST(DeathTest::RK_DidNotDie, ((void)0));
-}
+int main(int, char**) {
+  { // Success -- verbose abort with any matcher.
+    auto fail_assert = [] { _LIBCPP_ASSERT(false, "Some message"); };
+#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+    TEST_DEATH_TEST_MATCHES(Outcome::Success, DeathCause::VerboseAbort, AnyMatcher, fail_assert());
+#else
+    TEST_DEATH_TEST_MATCHES(Outcome::Success, DeathCause::Trap, AnyMatcher, fail_assert());
+#endif
+  }
 
-void test_unknown() {
-  TEST_DEATH_TEST(DeathTest::RK_Unknown, std::exit(13));
-}
+  { // Success -- verbose abort with a specific matcher.
+    auto fail_assert = [] { _LIBCPP_ASSERT(false, "Some message"); };
+    AssertionInfoMatcher matcher("Some message");
+#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+    TEST_DEATH_TEST_MATCHES(Outcome::Success, DeathCause::VerboseAbort, matcher, fail_assert());
+#else
+    TEST_DEATH_TEST_MATCHES(Outcome::Success, DeathCause::Trap, matcher, fail_assert());
+#endif
+  }
+
+  { // Success -- `std::terminate`.
+    TEST_DEATH_TEST(Outcome::Success, DeathCause::StdTerminate, std::terminate());
+  }
+
+  { // Success -- trapping.
+    TEST_DEATH_TEST(Outcome::Success, DeathCause::Trap, __builtin_trap());
+  }
+
+  { // Error message doesn't match.
+    auto fail_assert = [] { _LIBCPP_ASSERT(false, "Actual message doesn't match"); };
+    AssertionInfoMatcher matcher("Bad expected message");
+#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+    TEST_DEATH_TEST_MATCHES(Outcome::UnexpectedAbortMessage, DeathCause::VerboseAbort, matcher, fail_assert());
+#else
+    TEST_DEATH_TEST_MATCHES(Outcome::Success, DeathCause::Trap, matcher, fail_assert());
+#endif
+  }
+
+
+  { // Invalid cause -- child did not die.
+    TEST_DEATH_TEST(Outcome::InvalidCause, DeathCause::DidNotDie, ((void)0));
+  }
+
+  { // Invalid cause -- unknown.
+    TEST_DEATH_TEST(Outcome::InvalidCause, DeathCause::Unknown, std::exit(13));
+  }
 
-int main(int, char**) {
-  test_no_match_found();
-  test_did_not_die();
-  test_unknown();
   return 0;
 }
diff --git a/libcxx/vendor/llvm/default_assertion_handler.in b/libcxx/vendor/llvm/default_assertion_handler.in
index 111d305a16f7c4..60bbf27489097b 100644
--- a/libcxx/vendor/llvm/default_assertion_handler.in
+++ b/libcxx/vendor/llvm/default_assertion_handler.in
@@ -17,7 +17,14 @@
 #  pragma GCC system_header
 #endif
 
-// TODO(hardening): in production, trap rather than abort.
-#define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_ABORT("%s", message)
+#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+
+#  define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_ABORT("%s", message)
+
+#else
+
+#  define _LIBCPP_ASSERTION_HANDLER(message) ((void)message, __builtin_trap())
+
+#endif // _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
 
 #endif // _LIBCPP___ASSERTION_HANDLER

>From 522100d40be08e67894416fb485e8be467160884 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconsteq at gmail.com>
Date: Thu, 18 Jan 2024 02:40:42 -0800
Subject: [PATCH 2/9] Format

---
 libcxx/test/support/check_assertion.h         | 34 +++++++++----------
 .../test_check_assertion.pass.cpp             |  1 -
 2 files changed, 16 insertions(+), 19 deletions(-)

diff --git a/libcxx/test/support/check_assertion.h b/libcxx/test/support/check_assertion.h
index 8e1493b70a66a6..229cd250e66f1b 100644
--- a/libcxx/test/support/check_assertion.h
+++ b/libcxx/test/support/check_assertion.h
@@ -207,33 +207,31 @@ std::string ToString(Outcome outcome) {
 }
 
 class DeathTestResult {
-  public:
-    DeathTestResult() = default;
-    DeathTestResult(Outcome set_outcome, DeathCause set_cause, const std::string& set_failure_description = "")
-        : outcome_(set_outcome), cause_(set_cause), failure_description_(set_failure_description) {}
-
-    bool success() const { return outcome() == Outcome::Success; }
-    Outcome outcome() const { return outcome_; }
-    DeathCause cause() const { return cause_; }
-    const std::string& failure_description() const { return failure_description_; }
-
-  private:
-    Outcome outcome_ = Outcome::Success;
-    DeathCause cause_ = DeathCause::Unknown;
-    std::string failure_description_;
+public:
+  DeathTestResult() = default;
+  DeathTestResult(Outcome set_outcome, DeathCause set_cause, const std::string& set_failure_description = "")
+      : outcome_(set_outcome), cause_(set_cause), failure_description_(set_failure_description) {}
+
+  bool success() const { return outcome() == Outcome::Success; }
+  Outcome outcome() const { return outcome_; }
+  DeathCause cause() const { return cause_; }
+  const std::string& failure_description() const { return failure_description_; }
+
+private:
+  Outcome outcome_  = Outcome::Success;
+  DeathCause cause_ = DeathCause::Unknown;
+  std::string failure_description_;
 };
 
 class DeathTest {
-  public:
+public:
   DeathTest()                            = default;
   DeathTest(DeathTest const&)            = delete;
   DeathTest& operator=(DeathTest const&) = delete;
 
   template <class Func>
   DeathTestResult Run(DeathCause expected_cause, Func&& func, const AssertionInfoMatcher& matcher) {
-    std::set_terminate([] {
-      StopChildProcess(DeathCause::StdTerminate);
-    });
+    std::set_terminate([] { StopChildProcess(DeathCause::StdTerminate); });
 
     DeathCause cause = Run(func);
 
diff --git a/libcxx/test/support/test.support/test_check_assertion.pass.cpp b/libcxx/test/support/test.support/test_check_assertion.pass.cpp
index b45c101f567cda..a078f66cf0a19b 100644
--- a/libcxx/test/support/test.support/test_check_assertion.pass.cpp
+++ b/libcxx/test/support/test.support/test_check_assertion.pass.cpp
@@ -98,7 +98,6 @@ int main(int, char**) {
 #endif
   }
 
-
   { // Invalid cause -- child did not die.
     TEST_DEATH_TEST(Outcome::InvalidCause, DeathCause::DidNotDie, ((void)0));
   }

>From 895e6ad17876a34d069e5e5cc358fc4da42f4be2 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconsteq at gmail.com>
Date: Thu, 18 Jan 2024 23:25:50 -0800
Subject: [PATCH 3/9] Fix GCC error

---
 libcxx/test/support/check_assertion.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libcxx/test/support/check_assertion.h b/libcxx/test/support/check_assertion.h
index 229cd250e66f1b..2a382c172564f5 100644
--- a/libcxx/test/support/check_assertion.h
+++ b/libcxx/test/support/check_assertion.h
@@ -175,6 +175,8 @@ std::string ToString(DeathCause cause) {
   case DeathCause::Unknown:
     return "<invalid cause (unknown)>";
   }
+
+  assert(false && "Unreachable");
 }
 
 TEST_NORETURN void StopChildProcess(DeathCause cause) { std::exit(static_cast<int>(cause)); }
@@ -204,6 +206,8 @@ std::string ToString(Outcome outcome) {
   case Outcome::InvalidCause:
     return "invalid death cause";
   }
+
+  assert(false && "Unreachable");
 }
 
 class DeathTestResult {

>From 119f7cdda0d98329ea1d2372d51204a5225b7515 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconsteq at gmail.com>
Date: Fri, 19 Jan 2024 00:20:08 -0800
Subject: [PATCH 4/9] Feedback + other refactoring

---
 libcxx/test/support/check_assertion.h         | 239 ++++++------------
 .../test_check_assertion.pass.cpp             |  35 ++-
 2 files changed, 92 insertions(+), 182 deletions(-)

diff --git a/libcxx/test/support/check_assertion.h b/libcxx/test/support/check_assertion.h
index 2a382c172564f5..deeba7e8f38ba2 100644
--- a/libcxx/test/support/check_assertion.h
+++ b/libcxx/test/support/check_assertion.h
@@ -15,6 +15,7 @@
 #include <cstdio>
 #include <cstdlib>
 #include <exception>
+#include <functional>
 #include <regex>
 #include <sstream>
 #include <string>
@@ -31,112 +32,51 @@
 #  error "C++11 or greater is required to use this header"
 #endif
 
-struct AssertionInfoMatcher {
-  // When printing the assertion message to `stderr`, delimit it with a marker to make it easier to match the message
-  // later.
-  static constexpr const char* Marker = "###";
-
-  static const int any_line             = -1;
-  static constexpr const char* any_file = "*";
-  static constexpr const char* any_msg  = "*";
-
-  constexpr AssertionInfoMatcher()
-      : msg_(any_msg, __builtin_strlen(any_msg)), file_(any_file, __builtin_strlen(any_file)), line_(any_line) {}
-  constexpr AssertionInfoMatcher(const char* msg, const char* file = any_file, int line = any_line)
-      : is_empty_(false), msg_(msg, __builtin_strlen(msg)), file_(file, __builtin_strlen(file)), line_(line) {}
-
-  bool CheckMatchInOutput(const std::string& output, std::string& error) const {
-    // Extract information from the error message. This has to stay synchronized with how we format assertions in the
-    // library.
-    std::regex message_format(".*###\\n(.*):(\\d+): assertion (.*) failed: (.*)\\n###");
-
-    std::smatch match_result;
-    bool has_match = std::regex_match(output, match_result, message_format);
-    assert(has_match);
-    assert(match_result.size() == 5);
-
-    const std::string& file = match_result[1];
-    int line                = std::stoi(match_result[2]);
-    // Omitting `expression` in `match_result[3]`
-    const std::string& failure_reason = match_result[4];
-
-    bool result = Matches(file, line, failure_reason);
-    if (!result) {
-      error = FormatMatchingError(file, line, failure_reason);
-    }
-    return result;
-  }
-
-  bool Matches(const std::string& file, int line, const std::string& message) const {
-    assert(!empty() && "Empty matcher");
-    return CheckLineMatches(line) && CheckFileMatches(file) && CheckMessageMatches(message);
-  }
-
-  std::string FormatMatchingError(const std::string& file, int line, const std::string& message) const {
-    std::stringstream output;
-    output                                                                 //
-        << "Expected message:   '" << msg_.data() << "'\n"                 //
-        << "Actual message:     '" << message.c_str() << "'\n"             //
-        << "Expected location:   " << FormatLocation(file_, line_) << "\n" //
-        << "Actual location:     " << FormatLocation(file, line) << "\n";
-    return output.str();
-  }
-
-  static std::string FormatLocation(std::string_view file, int line) {
-    std::string result;
-    result += (file == any_file ? "*" : std::string(file)) + ":";
-    result += (line == any_line ? "*" : std::to_string(line));
-    return result;
-  }
-
-  bool empty() const { return is_empty_; }
-  bool IsAnyMatcher() const { return msg_ == any_msg && file_ == any_file && line_ == any_line; }
-
-private:
-  bool CheckLineMatches(int got_line) const {
-    if (line_ == any_line)
-      return true;
-    return got_line == line_;
-  }
-
-  bool CheckFileMatches(std::string_view got_file) const {
-    assert(!empty() && "empty matcher");
-    if (file_ == any_file)
-      return true;
-    std::size_t found_at = got_file.find(file_);
-    if (found_at == std::string_view::npos)
-      return false;
-    // require the match start at the beginning of the file or immediately after
-    // a directory separator.
-    if (found_at != 0) {
-      char last_char = got_file[found_at - 1];
-      if (last_char != '/' && last_char != '\\')
-        return false;
-    }
-    // require the match goes until the end of the string.
-    return got_file.substr(found_at) == file_;
+// When printing the assertion message to `stderr`, delimit it with a marker to make it easier to match the message
+// later.
+static constexpr const char* Marker = "###";
+
+// (success, error-message-if-failed)
+using MatchResult = std::pair<bool, std::string>;
+using Matcher = std::function<MatchResult(const std::string& /*text*/)>;
+
+MatchResult MatchAssertionMessage(const std::string& text, std::string_view expected_message) {
+  // Extract information from the error message. This has to stay synchronized with how we format assertions in the
+  // library.
+  std::regex assertion_format(".*###\\n(.*):(\\d+): assertion (.*) failed: (.*)\\n###");
+
+  std::smatch match_result;
+  bool has_match = std::regex_match(text, match_result, assertion_format);
+  assert(has_match);
+  assert(match_result.size() == 5);
+
+  const std::string& file = match_result[1];
+  int line                = std::stoi(match_result[2]);
+  // Omitting `expression` in `match_result[3]`
+  const std::string& assertion_message = match_result[4];
+
+  bool result = assertion_message.find(expected_message) != std::string::npos;
+  if (!result) {
+    std::stringstream matching_error;
+    matching_error                                                       //
+        << "Expected message:   '" << expected_message.data() << "'\n"   //
+        << "Actual message:     '" << assertion_message.c_str() << "'\n" //
+        << "Source location:     " << file << ":" << std::to_string(line) << "\n";
+    return MatchResult(/*success=*/false, matching_error.str());
   }
 
-  bool CheckMessageMatches(std::string_view got_msg) const {
-    assert(!empty() && "empty matcher");
-    if (msg_ == any_msg)
-      return true;
-    std::size_t found_at = got_msg.find(msg_);
-    if (found_at == std::string_view::npos)
-      return false;
-    // Allow any match
-    return true;
-  }
+  return MatchResult(/*success=*/true, /*maybe_error=*/"");
+}
 
-private:
-  bool is_empty_ = true;
-  ;
-  std::string_view msg_;
-  std::string_view file_;
-  int line_;
-};
+Matcher MakeAssertionMessageMatcher(std::string_view assertion_message) {
+  return [=] (const std::string& text) {
+    return MatchAssertionMessage(text, assertion_message);
+  };
+}
 
-static constexpr AssertionInfoMatcher AnyMatcher(AssertionInfoMatcher::any_msg);
+Matcher MakeAnyMatcher() {
+  return [](const std::string&) { return MatchResult(/*success=*/true, /*maybe_error=*/""); };
+}
 
 enum class DeathCause {
   // Valid causes
@@ -169,11 +109,11 @@ std::string ToString(DeathCause cause) {
   case DeathCause::Trap:
     return "trap";
   case DeathCause::DidNotDie:
-    return "<invalid cause (did not die)>";
+    return "<invalid cause (child did not die)>";
   case DeathCause::SetupFailure:
-    return "<invalid cause (setup failure)>";
+    return "<invalid cause (child failed to set up test environment)>";
   case DeathCause::Unknown:
-    return "<invalid cause (unknown)>";
+    return "<invalid cause (cause unknown)>";
   }
 
   assert(false && "Unreachable");
@@ -191,7 +131,7 @@ DeathCause ConvertToDeathCause(int val) {
 enum class Outcome {
   Success,
   UnexpectedCause,
-  UnexpectedAbortMessage,
+  UnexpectedErrorMessage,
   InvalidCause,
 };
 
@@ -201,8 +141,8 @@ std::string ToString(Outcome outcome) {
     return "success";
   case Outcome::UnexpectedCause:
     return "unexpected death cause";
-  case Outcome::UnexpectedAbortMessage:
-    return "unexpected verbose abort message";
+  case Outcome::UnexpectedErrorMessage:
+    return "unexpected error message";
   case Outcome::InvalidCause:
     return "invalid death cause";
   }
@@ -234,53 +174,31 @@ class DeathTest {
   DeathTest& operator=(DeathTest const&) = delete;
 
   template <class Func>
-  DeathTestResult Run(DeathCause expected_cause, Func&& func, const AssertionInfoMatcher& matcher) {
+  DeathTestResult Run(DeathCause expected_cause, Func&& func, const Matcher& matcher) {
     std::set_terminate([] { StopChildProcess(DeathCause::StdTerminate); });
 
     DeathCause cause = Run(func);
 
-    auto DescribeDeathCauseMismatch = [](DeathCause expected, DeathCause actual) {
-      std::stringstream output;
-      output                                                    //
-          << "Child died, but with a different death cause\n"   //
-          << "Expected cause:   " << ToString(expected) << "\n" //
-          << "Actual cause:     " << ToString(actual) << "\n";
-      return output.str();
-    };
-
-    switch (cause) {
-    case DeathCause::StdTerminate:
-    case DeathCause::Trap:
-      if (expected_cause != cause) {
-        auto failure_description = DescribeDeathCauseMismatch(expected_cause, cause);
-        return DeathTestResult(Outcome::UnexpectedCause, cause, failure_description);
-      }
-      return DeathTestResult(Outcome::Success, cause);
-
-    case DeathCause::VerboseAbort: {
-      if (expected_cause != cause) {
-        auto failure_description = DescribeDeathCauseMismatch(expected_cause, cause);
-        return DeathTestResult(Outcome::UnexpectedCause, cause, failure_description);
-      }
+    if (!IsValidCause(cause)) {
+      return DeathTestResult(Outcome::InvalidCause, cause, ToString(cause));
+    }
 
-      std::string maybe_error;
-      if (matcher.CheckMatchInOutput(getChildStdErr(), maybe_error)) {
-        return DeathTestResult(Outcome::Success, cause);
-      }
-      auto failure_description = std::string("Child died, but with a different verbose abort message\n") + maybe_error;
-      return DeathTestResult(Outcome::UnexpectedAbortMessage, cause, failure_description);
+    if (expected_cause != cause) {
+      std::stringstream failure_description;
+      failure_description                                       //
+          << "Child died, but with a different death cause\n"   //
+          << "Expected cause:   " << ToString(expected_cause) << "\n" //
+          << "Actual cause:     " << ToString(cause) << "\n";
+      return DeathTestResult(Outcome::UnexpectedCause, cause, failure_description.str());
     }
 
-    // Invalid causes.
-    case DeathCause::DidNotDie:
-      return DeathTestResult(Outcome::InvalidCause, cause, "Child did not die");
-    case DeathCause::SetupFailure:
-      return DeathTestResult(Outcome::InvalidCause, cause, "Child failed to set up test environment");
-    case DeathCause::Unknown:
-      return DeathTestResult(Outcome::InvalidCause, cause, "Cause unknown");
+    MatchResult match_result = matcher(GetChildStdErr());
+    if (!match_result.first) {
+      auto failure_description = std::string("Child died, but with a different error message\n") + match_result.second;
+      return DeathTestResult(Outcome::UnexpectedErrorMessage, cause, failure_description);
     }
 
-    assert(false && "Unreachable");
+    return DeathTestResult(Outcome::Success, cause);
   }
 
   void PrintFailureDetails(std::string_view failure_description, std::string_view stmt, DeathCause cause) const {
@@ -288,19 +206,19 @@ class DeathTest {
         stderr, "Failure: EXPECT_DEATH( %s ) failed!\n(reason: %s)\n\n", stmt.data(), failure_description.data());
 
     if (cause != DeathCause::Unknown) {
-      std::fprintf(stderr, "child exit code: %d\n", getChildExitCode());
+      std::fprintf(stderr, "child exit code: %d\n", GetChildExitCode());
     }
-    std::fprintf(stderr, "---------- standard err ----------\n%s", getChildStdErr().c_str());
+    std::fprintf(stderr, "---------- standard err ----------\n%s", GetChildStdErr().c_str());
     std::fprintf(stderr, "\n----------------------------------\n");
-    std::fprintf(stderr, "---------- standard out ----------\n%s", getChildStdOut().c_str());
+    std::fprintf(stderr, "---------- standard out ----------\n%s", GetChildStdOut().c_str());
     std::fprintf(stderr, "\n----------------------------------\n");
   };
 
-  int getChildExitCode() const { return exit_code_; }
-  std::string const& getChildStdOut() const { return stdout_from_child_; }
-  std::string const& getChildStdErr() const { return stderr_from_child_; }
-
 private:
+  int GetChildExitCode() const { return exit_code_; }
+  std::string const& GetChildStdOut() const { return stdout_from_child_; }
+  std::string const& GetChildStdErr() const { return stderr_from_child_; }
+
   template <class Func>
   DeathCause Run(Func&& f) {
     int pipe_res = pipe(stdout_pipe_fd_);
@@ -378,11 +296,8 @@ class DeathTest {
   }
 
   int GetStdOutReadFD() const { return stdout_pipe_fd_[0]; }
-
   int GetStdOutWriteFD() const { return stdout_pipe_fd_[1]; }
-
   int GetStdErrReadFD() const { return stderr_pipe_fd_[0]; }
-
   int GetStdErrWriteFD() const { return stderr_pipe_fd_[1]; }
 
   pid_t child_pid_ = -1;
@@ -398,9 +313,9 @@ void std::__libcpp_verbose_abort(char const* format, ...) {
   va_list args;
   va_start(args, format);
 
-  std::fprintf(stderr, "%s\n", AssertionInfoMatcher::Marker);
+  std::fprintf(stderr, "%s\n", Marker);
   std::vfprintf(stderr, format, args);
-  std::fprintf(stderr, "%s", AssertionInfoMatcher::Marker);
+  std::fprintf(stderr, "%s", Marker);
 
   va_end(args);
 
@@ -409,7 +324,7 @@ void std::__libcpp_verbose_abort(char const* format, ...) {
 #endif // _LIBCPP_VERSION
 
 template <class Func>
-inline bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func, AssertionInfoMatcher matcher) {
+bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func, const Matcher& matcher) {
   assert(IsValidCause(expected_cause));
 
   DeathTest test_case;
@@ -422,8 +337,8 @@ inline bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func
 }
 
 template <class Func>
-inline bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func) {
-  return ExpectDeath(expected_cause, stmt, func, AnyMatcher);
+bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func) {
+  return ExpectDeath(expected_cause, stmt, func, MakeAnyMatcher());
 }
 
 // clang-format off
@@ -438,7 +353,7 @@ inline bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func
 
 #if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
 #define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
-    assert(( ExpectDeath(DeathCause::VerboseAbort, #expr, [&]() { (void)(expr); }, AssertionInfoMatcher(message)) ))
+    assert(( ExpectDeath(DeathCause::VerboseAbort, #expr, [&]() { (void)(expr); }, MakeAssertionMessageMatcher(message)) ))
 #else
 #define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
     assert(( ExpectDeath(DeathCause::Trap,         #expr, [&]() { (void)(expr); }) ))
diff --git a/libcxx/test/support/test.support/test_check_assertion.pass.cpp b/libcxx/test/support/test.support/test_check_assertion.pass.cpp
index a078f66cf0a19b..09f1429f71fdae 100644
--- a/libcxx/test/support/test.support/test_check_assertion.pass.cpp
+++ b/libcxx/test/support/test.support/test_check_assertion.pass.cpp
@@ -18,8 +18,8 @@
 #include "check_assertion.h"
 
 template <class Func>
-inline bool TestDeathTest(
-    Outcome expected_outcome, DeathCause expected_cause, const char* stmt, Func&& func, AssertionInfoMatcher matcher) {
+bool TestDeathTest(
+    Outcome expected_outcome, DeathCause expected_cause, const char* stmt, Func&& func, const Matcher& matcher) {
   DeathTest test_case;
   DeathTestResult test_result = test_case.Run(expected_cause, func, matcher);
   std::string maybe_failure_description;
@@ -54,30 +54,29 @@ inline bool TestDeathTest(
 // clang-format off
 
 #define TEST_DEATH_TEST(outcome, cause, ...)                   \
-  assert(( TestDeathTest(outcome, cause, #__VA_ARGS__, [&]() { __VA_ARGS__; }, AnyMatcher) ))
+  assert(( TestDeathTest(outcome, cause, #__VA_ARGS__, [&]() { __VA_ARGS__; }, MakeAnyMatcher()) ))
 #define TEST_DEATH_TEST_MATCHES(outcome, cause, matcher, ...)  \
   assert(( TestDeathTest(outcome, cause, #__VA_ARGS__, [&]() { __VA_ARGS__; }, matcher) ))
 
 // clang-format on
 
-int main(int, char**) {
-  { // Success -- verbose abort with any matcher.
-    auto fail_assert = [] { _LIBCPP_ASSERT(false, "Some message"); };
 #if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
-    TEST_DEATH_TEST_MATCHES(Outcome::Success, DeathCause::VerboseAbort, AnyMatcher, fail_assert());
+DeathCause assertion_death_cause = DeathCause::VerboseAbort;
 #else
-    TEST_DEATH_TEST_MATCHES(Outcome::Success, DeathCause::Trap, AnyMatcher, fail_assert());
+DeathCause assertion_death_cause = DeathCause::Trap;
 #endif
+
+int main(int, char**) {
+
+  { // Success -- verbose abort with any matcher.
+    auto fail_assert = [] { _LIBCPP_ASSERT(false, "Some message"); };
+    TEST_DEATH_TEST_MATCHES(Outcome::Success, assertion_death_cause, MakeAnyMatcher(), fail_assert());
   }
 
   { // Success -- verbose abort with a specific matcher.
     auto fail_assert = [] { _LIBCPP_ASSERT(false, "Some message"); };
-    AssertionInfoMatcher matcher("Some message");
-#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
-    TEST_DEATH_TEST_MATCHES(Outcome::Success, DeathCause::VerboseAbort, matcher, fail_assert());
-#else
-    TEST_DEATH_TEST_MATCHES(Outcome::Success, DeathCause::Trap, matcher, fail_assert());
-#endif
+    Matcher matcher = MakeAssertionMessageMatcher("Some message");
+    TEST_DEATH_TEST_MATCHES(Outcome::Success, assertion_death_cause, matcher, fail_assert());
   }
 
   { // Success -- `std::terminate`.
@@ -90,12 +89,8 @@ int main(int, char**) {
 
   { // Error message doesn't match.
     auto fail_assert = [] { _LIBCPP_ASSERT(false, "Actual message doesn't match"); };
-    AssertionInfoMatcher matcher("Bad expected message");
-#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
-    TEST_DEATH_TEST_MATCHES(Outcome::UnexpectedAbortMessage, DeathCause::VerboseAbort, matcher, fail_assert());
-#else
-    TEST_DEATH_TEST_MATCHES(Outcome::Success, DeathCause::Trap, matcher, fail_assert());
-#endif
+    Matcher matcher = MakeAssertionMessageMatcher("Bad expected message");
+    TEST_DEATH_TEST_MATCHES(Outcome::UnexpectedErrorMessage, assertion_death_cause, matcher, fail_assert());
   }
 
   { // Invalid cause -- child did not die.

>From 43c33cf8ed31758979d9f94393fe05bbc52097e9 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconsteq at gmail.com>
Date: Fri, 19 Jan 2024 00:29:19 -0800
Subject: [PATCH 5/9] Minor refactoring, clang-format

---
 libcxx/test/support/check_assertion.h | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/support/check_assertion.h b/libcxx/test/support/check_assertion.h
index deeba7e8f38ba2..1fa5cc72adbc24 100644
--- a/libcxx/test/support/check_assertion.h
+++ b/libcxx/test/support/check_assertion.h
@@ -38,7 +38,7 @@ static constexpr const char* Marker = "###";
 
 // (success, error-message-if-failed)
 using MatchResult = std::pair<bool, std::string>;
-using Matcher = std::function<MatchResult(const std::string& /*text*/)>;
+using Matcher     = std::function<MatchResult(const std::string& /*text*/)>;
 
 MatchResult MatchAssertionMessage(const std::string& text, std::string_view expected_message) {
   // Extract information from the error message. This has to stay synchronized with how we format assertions in the
@@ -55,7 +55,7 @@ MatchResult MatchAssertionMessage(const std::string& text, std::string_view expe
   // Omitting `expression` in `match_result[3]`
   const std::string& assertion_message = match_result[4];
 
-  bool result = assertion_message.find(expected_message) != std::string::npos;
+  bool result = assertion_message == expected_message;
   if (!result) {
     std::stringstream matching_error;
     matching_error                                                       //
@@ -69,13 +69,15 @@ MatchResult MatchAssertionMessage(const std::string& text, std::string_view expe
 }
 
 Matcher MakeAssertionMessageMatcher(std::string_view assertion_message) {
-  return [=] (const std::string& text) {
+  return [=](const std::string& text) { //
     return MatchAssertionMessage(text, assertion_message);
   };
 }
 
 Matcher MakeAnyMatcher() {
-  return [](const std::string&) { return MatchResult(/*success=*/true, /*maybe_error=*/""); };
+  return [](const std::string&) { //
+    return MatchResult(/*success=*/true, /*maybe_error=*/"");
+  };
 }
 
 enum class DeathCause {
@@ -185,8 +187,8 @@ class DeathTest {
 
     if (expected_cause != cause) {
       std::stringstream failure_description;
-      failure_description                                       //
-          << "Child died, but with a different death cause\n"   //
+      failure_description                                             //
+          << "Child died, but with a different death cause\n"         //
           << "Expected cause:   " << ToString(expected_cause) << "\n" //
           << "Actual cause:     " << ToString(cause) << "\n";
       return DeathTestResult(Outcome::UnexpectedCause, cause, failure_description.str());

>From a95509b73dd8a6f84f8210df56131c8d37ddc6f2 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconsteq at gmail.com>
Date: Fri, 19 Jan 2024 02:53:31 -0800
Subject: [PATCH 6/9] Refactoring, more feedback

---
 ...tomize_verbose_abort.compile-time.pass.cpp |  3 +-
 ...customize_verbose_abort.link-time.pass.cpp |  3 +-
 .../assertions/default_verbose_abort.pass.cpp |  4 +-
 .../test_check_assertion.pass.cpp             | 71 ++++++++++++-------
 4 files changed, 51 insertions(+), 30 deletions(-)

diff --git a/libcxx/test/libcxx/assertions/customize_verbose_abort.compile-time.pass.cpp b/libcxx/test/libcxx/assertions/customize_verbose_abort.compile-time.pass.cpp
index d176d8a12875e6..69154c3f7eaffa 100644
--- a/libcxx/test/libcxx/assertions/customize_verbose_abort.compile-time.pass.cpp
+++ b/libcxx/test/libcxx/assertions/customize_verbose_abort.compile-time.pass.cpp
@@ -8,7 +8,6 @@
 
 // This compile-time customization requires cross-file macros, which doesn't work with modules.
 // UNSUPPORTED: clang-modules-build
-// REQUIRES: libcpp-hardening-mode=debug
 
 // Make sure that we can customize the verbose termination function at compile-time by
 // defining _LIBCPP_VERBOSE_ABORT ourselves. Note that this does not have any
@@ -23,6 +22,6 @@ void my_abort(char const*, ...) {
 }
 
 int main(int, char**) {
-  _LIBCPP_ASSERT(false, "message");
+  _LIBCPP_VERBOSE_ABORT("%s", "message");
   return EXIT_FAILURE;
 }
diff --git a/libcxx/test/libcxx/assertions/customize_verbose_abort.link-time.pass.cpp b/libcxx/test/libcxx/assertions/customize_verbose_abort.link-time.pass.cpp
index c82aaea6ba30df..585ab73f2cb261 100644
--- a/libcxx/test/libcxx/assertions/customize_verbose_abort.link-time.pass.cpp
+++ b/libcxx/test/libcxx/assertions/customize_verbose_abort.link-time.pass.cpp
@@ -11,7 +11,6 @@
 // We flag uses of the verbose termination function in older dylibs at compile-time to avoid runtime
 // failures when back-deploying.
 // XFAIL: availability-verbose_abort-missing
-// REQUIRES: libcpp-hardening-mode=debug
 
 #include <cstdlib>
 
@@ -20,6 +19,6 @@ void std::__libcpp_verbose_abort(char const*, ...) {
 }
 
 int main(int, char**) {
-  _LIBCPP_ASSERT(false, "message");
+  std::__libcpp_verbose_abort("%s", "message");
   return EXIT_FAILURE;
 }
diff --git a/libcxx/test/libcxx/assertions/default_verbose_abort.pass.cpp b/libcxx/test/libcxx/assertions/default_verbose_abort.pass.cpp
index 29ef10016c1a37..18cc7a9521e498 100644
--- a/libcxx/test/libcxx/assertions/default_verbose_abort.pass.cpp
+++ b/libcxx/test/libcxx/assertions/default_verbose_abort.pass.cpp
@@ -7,8 +7,8 @@
 //===----------------------------------------------------------------------===//
 
 // Test that the default verbose termination function aborts the program.
-// REQUIRES: libcpp-hardening-mode=debug
 
+#include <__verbose_abort>
 #include <csignal>
 #include <cstdlib>
 
@@ -20,6 +20,6 @@ void signal_handler(int signal) {
 
 int main(int, char**) {
   if (std::signal(SIGABRT, signal_handler) != SIG_ERR)
-    _LIBCPP_ASSERT(false, "foo");
+    std::__libcpp_verbose_abort("%s", "foo");
   return EXIT_FAILURE;
 }
diff --git a/libcxx/test/support/test.support/test_check_assertion.pass.cpp b/libcxx/test/support/test.support/test_check_assertion.pass.cpp
index 09f1429f71fdae..137506ceedbc2b 100644
--- a/libcxx/test/support/test.support/test_check_assertion.pass.cpp
+++ b/libcxx/test/support/test.support/test_check_assertion.pass.cpp
@@ -20,8 +20,17 @@
 template <class Func>
 bool TestDeathTest(
     Outcome expected_outcome, DeathCause expected_cause, const char* stmt, Func&& func, const Matcher& matcher) {
+  auto get_matcher = [&] {
+#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+    return matcher;
+#else
+    (void)matcher;
+    return MakeAnyMatcher();
+#endif
+  };
+
   DeathTest test_case;
-  DeathTestResult test_result = test_case.Run(expected_cause, func, matcher);
+  DeathTestResult test_result = test_case.Run(expected_cause, func, get_matcher());
   std::string maybe_failure_description;
 
   Outcome outcome = test_result.outcome();
@@ -67,38 +76,52 @@ DeathCause assertion_death_cause = DeathCause::Trap;
 #endif
 
 int main(int, char**) {
+  auto fail_assert     = [] { _LIBCPP_ASSERT(false, "Some message"); };
+  Matcher good_matcher = MakeAssertionMessageMatcher("Some message");
+  Matcher bad_matcher  = MakeAssertionMessageMatcher("Bad expected message");
+
+  // Test the implementation of death tests. We're bypassing the assertions added by the actual `EXPECT_DEATH` macros
+  // which allows us to test failure cases (where the assertion would fail) as well.
+  {
+    // Success -- `std::terminate`.
+    TEST_DEATH_TEST(Outcome::Success, DeathCause::StdTerminate, std::terminate());
 
-  { // Success -- verbose abort with any matcher.
-    auto fail_assert = [] { _LIBCPP_ASSERT(false, "Some message"); };
+    (void)fail_assert;
+    // Success -- trapping.
+    TEST_DEATH_TEST(Outcome::Success, DeathCause::Trap, __builtin_trap());
+
+    // Success -- assertion failure with any matcher.
     TEST_DEATH_TEST_MATCHES(Outcome::Success, assertion_death_cause, MakeAnyMatcher(), fail_assert());
-  }
 
-  { // Success -- verbose abort with a specific matcher.
-    auto fail_assert = [] { _LIBCPP_ASSERT(false, "Some message"); };
-    Matcher matcher = MakeAssertionMessageMatcher("Some message");
-    TEST_DEATH_TEST_MATCHES(Outcome::Success, assertion_death_cause, matcher, fail_assert());
-  }
+    // Success -- assertion failure with a specific matcher.
+    TEST_DEATH_TEST_MATCHES(Outcome::Success, assertion_death_cause, good_matcher, fail_assert());
 
-  { // Success -- `std::terminate`.
-    TEST_DEATH_TEST(Outcome::Success, DeathCause::StdTerminate, std::terminate());
-  }
+#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+    // Failure -- error message doesn't match.
+    TEST_DEATH_TEST_MATCHES(Outcome::UnexpectedErrorMessage, assertion_death_cause, bad_matcher, fail_assert());
+#endif
 
-  { // Success -- trapping.
-    TEST_DEATH_TEST(Outcome::Success, DeathCause::Trap, __builtin_trap());
-  }
+    // Invalid cause -- child did not die.
+    TEST_DEATH_TEST(Outcome::InvalidCause, DeathCause::DidNotDie, ((void)0));
 
-  { // Error message doesn't match.
-    auto fail_assert = [] { _LIBCPP_ASSERT(false, "Actual message doesn't match"); };
-    Matcher matcher = MakeAssertionMessageMatcher("Bad expected message");
-    TEST_DEATH_TEST_MATCHES(Outcome::UnexpectedErrorMessage, assertion_death_cause, matcher, fail_assert());
+    // Invalid cause --  unknown.
+    TEST_DEATH_TEST(Outcome::InvalidCause, DeathCause::Unknown, std::exit(13));
   }
 
-  { // Invalid cause -- child did not die.
-    TEST_DEATH_TEST(Outcome::InvalidCause, DeathCause::DidNotDie, ((void)0));
-  }
+  // Test the `EXPECT_DEATH` macros themselves. Since they assert success, we can only test successful cases.
+  {
+    auto invoke_abort = [] { _LIBCPP_VERBOSE_ABORT("contains some message"); };
 
-  { // Invalid cause -- unknown.
-    TEST_DEATH_TEST(Outcome::InvalidCause, DeathCause::Unknown, std::exit(13));
+    auto simple_matcher = [] (const std::string& text) {
+      bool success = text.find("some") != std::string::npos;
+      return MatchResult(success, "");
+    };
+
+    EXPECT_DEATH(invoke_abort());
+    EXPECT_DEATH_MATCHES(MakeAnyMatcher(), invoke_abort());
+    EXPECT_DEATH_MATCHES(simple_matcher, invoke_abort());
+    EXPECT_STD_TERMINATE([] { std::terminate(); });
+    TEST_LIBCPP_ASSERT_FAILURE(fail_assert(), "Some message");
   }
 
   return 0;

>From 5727f38fb949c39a89c811ac9dc066539af3f86a Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconsteq at gmail.com>
Date: Fri, 19 Jan 2024 03:06:08 -0800
Subject: [PATCH 7/9] Add a comment

---
 libcxx/test/support/check_assertion.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/support/check_assertion.h b/libcxx/test/support/check_assertion.h
index 1fa5cc72adbc24..485f8103c7ad8d 100644
--- a/libcxx/test/support/check_assertion.h
+++ b/libcxx/test/support/check_assertion.h
@@ -289,6 +289,7 @@ class DeathTest {
 
     if (WIFSIGNALED(status_value)) {
       exit_code_ = WTERMSIG(status_value);
+      // `__builtin_trap` generqtes `SIGILL` on x86 and `SIGTRAP` on ARM.
       if (exit_code_ == SIGILL || exit_code_ == SIGTRAP) {
         return DeathCause::Trap;
       }

>From de5e151259849acf8ab75043701c9ede6b26f5bd Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconsteq at gmail.com>
Date: Fri, 19 Jan 2024 03:19:50 -0800
Subject: [PATCH 8/9] Update docs

---
 libcxx/docs/BuildingLibcxx.rst  | 20 ++++++++------------
 libcxx/docs/ReleaseNotes/18.rst | 21 ++++++++++++++-------
 2 files changed, 22 insertions(+), 19 deletions(-)

diff --git a/libcxx/docs/BuildingLibcxx.rst b/libcxx/docs/BuildingLibcxx.rst
index 11e250e3f3735a..9a250e7cd79059 100644
--- a/libcxx/docs/BuildingLibcxx.rst
+++ b/libcxx/docs/BuildingLibcxx.rst
@@ -485,18 +485,14 @@ LLVM-specific options
 .. _assertion-handler:
 
 Overriding the default assertion handler
-==========================================
-
-When the library wants to terminate due to an unforeseen condition (such as
-a hardening assertion failure), the program is aborted through a special verbose
-termination function. The library provides a default function that prints an
-error message and calls ``std::abort()``. Note that this function is provided by
-the static or shared library, so it is only available when deploying to
-a platform where the compiled library is sufficiently recent. On older
-platforms, the program will terminate in an unspecified unsuccessful manner, but
-the quality of diagnostics won't be great.
-
-However, vendors can also override that mechanism at CMake configuration time.
+========================================
+
+When the library wants to terminate due to a hardening assertion failure, the
+program is aborted by invoking a trap instruction (or in debug mode, by
+a special verbose termination function that prints an error message and calls
+``std::abort()``). However, vendors can also override that mechanism at CMake
+configuration time.
+
 When a hardening assertion fails, the library invokes the
 ``_LIBCPP_ASSERTION_HANDLER`` macro. A vendor may provide a header that contains
 a custom definition of this macro and specify the path to the header via the
diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index 77b7939a0c0ac9..9cc024c3d7634e 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -111,13 +111,18 @@ Deprecations and Removals
   macro is provided to restore the previous behavior, and it will be supported in the LLVM 18 release only.
   In LLVM 19 and beyond, ``_LIBCPP_ENABLE_NARROWING_CONVERSIONS_IN_VARIANT`` will not be honored anymore.
 
-- The only supported way to customize the assertion handler that gets invoked when a hardening assertion fails
-  is now by setting the ``LIBCXX_ASSERTION_HANDLER_FILE`` CMake variable and providing a custom header. See
-  the documentation on overriding the default assertion handler for details.
+- Overriding `__libcpp_verbose_abort` no longer has any effect on library assertions. The only supported way
+  to customize the assertion handler that gets invoked when a hardening assertion fails is now by setting the
+  ``LIBCXX_ASSERTION_HANDLER_FILE`` CMake variable and providing a custom header. See the documentation on
+  overriding the default assertion handler for details. The ability to override `__libcpp_verbose_abort` will
+  be removed in an upcoming release in favor of the new overriding mechanism.
+
+- In safe mode (which is now equivalent to the ``extensive`` hardening mode), a failed assertion will now
+  generate a trap rather than a call to verbose abort.
 
 - The ``_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED`` macro is not honored anymore in LLVM 18.
-  Please see the updated documentation about the hardening modes in libc++ and in particular the
-  ``_LIBCPP_VERBOSE_ABORT`` macro for details.
+  Please see the updated documentation about the hardening modes in libc++ and in particular on
+  overriding the default assertion handler.
 
 - The headers ``<experimental/deque>``, ``<experimental/forward_list>``, ``<experimental/list>``,
   ``<experimental/map>``, ``<experimental/memory_resource>``, ``<experimental/regex>``, ``<experimental/set>``,
@@ -140,13 +145,15 @@ Deprecations and Removals
 Upcoming Deprecations and Removals
 ----------------------------------
 
+- ``__libcpp_verbose_abort`` and the ability to override this function will be removed in an upcoming release.
+
 LLVM 19
 ~~~~~~~
 
 - The ``LIBCXX_ENABLE_ASSERTIONS`` CMake variable that was used to enable the safe mode will be deprecated and setting
   it will trigger an error; use the ``LIBCXX_HARDENING_MODE`` variable with the value ``extensive`` instead. Similarly,
-  the ``_LIBCPP_ENABLE_ASSERTIONS`` macro will be deprecated (setting it to ``1`` still enables the extensive mode the
-  LLVM 19 release while also issuing a deprecation warning). See :ref:`the hardening documentation
+  the ``_LIBCPP_ENABLE_ASSERTIONS`` macro will be deprecated (setting it to ``1`` still enables the extensive mode in
+  the LLVM 19 release while also issuing a deprecation warning). See :ref:`the hardening documentation
   <using-hardening-modes>` for more details.
 
 - The base template for ``std::char_traits`` has been marked as deprecated and will be removed in LLVM 19. If you

>From e2a162c9366576892ae9f535250c529613c7bcaf Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconsteq at gmail.com>
Date: Fri, 19 Jan 2024 03:20:39 -0800
Subject: [PATCH 9/9] format

---
 libcxx/test/support/test.support/test_check_assertion.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/support/test.support/test_check_assertion.pass.cpp b/libcxx/test/support/test.support/test_check_assertion.pass.cpp
index 137506ceedbc2b..ce0c30ad4069a3 100644
--- a/libcxx/test/support/test.support/test_check_assertion.pass.cpp
+++ b/libcxx/test/support/test.support/test_check_assertion.pass.cpp
@@ -112,7 +112,7 @@ int main(int, char**) {
   {
     auto invoke_abort = [] { _LIBCPP_VERBOSE_ABORT("contains some message"); };
 
-    auto simple_matcher = [] (const std::string& text) {
+    auto simple_matcher = [](const std::string& text) {
       bool success = text.find("some") != std::string::npos;
       return MatchResult(success, "");
     };



More information about the libcxx-commits mailing list