[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