[libcxx-commits] [libcxx] [libcxxabi] varconst/hardening semantics introduce (PR #148268)

Konstantin Varlamov via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jul 11 10:59:56 PDT 2025


https://github.com/var-const created https://github.com/llvm/llvm-project/pull/148268

- [libc++][hardening][NFC] Introduce `_LIBCPP_VERBOSE_TRAP` macro.
- [libc++][hardening] Introduce a dylib function to log hardening errors.
- [libc++][hardening] Introduce assertion semantics.


>From 6708ebf6020c59a4832aac95cb9524d533798100 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Fri, 11 Jul 2025 10:17:36 -0700
Subject: [PATCH 1/3] [libc++][hardening][NFC] Introduce `_LIBCPP_VERBOSE_TRAP`
 macro.

Split out the calls to `__builtin_verbose_trap` into a separate header.
This is just a refactoring to make the code a bit more structured.
---
 libcxx/include/CMakeLists.txt                 |  1 +
 libcxx/include/__verbose_trap                 | 36 +++++++++++++++++++
 .../vendor/llvm/default_assertion_handler.in  | 14 ++------
 3 files changed, 39 insertions(+), 12 deletions(-)
 create mode 100644 libcxx/include/__verbose_trap

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index c8e6d28584623..f79edc9e32599 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -944,6 +944,7 @@ set(files
   __vector/vector_bool.h
   __vector/vector_bool_formatter.h
   __verbose_abort
+  __verbose_trap
   algorithm
   any
   array
diff --git a/libcxx/include/__verbose_trap b/libcxx/include/__verbose_trap
new file mode 100644
index 0000000000000..13ea727738c3b
--- /dev/null
+++ b/libcxx/include/__verbose_trap
@@ -0,0 +1,36 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___VERBOSE_TRAP
+#define _LIBCPP___VERBOSE_TRAP
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if __has_builtin(__builtin_verbose_trap)
+// AppleClang shipped a slightly different version of __builtin_verbose_trap from the upstream
+// version before upstream Clang actually got the builtin.
+// TODO: Remove once AppleClang supports the two-arguments version of the builtin.
+#  if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1700
+#    define _LIBCPP_VERBOSE_TRAP(message) __builtin_verbose_trap(message)
+#  else
+#    define _LIBCPP_VERBOSE_TRAP(message) __builtin_verbose_trap("libc++", message)
+#  endif
+#else
+#  define _LIBCPP_VERBOSE_TRAP(message) ((void)message, __builtin_trap())
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___VERBOSE_TRAP
diff --git a/libcxx/vendor/llvm/default_assertion_handler.in b/libcxx/vendor/llvm/default_assertion_handler.in
index 1d6b21fc6bb45..90b202a2dae57 100644
--- a/libcxx/vendor/llvm/default_assertion_handler.in
+++ b/libcxx/vendor/llvm/default_assertion_handler.in
@@ -15,6 +15,7 @@
 #  include <__cxx03/__verbose_abort>
 #else
 #  include <__config>
+#  include <__verbose_trap>
 #  include <__verbose_abort>
 #endif
 
@@ -28,18 +29,7 @@
 
 #else
 
-#  if __has_builtin(__builtin_verbose_trap)
-// AppleClang shipped a slightly different version of __builtin_verbose_trap from the upstream
-// version before upstream Clang actually got the builtin.
-// TODO: Remove once AppleClang supports the two-arguments version of the builtin.
-#    if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1700
-#      define _LIBCPP_ASSERTION_HANDLER(message) __builtin_verbose_trap(message)
-#    else
-#      define _LIBCPP_ASSERTION_HANDLER(message) __builtin_verbose_trap("libc++", message)
-#    endif
-#  else
-#    define _LIBCPP_ASSERTION_HANDLER(message) ((void)message, __builtin_trap())
-#  endif
+#  define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_TRAP(message)
 
 #endif // _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
 

>From 1fcfec368e34964811270b9a10ad969814e3a691 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Fri, 11 Jul 2025 10:29:10 -0700
Subject: [PATCH 2/3] [libc++][hardening] Introduce a dylib function to log
 hardening errors.

Unlike `verbose_abort`, this function merely logs the error but does not
terminate execution. It is intended to make it possible to implement the
`observe` semantic for Hardening.
---
 libcxx/include/CMakeLists.txt                 |  1 +
 libcxx/include/__configuration/availability.h |  5 ++
 libcxx/include/__log_hardening_failure        | 45 +++++++++++++++++
 libcxx/src/CMakeLists.txt                     |  1 +
 libcxx/src/log_hardening_failure.cpp          | 49 +++++++++++++++++++
 5 files changed, 101 insertions(+)
 create mode 100644 libcxx/include/__log_hardening_failure
 create mode 100644 libcxx/src/log_hardening_failure.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index c8e6d28584623..2f8be540e73e2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -535,6 +535,7 @@ set(files
   __locale_dir/time.h
   __locale_dir/wbuffer_convert.h
   __locale_dir/wstring_convert.h
+  __log_hardening_failure
   __math/abs.h
   __math/copysign.h
   __math/error_functions.h
diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index ae58e36b508b4..cb72e927caa9c 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -304,6 +304,11 @@
 #define _LIBCPP_AVAILABILITY_HAS_VERBOSE_ABORT _LIBCPP_INTRODUCED_IN_LLVM_15
 #define _LIBCPP_AVAILABILITY_VERBOSE_ABORT _LIBCPP_INTRODUCED_IN_LLVM_15_ATTRIBUTE
 
+// This controls whether the library provides a function to log hardening failures without terminating the program (for
+// the `observe` assertion semantic).
+#define _LIBCPP_AVAILABILITY_HAS_LOG_HARDENING_FAILURE _LIBCPP_INTRODUCED_IN_LLVM_21
+#define _LIBCPP_AVAILABILITY_LOG_HARDENING_FAILURE _LIBCPP_INTRODUCED_IN_LLVM_21_ATTRIBUTE
+
 // This controls the availability of the C++17 std::pmr library,
 // which is implemented in large part in the built library.
 //
diff --git a/libcxx/include/__log_hardening_failure b/libcxx/include/__log_hardening_failure
new file mode 100644
index 0000000000000..73cff0ac64155
--- /dev/null
+++ b/libcxx/include/__log_hardening_failure
@@ -0,0 +1,45 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___LOG_HARDENING_FAILURE
+#define _LIBCPP___LOG_HARDENING_FAILURE
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// This function should never be called directly from the code -- it should only be called through the
+// `_LIBCPP_LOG_HARDENING_FAILURE` macro.
+_LIBCPP_AVAILABILITY_LOG_HARDENING_FAILURE _LIBCPP_OVERRIDABLE_FUNC_VIS void
+__libcpp_log_hardening_failure(const char* message) _NOEXCEPT;
+
+// _LIBCPP_LOG_HARDENING_FAILURE(message)
+//
+// This macro is used to log a hardening failure without terminating the program (as is the case if the `observe`
+// assertion semantic is used). Where possible, it logs in a way that indicates a fatal error (which might include
+// capturing the stack trace).
+#if !defined(_LIBCPP_LOG_HARDENING_FAILURE)
+
+#  if !_LIBCPP_AVAILABILITY_HAS_LOG_HARDENING_FAILURE
+// The decltype is there to suppress -Wunused warnings in this configuration.
+void __use(const char*);
+#    define _LIBCPP_LOG_HARDENING_FAILURE(message) (decltype(::std::__use(message))())
+#  else
+#    define _LIBCPP_LOG_HARDENING_FAILURE(message) ::std::__libcpp_log_hardening_failure(message)
+#  endif
+
+#endif // !defined(_LIBCPP_LOG_HARDENING_FAILURE)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___LOG_HARDENING_FAILURE
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 97fe57a5f24f8..926deb3a1c732 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -30,6 +30,7 @@ set(LIBCXX_SOURCES
   include/ryu/ryu.h
   include/to_chars_floating_point.h
   include/from_chars_floating_point.h
+  log_hardening_failure.cpp
   memory.cpp
   memory_resource.cpp
   new_handler.cpp
diff --git a/libcxx/src/log_hardening_failure.cpp b/libcxx/src/log_hardening_failure.cpp
new file mode 100644
index 0000000000000..7e408a6f010b4
--- /dev/null
+++ b/libcxx/src/log_hardening_failure.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__log_hardening_failure>
+#include <cstdio>
+
+#ifdef __BIONIC__
+#  include <syslog.h>
+extern "C" void android_set_abort_message(const char* msg);
+#endif // __BIONIC__
+
+#if defined(__APPLE__) && __has_include(<os/reason_private.h>)
+#  include <os/reason_private.h>
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+_LIBCPP_WEAK void __libcpp_log_hardening_failure(const char* message) noexcept {
+  // On Apple platforms, use the `os_fault_with_payload` OS function that simulates a crash.
+#if defined(__APPLE__) && __has_include(<os/reason_private.h>)
+  os_fault_with_payload(
+      /*reason_namespace=*/OS_REASON_SECURITY,
+      /*reason_code=*/0,
+      /*payload=*/nullptr,
+      /*payload_size=*/0,
+      /*reason_string=*/message,
+      /*reason_flags=*/0);
+
+#elif defined(__BIONIC__)
+  // Show error in tombstone.
+  android_set_abort_message(message);
+
+  // Show error in logcat.
+  openlog("libc++", 0, 0);
+  syslog(LOG_CRIT, "%s", message);
+  closelog();
+
+#else
+  fprintf(stderr, "%s", message);
+#endif
+}
+
+_LIBCPP_END_NAMESPACE_STD

>From debdf1452cb4a3ba5258313f9b5c0fae146dd576 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Fri, 11 Jul 2025 10:58:57 -0700
Subject: [PATCH 3/3] [libc++][hardening] Introduce assertion semantics.

---
 libcxx/include/__config                       |  30 +++++
 libcxx/test/support/check_assertion.h         | 120 ++++++++++++++++--
 .../vendor/llvm/default_assertion_handler.in  |  21 ++-
 libcxxabi/src/CMakeLists.txt                  |  15 +--
 libcxxabi/src/demangle/DemangleConfig.h       |   5 +
 libcxxabi/src/log_error_and_continue.cpp      |  48 +++++++
 libcxxabi/src/log_error_and_continue.h        |  16 +++
 7 files changed, 229 insertions(+), 26 deletions(-)
 create mode 100644 libcxxabi/src/log_error_and_continue.cpp
 create mode 100644 libcxxabi/src/log_error_and_continue.h

diff --git a/libcxx/include/__config b/libcxx/include/__config
index d940461c30234..2223d466c2a7f 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -147,6 +147,36 @@ _LIBCPP_HARDENING_MODE_EXTENSIVE, \
 _LIBCPP_HARDENING_MODE_DEBUG
 #  endif
 
+// Hardening assertion semantics mirror the evaluation semantics of P3100 Contracts:
+// - `ignore` does not evaluate the assertion;
+// - `observe` logs an error (indicating, if possible, that the error is fatal) and continues execution;
+// - `quick-enforce` terminates the program as fast as possible (via trapping);
+// - `enforce` logs an error and then terminates the program.
+// Notes:
+// - Continuing execution after a hardening check fails results in undefined behavior; the `observe` semantic is meant
+//   to make adopting hardening easier but should not be used outside of this scenario;
+// - P3471 "Standard Library Hardening" wording precludes using the Contracts `ignore` semantic for hardened
+//   preconditions in the Library; allowing this semantic to be used is a libc++ vendor extension.
+// clang-format off
+#  define _LIBCPP_ASSERTION_SEMANTIC_IGNORE        (1 << 1)
+#  define _LIBCPP_ASSERTION_SEMANTIC_OBSERVE       (1 << 2)
+#  define _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE (1 << 3)
+#  define _LIBCPP_ASSERTION_SEMANTIC_ENFORCE       (1 << 4)
+// clang-format on
+
+// Allow users to define an arbitrary assertion semantic; otherwise, use the default mapping from modes to semantics.
+// The default is for production-capable modes to use `quick-enforce` (i.e., trap) and for the `debug` mode to use
+// `enforce` (i.e., log and abort).
+#  ifndef _LIBCPP_ASSERTION_SEMANTIC
+
+#    if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+#      define _LIBCPP_ASSERTION_SEMANTIC _LIBCPP_ASSERTION_SEMANTIC_ENFORCE
+#    else
+#      define _LIBCPP_ASSERTION_SEMANTIC _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE
+#    endif
+
+#  endif // _LIBCPP_ASSERTION_SEMANTIC
+
 // } HARDENING
 
 #  define _LIBCPP_TOSTRING2(x) #x
diff --git a/libcxx/test/support/check_assertion.h b/libcxx/test/support/check_assertion.h
index a279400d651b4..7f8102d82970d 100644
--- a/libcxx/test/support/check_assertion.h
+++ b/libcxx/test/support/check_assertion.h
@@ -50,9 +50,17 @@ MatchResult MatchAssertionMessage(const std::string& text, std::string_view expe
   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);
+  // If a non-terminating assertion semantic is used, more than one assertion might be triggered before the process
+  // dies, so we cannot expect the entire target string to match.
+  bool has_match = std::regex_search(text, match_result, assertion_format);
+  if (!has_match || match_result.size() != 5) {
+    std::stringstream matching_error;
+    matching_error                                                     //
+        << "Failed to parse the assertion message.\n"                  //
+        << "Expected message:   '" << expected_message.data() << "'\n" //
+        << "Stderr contents:    '" << text.c_str() << "'\n";
+    return MatchResult(/*success=*/false, matching_error.str());
+  }
 
   const std::string& file = match_result[1];
   int line                = std::stoi(match_result[2]);
@@ -85,13 +93,16 @@ Matcher MakeAnyMatcher() {
 }
 
 enum class DeathCause {
-  // Valid causes
+  // Valid causes.
   VerboseAbort = 1,
   StdAbort,
   StdTerminate,
   Trap,
-  // Invalid causes
+  // Causes that might be invalid or might stem from undefined behavior (relevant for non-terminating assertion
+  // semantics).
   DidNotDie,
+  Segfault,
+  // Always invalid causes.
   SetupFailure,
   Unknown
 };
@@ -108,6 +119,16 @@ bool IsValidCause(DeathCause cause) {
   }
 }
 
+bool IsTestSetupErrorCause(DeathCause cause) {
+  switch (cause) {
+  case DeathCause::SetupFailure:
+  case DeathCause::Unknown:
+    return true;
+  default:
+    return false;
+  }
+}
+
 std::string ToString(DeathCause cause) {
   switch (cause) {
   case DeathCause::VerboseAbort:
@@ -120,10 +141,12 @@ std::string ToString(DeathCause cause) {
     return "trap";
   case DeathCause::DidNotDie:
     return "<invalid cause (child did not die)>";
+  case DeathCause::Segfault:
+    return "<invalid cause (segmentation fault)>";
   case DeathCause::SetupFailure:
-    return "<invalid cause (child failed to set up test environment)>";
+    return "<test setup error (child failed to set up test environment)>";
   case DeathCause::Unknown:
-    return "<invalid cause (cause unknown)>";
+    return "<test setup error (test doesn't know how to interpret the death cause)>";
   }
 
   assert(false && "Unreachable");
@@ -225,9 +248,38 @@ class DeathTest {
     return DeathTestResult(Outcome::Success, cause);
   }
 
-  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());
+  // When non-terminating assertion semantics are used, the program will invoke UB which might or might not crash the
+  // process; we make sure that the execution produces the expected error message but otherwise consider the test run
+  // successful whether the child process dies or not.
+  template <class Func>
+  DeathTestResult RunWithoutGuaranteedDeath(Func&& func, const Matcher& matcher) {
+    std::signal(SIGABRT, [](int) { StopChildProcess(DeathCause::StdAbort); });
+    std::set_terminate([] { StopChildProcess(DeathCause::StdTerminate); });
+
+    DeathCause cause = Run(func);
+
+    if (IsTestSetupErrorCause(cause)) {
+      return DeathTestResult(Outcome::InvalidCause, cause, ToString(cause));
+    }
+
+    MatchResult match_result = matcher(GetChildStdErr());
+    if (!match_result.first) {
+      auto failure_description = std::string("Child produced a different error message\n") + match_result.second;
+      return DeathTestResult(Outcome::UnexpectedErrorMessage, cause, failure_description);
+    }
+
+    return DeathTestResult(Outcome::Success, cause);
+  }
+
+  void PrintFailureDetails(std::string_view invocation,
+                           std::string_view failure_description,
+                           std::string_view stmt,
+                           DeathCause cause) const {
+    std::fprintf(stderr,
+                 "Failure: %s( %s ) failed!\n(reason: %s)\n\n",
+                 invocation.data(),
+                 stmt.data(),
+                 failure_description.data());
 
     if (cause != DeathCause::Unknown) {
       std::fprintf(stderr, "child exit code: %d\n", GetChildExitCode());
@@ -311,10 +363,13 @@ class DeathTest {
 
     if (WIFSIGNALED(status_value)) {
       exit_code_ = WTERMSIG(status_value);
-      // `__builtin_trap` generqtes `SIGILL` on x86 and `SIGTRAP` on ARM.
+      // `__builtin_trap` generates `SIGILL` on x86 and `SIGTRAP` on ARM.
       if (exit_code_ == SIGILL || exit_code_ == SIGTRAP) {
         return DeathCause::Trap;
       }
+      if (exit_code_ == SIGSEGV) {
+        return DeathCause::Segfault;
+      }
     }
 
     return DeathCause::Unknown;
@@ -334,6 +389,12 @@ class DeathTest {
 };
 
 #ifdef _LIBCPP_VERSION
+void std::__libcpp_log_hardening_failure(const char* message) noexcept {
+  std::fprintf(stderr, "%s\n", Marker);
+  std::fprintf(stderr, "%s", message);
+  std::fprintf(stderr, "%s\n", Marker);
+}
+
 void std::__libcpp_verbose_abort(char const* format, ...) noexcept {
   va_list args;
   va_start(args, format);
@@ -357,7 +418,7 @@ bool ExpectDeath(
   DeathTest test_case;
   DeathTestResult test_result = test_case.Run(expected_causes, func, matcher);
   if (!test_result.success()) {
-    test_case.PrintFailureDetails(test_result.failure_description(), stmt, test_result.cause());
+    test_case.PrintFailureDetails("EXPECT_DEATH", test_result.failure_description(), stmt, test_result.cause());
   }
 
   return test_result.success();
@@ -378,6 +439,22 @@ bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func) {
   return ExpectDeath(std::array<DeathCause, 1>{expected_cause}, stmt, func, MakeAnyMatcher());
 }
 
+template <class Func>
+bool ExpectLog(const char* stmt, Func&& func, const Matcher& matcher) {
+  DeathTest test_case;
+  DeathTestResult test_result = test_case.RunWithoutGuaranteedDeath(func, matcher);
+  if (!test_result.success()) {
+    test_case.PrintFailureDetails("EXPECT_LOG", test_result.failure_description(), stmt, test_result.cause());
+  }
+
+  return test_result.success();
+}
+
+template <class Func>
+bool ExpectLog(const char* stmt, Func&& func) {
+  return ExpectLog(stmt, func, MakeAnyMatcher());
+}
+
 // clang-format off
 
 /// Assert that the specified expression aborts with the expected cause and, optionally, error message.
@@ -392,13 +469,28 @@ bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func) {
 #define EXPECT_STD_TERMINATE(...)                 \
     assert(  ExpectDeath(DeathCause::StdTerminate, #__VA_ARGS__, __VA_ARGS__)  )
 
-#if defined(_LIBCPP_HARDENING_MODE) && _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+#if defined(_LIBCPP_ASSERTION_SEMANTIC)
+
+#if _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_ENFORCE
 #define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
     assert(( ExpectDeath(DeathCause::VerboseAbort, #expr, [&]() { (void)(expr); }, MakeAssertionMessageMatcher(message)) ))
+#elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE
+#define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
+    assert(( ExpectDeath(DeathCause::Trap,         #expr, [&]() { (void)(expr); }) ))
+#elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_OBSERVE
+#define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
+    assert(( ExpectLog(#expr, [&]() { (void)(expr); }, MakeAssertionMessageMatcher(message)) ))
+#elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_IGNORE
+#define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
+    assert(( ExpectLog(#expr, [&]() { (void)(expr); }) ))
+#else
+#error "_LIBCPP_ASSERTION_SEMANTIC is set to an invalid value"
+#endif // _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_ENFORCE
+
 #else
 #define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
     assert(( ExpectDeath(DeathCause::Trap,         #expr, [&]() { (void)(expr); }) ))
-#endif // _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+#endif // defined(_LIBCPP_ASSERTION_SEMANTIC)
 
 // clang-format on
 
diff --git a/libcxx/vendor/llvm/default_assertion_handler.in b/libcxx/vendor/llvm/default_assertion_handler.in
index 90b202a2dae57..4ece031c03ece 100644
--- a/libcxx/vendor/llvm/default_assertion_handler.in
+++ b/libcxx/vendor/llvm/default_assertion_handler.in
@@ -15,22 +15,35 @@
 #  include <__cxx03/__verbose_abort>
 #else
 #  include <__config>
-#  include <__verbose_trap>
+#  include <__log_hardening_failure>
 #  include <__verbose_abort>
+#  include <__verbose_trap>
 #endif
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
 
-#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+#if _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_IGNORE
+#  define _LIBCPP_ASSERTION_HANDLER(message) ((void)0)
+
+#elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_OBSERVE
+#  define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_LOG_HARDENING_FAILURE(message)
 
+#elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE
+#  define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_TRAP(message)
+
+#elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_ENFORCE
 #  define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_ABORT("%s", message)
 
 #else
 
-#  define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_TRAP(message)
+#  error _LIBCPP_ASSERTION_SEMANTIC must be set to one of the following values: \
+_LIBCPP_ASSERTION_SEMANTIC_IGNORE, \
+_LIBCPP_ASSERTION_SEMANTIC_OBSERVE, \
+_LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE, \
+_LIBCPP_ASSERTION_SEMANTIC_ENFORCE
 
-#endif // _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+#endif // _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_IGNORE
 
 #endif // _LIBCPP___ASSERTION_HANDLER
diff --git a/libcxxabi/src/CMakeLists.txt b/libcxxabi/src/CMakeLists.txt
index 0675577aed6a1..9a9a65ce3aaa1 100644
--- a/libcxxabi/src/CMakeLists.txt
+++ b/libcxxabi/src/CMakeLists.txt
@@ -16,6 +16,7 @@ set(LIBCXXABI_SOURCES
   # Internal files
   abort_message.cpp
   fallback_malloc.cpp
+  log_error_and_continue.cpp
   private_typeinfo.cpp
 )
 
@@ -172,12 +173,11 @@ if (LIBCXXABI_USE_LLVM_UNWINDER)
     target_link_libraries(cxxabi_shared_objects PUBLIC unwind_shared)
   endif()
 endif()
-target_link_libraries(cxxabi_shared_objects
-  PUBLIC cxxabi-headers
-  PRIVATE cxx-headers runtimes-libc-headers ${LIBCXXABI_LIBRARIES})
+target_link_libraries(cxxabi_shared_objects PRIVATE cxx-headers ${LIBCXXABI_LIBRARIES})
 if (NOT CXX_SUPPORTS_NOSTDLIBXX_FLAG)
   target_link_libraries(cxxabi_shared_objects PRIVATE ${LIBCXXABI_BUILTINS_LIBRARY})
 endif()
+target_link_libraries(cxxabi_shared_objects PUBLIC cxxabi-headers)
 set_target_properties(cxxabi_shared_objects
   PROPERTIES
     CXX_EXTENSIONS OFF
@@ -216,7 +216,7 @@ if (ZOS)
 endif ()
 
 target_link_libraries(cxxabi_shared
-  PUBLIC cxxabi_shared_objects runtimes-libc-shared
+  PUBLIC cxxabi_shared_objects
   PRIVATE ${LIBCXXABI_LIBRARIES})
 
 if (LIBCXXABI_ENABLE_SHARED)
@@ -275,9 +275,8 @@ if (LIBCXXABI_USE_LLVM_UNWINDER AND LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_STATIC
   target_link_libraries(cxxabi_static_objects PUBLIC unwind_static_objects) # propagate usage requirements
   target_sources(cxxabi_static_objects PUBLIC $<TARGET_OBJECTS:unwind_static_objects>)
 endif()
-target_link_libraries(cxxabi_static_objects
-  PUBLIC cxxabi-headers
-  PRIVATE cxx-headers runtimes-libc-headers ${LIBCXXABI_STATIC_LIBRARIES} ${LIBCXXABI_LIBRARIES})
+target_link_libraries(cxxabi_static_objects PRIVATE cxx-headers ${LIBCXXABI_STATIC_LIBRARIES} ${LIBCXXABI_LIBRARIES})
+target_link_libraries(cxxabi_static_objects PUBLIC cxxabi-headers)
 set_target_properties(cxxabi_static_objects
   PROPERTIES
     CXX_EXTENSIONS OFF
@@ -313,7 +312,7 @@ endif()
 
 add_library(cxxabi_static STATIC)
 if (LIBCXXABI_USE_LLVM_UNWINDER AND NOT LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_STATIC_LIBRARY)
-  target_link_libraries(cxxabi_static PUBLIC unwind_static runtimes-libc-static)
+  target_link_libraries(cxxabi_static PUBLIC unwind_static)
 endif()
 set_target_properties(cxxabi_static
   PROPERTIES
diff --git a/libcxxabi/src/demangle/DemangleConfig.h b/libcxxabi/src/demangle/DemangleConfig.h
index 06fd223f5553f..994d59507588f 100644
--- a/libcxxabi/src/demangle/DemangleConfig.h
+++ b/libcxxabi/src/demangle/DemangleConfig.h
@@ -19,6 +19,11 @@
 #include "../abort_message.h"
 #endif
 
+#ifndef _LIBCPP_LOG_HARDENING_FAILURE
+#define _LIBCPP_LOG_HARDENING_FAILURE(message) __log_error_and_continue(message)
+#include "../log_error_and_continue.h"
+#endif
+
 #include <version>
 
 #ifdef _MSC_VER
diff --git a/libcxxabi/src/log_error_and_continue.cpp b/libcxxabi/src/log_error_and_continue.cpp
new file mode 100644
index 0000000000000..51088cd11d914
--- /dev/null
+++ b/libcxxabi/src/log_error_and_continue.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include "log_error_and_continue.h"
+
+#ifdef __BIONIC__
+#  include <syslog.h>
+extern "C" void android_set_abort_message(const char* msg);
+#endif // __BIONIC__
+
+#if defined(__APPLE__) && __has_include(<os/reason_private.h>)
+#  include <os/reason_private.h>
+#   define _LIBCXXABI_USE_OS_FAULT
+#endif
+
+void __log_error_and_continue(const char* message)
+{
+  // On Apple platforms, use the `os_fault_with_payload` OS function that simulates a crash.
+#if defined(_LIBCXXABI_USE_OS_FAULT)
+  os_fault_with_payload(
+      /*reason_namespace=*/OS_REASON_SECURITY,
+      /*reason_code=*/0,
+      /*payload=*/nullptr,
+      /*payload_size=*/0,
+      /*reason_string=*/message,
+      /*reason_flags=*/0);
+
+#elif defined(__BIONIC__)
+  // Show error in tombstone.
+  android_set_abort_message(message);
+
+  // Show error in logcat.
+  openlog("libc++", 0, 0);
+  syslog(LOG_CRIT, "%s", message);
+  closelog();
+
+#else
+  fprintf(stderr, "%s", message);
+#endif
+}
diff --git a/libcxxabi/src/log_error_and_continue.h b/libcxxabi/src/log_error_and_continue.h
new file mode 100644
index 0000000000000..e976c1de65d06
--- /dev/null
+++ b/libcxxabi/src/log_error_and_continue.h
@@ -0,0 +1,16 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __LOG_ERROR_AND_CONTINUE_H_
+#define __LOG_ERROR_AND_CONTINUE_H_
+
+#include "cxxabi.h"
+
+extern "C" _LIBCXXABI_HIDDEN void __log_error_and_continue(const char* message);
+
+#endif // __LOG_ERROR_AND_CONTINUE_H_



More information about the libcxx-commits mailing list