From 0b6facec5dd01bf70a26609cdd1bb114dea6130d Mon Sep 17 00:00:00 2001 From: Konstantin Varlamov Date: Fri, 11 Jul 2025 01:54:50 -0700 Subject: [PATCH] [libc++][hardening] Implement support for assertion semantics. --- libcxx/include/CMakeLists.txt | 2 + libcxx/include/__config | 32 ++++- libcxx/include/__configuration/availability.h | 5 + libcxx/include/__log_hardening_failure | 45 +++++++ libcxx/include/__verbose_trap | 37 ++++++ libcxx/src/CMakeLists.txt | 1 + libcxx/src/log_hardening_failure.cpp | 49 +++++++ libcxx/test/support/check_assertion.h | 120 ++++++++++++++++-- .../vendor/llvm/default_assertion_handler.in | 33 ++--- libcxxabi/src/CMakeLists.txt | 1 + libcxxabi/src/demangle/DemangleConfig.h | 5 + libcxxabi/src/log_error_and_continue.cpp | 48 +++++++ libcxxabi/src/log_error_and_continue.h | 16 +++ 13 files changed, 364 insertions(+), 30 deletions(-) create mode 100644 libcxx/include/__log_hardening_failure create mode 100644 libcxx/include/__verbose_trap create mode 100644 libcxx/src/log_hardening_failure.cpp 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/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 248aed666e27e..a65b78a283919 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 @@ -944,6 +945,7 @@ set(files __vector/vector_bool.h __vector/vector_bool_formatter.h __verbose_abort + __verbose_trap algorithm any array diff --git a/libcxx/include/__config b/libcxx/include/__config index 58ca9bab937e4..1d2ac43c5ba85 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 @@ -489,7 +519,7 @@ typedef __char32_t char32_t; # ifndef _LIBCPP_NO_ABI_TAG # define _LIBCPP_HIDE_FROM_ABI \ _LIBCPP_HIDDEN _LIBCPP_EXCLUDE_FROM_EXPLICIT_INSTANTIATION \ - __attribute__((__abi_tag__(_LIBCPP_TOSTRING(_LIBCPP_ODR_SIGNATURE)))) + __attribute__((__abi_tag__(_LIBCPP_TOSTRING(_LIBCPP_ODR_SIGNATURE)))) # else # define _LIBCPP_HIDE_FROM_ABI _LIBCPP_HIDDEN _LIBCPP_EXCLUDE_FROM_EXPLICIT_INSTANTIATION # endif 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/include/__verbose_trap b/libcxx/include/__verbose_trap new file mode 100644 index 0000000000000..db77b29e69062 --- /dev/null +++ b/libcxx/include/__verbose_trap @@ -0,0 +1,37 @@ +// -*- 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/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt index 4e9bf900af4c5..e1506c720d431 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 + +#ifdef __BIONIC__ +# include +extern "C" void android_set_abort_message(const char* msg); +#endif // __BIONIC__ + +#if defined(__APPLE__) && __has_include() +# include +#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_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 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 ""; + case DeathCause::Segfault: + return ""; case DeathCause::SetupFailure: - return ""; + return ""; case DeathCause::Unknown: - return ""; + return ""; } 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 + 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{expected_cause}, stmt, func, MakeAnyMatcher()); } +template +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 +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 1d6b21fc6bb45..4ece031c03ece 100644 --- a/libcxx/vendor/llvm/default_assertion_handler.in +++ b/libcxx/vendor/llvm/default_assertion_handler.in @@ -15,32 +15,35 @@ # include <__cxx03/__verbose_abort> #else # include <__config> +# 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 -# 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 - -#endif // _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG +# 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_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_IGNORE #endif // _LIBCPP___ASSERTION_HANDLER diff --git a/libcxxabi/src/CMakeLists.txt b/libcxxabi/src/CMakeLists.txt index 0a6fc892a4f69..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 ) 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 #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 +#include +#include +#include "log_error_and_continue.h" + +#ifdef __BIONIC__ +# include +extern "C" void android_set_abort_message(const char* msg); +#endif // __BIONIC__ + +#if defined(__APPLE__) && __has_include() +# include +# 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_