From afc01467a27ed4650d43815f9a41bbfac91ea7a1 Mon Sep 17 00:00:00 2001 From: Konstantin Varlamov Date: Thu, 17 Jul 2025 21:57:49 -0700 Subject: [PATCH 1/4] [libc++][hardening] Add an experimental 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/__config | 1 + libcxx/include/__log_hardening_failure | 40 ++++++++++++++ libcxx/include/module.modulemap.in | 3 ++ libcxx/src/CMakeLists.txt | 1 + .../experimental/log_hardening_failure.cpp | 54 +++++++++++++++++++ .../assertions/log_hardening_failure.pass.cpp | 27 ++++++++++ .../fexperimental-library.compile.pass.cpp | 4 ++ libcxx/utils/libcxx/test/params.py | 1 + 9 files changed, 132 insertions(+) create mode 100644 libcxx/include/__log_hardening_failure create mode 100644 libcxx/src/experimental/log_hardening_failure.cpp create mode 100644 libcxx/test/libcxx/assertions/log_hardening_failure.pass.cpp diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 25b567df2dd33..4a6a61b640a48 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/__config b/libcxx/include/__config index ee06abfba7a08..c867b4bcc0f1f 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -207,6 +207,7 @@ _LIBCPP_HARDENING_MODE_DEBUG # define _LIBCPP_HAS_EXPERIMENTAL_PSTL _LIBCPP_HAS_EXPERIMENTAL_LIBRARY # define _LIBCPP_HAS_EXPERIMENTAL_TZDB _LIBCPP_HAS_EXPERIMENTAL_LIBRARY # define _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM _LIBCPP_HAS_EXPERIMENTAL_LIBRARY +# define _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC _LIBCPP_HAS_EXPERIMENTAL_LIBRARY # if defined(__MVS__) # include // for __NATIVE_ASCII_F diff --git a/libcxx/include/__log_hardening_failure b/libcxx/include/__log_hardening_failure new file mode 100644 index 0000000000000..76da1153d78b2 --- /dev/null +++ b/libcxx/include/__log_hardening_failure @@ -0,0 +1,40 @@ +// -*- 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 + +#if _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC + +_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_EXPORTED_FROM_ABI void __log_hardening_failure(const char* __message) _NOEXCEPT; + +// _LIBCPP_LOG_HARDENING_FAILURE(message) +// +// This macro is used to log an error without terminating the program (as is the case for hardening failures if the +// `observe` assertion semantic is used). + +# if !defined(_LIBCPP_LOG_HARDENING_FAILURE) +# define _LIBCPP_LOG_HARDENING_FAILURE(__message) ::std::__log_hardening_failure(__message) +# endif // !defined(_LIBCPP_LOG_HARDENING_FAILURE) + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC + +#endif // _LIBCPP___LOG_HARDENING_FAILURE diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index 78607f2c1301d..9ee964c0069f4 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -2353,6 +2353,9 @@ module std [system] { header "__std_mbstate_t.h" export * } + module log_hardening_failure { + header "__log_hardening_failure" + } module verbose_abort { header "__verbose_abort" } diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt index 97fe57a5f24f8..f59fe0e08fccb 100644 --- a/libcxx/src/CMakeLists.txt +++ b/libcxx/src/CMakeLists.txt @@ -309,6 +309,7 @@ add_custom_target(cxx DEPENDS ${LIBCXX_BUILD_TARGETS}) # Build the experimental static library set(LIBCXX_EXPERIMENTAL_SOURCES experimental/keep.cpp + experimental/log_hardening_failure.cpp ) if (LIBCXX_PSTL_BACKEND STREQUAL "libdispatch") diff --git a/libcxx/src/experimental/log_hardening_failure.cpp b/libcxx/src/experimental/log_hardening_failure.cpp new file mode 100644 index 0000000000000..274175c1acaa8 --- /dev/null +++ b/libcxx/src/experimental/log_hardening_failure.cpp @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// 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 +# include +#endif + +#if _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC + +_LIBCPP_BEGIN_NAMESPACE_STD + +void __log_hardening_failure(const char* message) noexcept { + // Always log the message to `stderr` in case the platform-specific system calls fail. + std::fputs(message, stderr); + + // On Apple platforms, use the `os_fault_with_payload` OS function that simulates a crash. +# if defined(__APPLE__) && __has_include() && !TARGET_OS_SIMULATOR + 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. The latter two arguments are ignored on Android. + openlog("libc++", 0, 0); + syslog(LOG_CRIT, "%s", message); + closelog(); +# endif +} + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC diff --git a/libcxx/test/libcxx/assertions/log_hardening_failure.pass.cpp b/libcxx/test/libcxx/assertions/log_hardening_failure.pass.cpp new file mode 100644 index 0000000000000..d13b1a0a9c13a --- /dev/null +++ b/libcxx/test/libcxx/assertions/log_hardening_failure.pass.cpp @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Basic smoke test for `__log_hardening_failure`. +// +// UNSUPPORTED: libcpp-has-no-experimental-hardening-observe-semantic +// XFAIL: availability-log_hardening_failure-missing +// UNSUPPORTED: FROZEN-CXX03-HEADERS-FIXME + +#include <__log_hardening_failure> + +#include "test_macros.h" + +ASSERT_NOEXCEPT(std::__log_hardening_failure("")); + +int main(int, char**) { + std::__log_hardening_failure("Some message"); + // It's difficult to properly test platform-specific logging behavior of the function; just make sure it exists and + // can be called at runtime. + + return 0; +} diff --git a/libcxx/test/libcxx/experimental/fexperimental-library.compile.pass.cpp b/libcxx/test/libcxx/experimental/fexperimental-library.compile.pass.cpp index 3cf497da233fb..3d97446ffe826 100644 --- a/libcxx/test/libcxx/experimental/fexperimental-library.compile.pass.cpp +++ b/libcxx/test/libcxx/experimental/fexperimental-library.compile.pass.cpp @@ -29,3 +29,7 @@ #if !_LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM # error "-fexperimental-library should enable the syncstream header" #endif + +#if !_LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC +# error "-fexperimental-library should allow using the Hardening observe semantic" +#endif diff --git a/libcxx/utils/libcxx/test/params.py b/libcxx/utils/libcxx/test/params.py index adfb2a9f69508..93cf29bcdff0d 100644 --- a/libcxx/utils/libcxx/test/params.py +++ b/libcxx/utils/libcxx/test/params.py @@ -361,6 +361,7 @@ def getSuitableClangTidy(cfg): AddFeature("libcpp-has-no-incomplete-pstl"), AddFeature("libcpp-has-no-experimental-tzdb"), AddFeature("libcpp-has-no-experimental-syncstream"), + AddFeature("libcpp-has-no-experimental-hardening-observe-semantic"), ], ), # TODO: This can be improved once we use a version of GoogleBenchmark that supports the dry-run mode. From 2aa530eb6d78011fd3e398a84fc7c3de3deb5479 Mon Sep 17 00:00:00 2001 From: Konstantin Varlamov Date: Wed, 23 Jul 2025 02:18:44 -0700 Subject: [PATCH 2/4] Address feedback --- libcxx/src/experimental/log_hardening_failure.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libcxx/src/experimental/log_hardening_failure.cpp b/libcxx/src/experimental/log_hardening_failure.cpp index 274175c1acaa8..f85e5d58d9dcd 100644 --- a/libcxx/src/experimental/log_hardening_failure.cpp +++ b/libcxx/src/experimental/log_hardening_failure.cpp @@ -20,8 +20,6 @@ extern "C" void android_set_abort_message(const char* msg); # include #endif -#if _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC - _LIBCPP_BEGIN_NAMESPACE_STD void __log_hardening_failure(const char* message) noexcept { @@ -50,5 +48,3 @@ void __log_hardening_failure(const char* message) noexcept { } _LIBCPP_END_NAMESPACE_STD - -#endif // _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC From 108ca7ea8aec98981350edf3d8fbe70a73e4bf41 Mon Sep 17 00:00:00 2001 From: Konstantin Varlamov Date: Wed, 23 Jul 2025 16:59:33 -0700 Subject: [PATCH 3/4] Address feedback --- libcxx/include/__log_hardening_failure | 8 ++++--- .../experimental/log_hardening_failure.cpp | 23 ++----------------- .../assertions/log_hardening_failure.pass.cpp | 3 +-- 3 files changed, 8 insertions(+), 26 deletions(-) diff --git a/libcxx/include/__log_hardening_failure b/libcxx/include/__log_hardening_failure index 76da1153d78b2..8bc0d6d96fffc 100644 --- a/libcxx/include/__log_hardening_failure +++ b/libcxx/include/__log_hardening_failure @@ -16,13 +16,15 @@ # pragma GCC system_header #endif -#if _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC +// Hardening logging is not available in the C++03 mode; moreover, it is currently only available in the experimental +// library. +#if _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC && !defined(_LIBCPP_CXX03_LANG) _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_EXPORTED_FROM_ABI void __log_hardening_failure(const char* __message) _NOEXCEPT; +[[__gnu__::__cold__]] _LIBCPP_EXPORTED_FROM_ABI void __log_hardening_failure(const char* __message) _NOEXCEPT; // _LIBCPP_LOG_HARDENING_FAILURE(message) // @@ -35,6 +37,6 @@ _LIBCPP_EXPORTED_FROM_ABI void __log_hardening_failure(const char* __message) _N _LIBCPP_END_NAMESPACE_STD -#endif // _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC +#endif // _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC && !defined(_LIBCPP_CXX03_LANG) #endif // _LIBCPP___LOG_HARDENING_FAILURE diff --git a/libcxx/src/experimental/log_hardening_failure.cpp b/libcxx/src/experimental/log_hardening_failure.cpp index f85e5d58d9dcd..f836c15452249 100644 --- a/libcxx/src/experimental/log_hardening_failure.cpp +++ b/libcxx/src/experimental/log_hardening_failure.cpp @@ -12,39 +12,20 @@ #ifdef __BIONIC__ # include -extern "C" void android_set_abort_message(const char* msg); #endif // __BIONIC__ -#if defined(__APPLE__) && __has_include() -# include -# include -#endif - _LIBCPP_BEGIN_NAMESPACE_STD void __log_hardening_failure(const char* message) noexcept { // Always log the message to `stderr` in case the platform-specific system calls fail. std::fputs(message, stderr); - // On Apple platforms, use the `os_fault_with_payload` OS function that simulates a crash. -# if defined(__APPLE__) && __has_include() && !TARGET_OS_SIMULATOR - 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); - +#if defined(__BIONIC__) // Show error in logcat. The latter two arguments are ignored on Android. openlog("libc++", 0, 0); syslog(LOG_CRIT, "%s", message); closelog(); -# endif +#endif } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/test/libcxx/assertions/log_hardening_failure.pass.cpp b/libcxx/test/libcxx/assertions/log_hardening_failure.pass.cpp index d13b1a0a9c13a..dda071b8d0296 100644 --- a/libcxx/test/libcxx/assertions/log_hardening_failure.pass.cpp +++ b/libcxx/test/libcxx/assertions/log_hardening_failure.pass.cpp @@ -8,9 +8,8 @@ // Basic smoke test for `__log_hardening_failure`. // +// UNSUPPORTED: c++03 // UNSUPPORTED: libcpp-has-no-experimental-hardening-observe-semantic -// XFAIL: availability-log_hardening_failure-missing -// UNSUPPORTED: FROZEN-CXX03-HEADERS-FIXME #include <__log_hardening_failure> From 53190bc0247630dbb9a843cc4645783e91b9eebf Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Thu, 24 Jul 2025 13:38:07 -0400 Subject: [PATCH 4/4] Update libcxx/include/__log_hardening_failure --- libcxx/include/__log_hardening_failure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/include/__log_hardening_failure b/libcxx/include/__log_hardening_failure index 8bc0d6d96fffc..d1805306f6b6e 100644 --- a/libcxx/include/__log_hardening_failure +++ b/libcxx/include/__log_hardening_failure @@ -24,7 +24,7 @@ _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. -[[__gnu__::__cold__]] _LIBCPP_EXPORTED_FROM_ABI void __log_hardening_failure(const char* __message) _NOEXCEPT; +[[__gnu__::__cold__]] _LIBCPP_EXPORTED_FROM_ABI void __log_hardening_failure(const char* __message) noexcept; // _LIBCPP_LOG_HARDENING_FAILURE(message) //