[libcxx-commits] [libcxx] [libc++][hardening] Introduce a dylib function to log hardening errors. (PR #149452)
Konstantin Varlamov via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Jul 22 02:54:32 PDT 2025
https://github.com/var-const updated https://github.com/llvm/llvm-project/pull/149452
>From afc01467a27ed4650d43815f9a41bbfac91ea7a1 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Thu, 17 Jul 2025 21:57:49 -0700
Subject: [PATCH] [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 <features.h> // 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 <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 <TargetConditionals.h>
+# include <os/reason_private.h>
+#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(<os/reason_private.h>) && !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.
More information about the libcxx-commits
mailing list