[libcxx-commits] [libcxx] 8dfc67d - [libc++][hardening] Rework how the assertion handler can be overridden. (#77883)

via libcxx-commits libcxx-commits at lists.llvm.org
Wed Jan 17 18:56:12 PST 2024


Author: Konstantin Varlamov
Date: 2024-01-17T18:56:07-08:00
New Revision: 8dfc67d6724eb0af5d278f4d1d5511ca9f9e039f

URL: https://github.com/llvm/llvm-project/commit/8dfc67d6724eb0af5d278f4d1d5511ca9f9e039f
DIFF: https://github.com/llvm/llvm-project/commit/8dfc67d6724eb0af5d278f4d1d5511ca9f9e039f.diff

LOG: [libc++][hardening] Rework how the assertion handler can be overridden. (#77883)

Previously there were two ways to override the verbose abort function
which gets called when a hardening assertion is triggered:
- compile-time: define the `_LIBCPP_VERBOSE_ABORT` macro;
- link-time: provide a definition of `__libcpp_verbose_abort` function.

This patch adds a new configure-time approach: the vendor can provide
a path to a custom header file which will get copied into the build by
CMake and included by the library. The header must provide a definition
of the
`_LIBCPP_ASSERTION_HANDLER` macro which is what will get called should
a hardening assertion fail. As of this patch, overriding
`_LIBCPP_VERBOSE_ABORT` will still work, but the previous mechanisms
will be effectively removed in a follow-up patch, making the
configure-time mechanism the sole way of overriding the default handler.

Note that `_LIBCPP_ASSERTION_HANDLER` only gets invoked when a hardening
assertion fails. It does not affect other cases where
`_LIBCPP_VERBOSE_ABORT` is currently used (e.g. when an exception is
thrown in the `-fno-exceptions` mode).

The library provides a default version of the custom header file that
will get used if it's not overridden by the vendor. That allows us to
always test the override mechanism and reduces the difference in
configuration between the pristine version of the library and
a platform-specific version.

Added: 
    libcxx/vendor/llvm/default_assertion_handler.in

Modified: 
    libcxx/CMakeLists.txt
    libcxx/docs/BuildingLibcxx.rst
    libcxx/docs/ReleaseNotes/18.rst
    libcxx/docs/UsingLibcxx.rst
    libcxx/include/CMakeLists.txt
    libcxx/include/__assert
    libcxx/test/libcxx/assertions/modes/enabling_assertions_enables_extensive_mode.pass.cpp
    libcxx/test/libcxx/assertions/modes/override_with_debug_mode.pass.cpp
    libcxx/test/libcxx/assertions/modes/override_with_extensive_mode.pass.cpp
    libcxx/test/libcxx/assertions/modes/override_with_fast_mode.pass.cpp
    libcxx/test/libcxx/assertions/modes/override_with_unchecked_mode.pass.cpp
    libcxx/test/libcxx/containers/sequences/deque/asan_caterpillar.pass.cpp
    libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/pstl.exception_handling.pass.cpp
    libcxx/test/std/algorithms/alg.modifying.operations/alg.move/pstl.exception_handling.pass.cpp
    libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.exception_handling.pass.cpp
    libcxx/test/std/algorithms/alg.modifying.operations/alg.rotate/pstl.exception_handling.pass.cpp
    libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.exception_handling.pass.cpp
    libcxx/test/std/algorithms/alg.nonmodifying/alg.all_of/pstl.exception_handling.pass.cpp
    libcxx/test/std/algorithms/alg.nonmodifying/alg.any_of/pstl.exception_handling.pass.cpp
    libcxx/test/std/algorithms/alg.nonmodifying/alg.equal/pstl.exception_handling.pass.cpp
    libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.exception_handling.pass.cpp
    libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/pstl.exception_handling.pass.cpp
    libcxx/test/std/algorithms/alg.nonmodifying/alg.none_of/pstl.exception_handling.pass.cpp
    libcxx/test/std/algorithms/alg.sorting/alg.merge/pstl.exception_handling.pass.cpp
    libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/pstl.exception_handling.pass.cpp
    libcxx/test/std/algorithms/numeric.ops/reduce/pstl.exception_handling.pass.cpp
    libcxx/test/std/algorithms/numeric.ops/transform.reduce/pstl.exception_handling.pass.cpp
    libcxx/test/support/check_assertion.h
    libcxx/utils/libcxx/header_information.py

Removed: 
    


################################################################################
diff  --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index d0abd8413f1b2b..53de53480266b0 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -69,6 +69,13 @@ if (NOT "${LIBCXX_HARDENING_MODE}" IN_LIST LIBCXX_SUPPORTED_HARDENING_MODES)
   message(FATAL_ERROR
     "Unsupported hardening mode: '${LIBCXX_HARDENING_MODE}'. Supported values are ${LIBCXX_SUPPORTED_HARDENING_MODES}.")
 endif()
+set(LIBCXX_ASSERTION_HANDLER_FILE
+  "${CMAKE_CURRENT_SOURCE_DIR}/vendor/llvm/default_assertion_handler.in"
+  CACHE STRING
+  "Specify the path to a header that contains a custom implementation of the
+   assertion handler that gets invoked when a hardening assertion fails. If
+   provided, this header will be included by the library, replacing the
+   default assertion handler.")
 option(LIBCXX_ENABLE_RANDOM_DEVICE
   "Whether to include support for std::random_device in the library. Disabling
    this can be useful when building the library for platforms that don't have

diff  --git a/libcxx/docs/BuildingLibcxx.rst b/libcxx/docs/BuildingLibcxx.rst
index f2304c9796b540..11e250e3f3735a 100644
--- a/libcxx/docs/BuildingLibcxx.rst
+++ b/libcxx/docs/BuildingLibcxx.rst
@@ -409,6 +409,15 @@ libc++ Feature Options
   Use the specified GCC toolchain and standard library when building the native
   stdlib benchmark tests.
 
+.. option:: LIBCXX_ASSERTION_HANDLER_FILE:PATH
+
+  **Default**:: ``"${CMAKE_CURRENT_SOURCE_DIR}/vendor/llvm/default_assertion_handler.in"``
+
+  Specify the path to a header that contains a custom implementation of the
+  assertion handler that gets invoked when a hardening assertion fails. If
+  provided, this header will be included by the library, replacing the
+  default assertion handler.
+
 
 libc++ ABI Feature Options
 --------------------------
@@ -473,6 +482,43 @@ LLVM-specific options
   others.
 
 
+.. _assertion-handler:
+
+Overriding the default assertion handler
+==========================================
+
+When the library wants to terminate due to an unforeseen condition (such as
+a hardening assertion failure), the program is aborted through a special verbose
+termination function. The library provides a default function that prints an
+error message and calls ``std::abort()``. Note that this function is provided by
+the static or shared library, so it is only available when deploying to
+a platform where the compiled library is sufficiently recent. On older
+platforms, the program will terminate in an unspecified unsuccessful manner, but
+the quality of diagnostics won't be great.
+
+However, vendors can also override that mechanism at CMake configuration time.
+When a hardening assertion fails, the library invokes the
+``_LIBCPP_ASSERTION_HANDLER`` macro. A vendor may provide a header that contains
+a custom definition of this macro and specify the path to the header via the
+``LIBCXX_ASSERTION_HANDLER_FILE`` CMake variable. If provided, this header will
+be included by the library and replace the default implementation. The header
+must not include any standard library headers (directly or transitively) because
+doing so will almost always create a circular dependency. The
+``_LIBCPP_ASSERTION_HANDLER(message)`` macro takes a single parameter that
+contains an error message explaining the hardening failure and some details
+about the source location that triggered it.
+
+When a hardening assertion fails, it means that the program is about to invoke
+library undefined behavior. For this reason, the custom assertion handler is
+generally expected to terminate the program. If a custom assertion handler
+decides to avoid doing so (e.g. it chooses to log and continue instead), it does
+so at its own risk -- this approach should only be used in non-production builds
+and with an understanding of potential consequences. Furthermore, the custom
+assertion handler should not throw any exceptions as it may be invoked from
+standard library functions that are marked ``noexcept`` (so throwing will result
+in ``std::terminate`` being called).
+
+
 Using Alternate ABI libraries
 =============================
 

diff  --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index 931de8ba1de639..77b7939a0c0ac9 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -111,6 +111,10 @@ Deprecations and Removals
   macro is provided to restore the previous behavior, and it will be supported in the LLVM 18 release only.
   In LLVM 19 and beyond, ``_LIBCPP_ENABLE_NARROWING_CONVERSIONS_IN_VARIANT`` will not be honored anymore.
 
+- The only supported way to customize the assertion handler that gets invoked when a hardening assertion fails
+  is now by setting the ``LIBCXX_ASSERTION_HANDLER_FILE`` CMake variable and providing a custom header. See
+  the documentation on overriding the default assertion handler for details.
+
 - The ``_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED`` macro is not honored anymore in LLVM 18.
   Please see the updated documentation about the hardening modes in libc++ and in particular the
   ``_LIBCPP_VERBOSE_ABORT`` macro for details.

diff  --git a/libcxx/docs/UsingLibcxx.rst b/libcxx/docs/UsingLibcxx.rst
index 746e0a57578367..daf39c9faa449a 100644
--- a/libcxx/docs/UsingLibcxx.rst
+++ b/libcxx/docs/UsingLibcxx.rst
@@ -146,70 +146,6 @@ IWYU, you should run the tool like so:
 If you would prefer to not use that flag, then you can replace ``/path/to/include-what-you-use/share/libcxx.imp``
 file with the libc++-provided ``libcxx.imp`` file.
 
-.. _termination-handler:
-
-Overriding the default termination handler
-==========================================
-
-When the library wants to terminate due to an unforeseen condition (such as a hardening assertion
-failure), the program is aborted through a special verbose termination function. The library provides
-a default function that prints an error message and calls ``std::abort()``. Note that this function is
-provided by the static or shared library, so it is only available when deploying to a platform where
-the compiled library is sufficiently recent. On older platforms, the program will terminate in an
-unspecified unsuccessful manner, but the quality of diagnostics won't be great.
-
-However, users can also override that mechanism at two 
diff erent levels. First, the mechanism can be
-overridden at compile time by defining the ``_LIBCPP_VERBOSE_ABORT(format, args...)`` variadic macro.
-When that macro is defined, it will be called with a format string as the first argument, followed by
-a series of arguments to format using printf-style formatting. Compile-time customization may be
-useful to get precise control over code generation, however it is also inconvenient to use in
-some cases. Indeed, compile-time customization of the verbose termination function requires that all
-translation units be compiled with a consistent definition for ``_LIBCPP_VERBOSE_ABORT`` to avoid ODR
-violations, which can add complexity in the build system of users.
-
-Otherwise, if compile-time customization is not necessary, link-time customization of the handler is also
-possible, similarly to how replacing ``operator new`` works. This mechanism trades off fine-grained control
-over the call site where the termination is initiated in exchange for better ergonomics. Link-time
-customization is done by simply defining the following function in exactly one translation unit of your
-program:
-
-.. code-block:: cpp
-
-  void __libcpp_verbose_abort(char const* format, ...)
-
-This mechanism is similar to how one can replace the default definition of ``operator new``
-and ``operator delete``. For example:
-
-.. code-block:: cpp
-
-  // In HelloWorldHandler.cpp
-  #include <version> // must include any libc++ header before defining the function (C compatibility headers excluded)
-
-  void std::__libcpp_verbose_abort(char const* format, ...) {
-    std::va_list list;
-    va_start(list, format);
-    std::vfprintf(stderr, format, list);
-    va_end(list);
-
-    std::abort();
-  }
-
-  // In HelloWorld.cpp
-  #include <vector>
-
-  int main() {
-    std::vector<int> v;
-    int& x = v[0]; // Your termination function will be called here if hardening is enabled.
-  }
-
-Also note that the verbose termination function should never return. Since assertions in libc++
-catch undefined behavior, your code will proceed with undefined behavior if your function is called
-and does return.
-
-Furthermore, exceptions should not be thrown from the function. Indeed, many functions in the
-library are ``noexcept``, and any exception thrown from the termination function will result
-in ``std::terminate`` being called.
-
 Libc++ Configuration Macros
 ===========================
 

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 0fe3ab44d2466e..9fdf978a89d7ed 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -1020,9 +1020,11 @@ endforeach()
 
 configure_file("__config_site.in" "${LIBCXX_GENERATED_INCLUDE_TARGET_DIR}/__config_site" @ONLY)
 configure_file("module.modulemap.in" "${LIBCXX_GENERATED_INCLUDE_DIR}/module.modulemap" @ONLY)
+configure_file("${LIBCXX_ASSERTION_HANDLER_FILE}" "${LIBCXX_GENERATED_INCLUDE_DIR}/__assertion_handler" COPYONLY)
 
 set(_all_includes "${LIBCXX_GENERATED_INCLUDE_TARGET_DIR}/__config_site"
-                  "${LIBCXX_GENERATED_INCLUDE_DIR}/module.modulemap")
+                  "${LIBCXX_GENERATED_INCLUDE_DIR}/module.modulemap"
+                  "${LIBCXX_GENERATED_INCLUDE_DIR}/__assertion_handler")
 foreach(f ${files})
   set(src "${CMAKE_CURRENT_SOURCE_DIR}/${f}")
   set(dst "${LIBCXX_GENERATED_INCLUDE_DIR}/${f}")
@@ -1059,6 +1061,12 @@ if (LIBCXX_INSTALL_HEADERS)
     PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
     COMPONENT cxx-headers)
 
+  # Install the generated __assertion_handler file to the generic include dir.
+  install(FILES "${LIBCXX_GENERATED_INCLUDE_DIR}/__assertion_handler"
+    DESTINATION "${LIBCXX_INSTALL_INCLUDE_DIR}"
+    PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
+    COMPONENT cxx-headers)
+
   # Install the generated modulemap file to the generic include dir.
   install(FILES "${LIBCXX_GENERATED_INCLUDE_DIR}/module.modulemap"
     DESTINATION "${LIBCXX_INSTALL_INCLUDE_DIR}"

diff  --git a/libcxx/include/__assert b/libcxx/include/__assert
index d4af7e6c7192ab..eb862b5369b258 100644
--- a/libcxx/include/__assert
+++ b/libcxx/include/__assert
@@ -10,8 +10,8 @@
 #ifndef _LIBCPP___ASSERT
 #define _LIBCPP___ASSERT
 
+#include <__assertion_handler> // Note: this include is generated by CMake and is potentially vendor-provided.
 #include <__config>
-#include <__verbose_abort>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -20,8 +20,8 @@
 #define _LIBCPP_ASSERT(expression, message)                                                                            \
   (__builtin_expect(static_cast<bool>(expression), 1)                                                                  \
        ? (void)0                                                                                                       \
-       : _LIBCPP_VERBOSE_ABORT(                                                                                        \
-             "%s:%d: assertion %s failed: %s\n", __builtin_FILE(), __builtin_LINE(), #expression, message))
+       : _LIBCPP_ASSERTION_HANDLER(__FILE__ ":" _LIBCPP_TOSTRING(__LINE__) ": assertion " _LIBCPP_TOSTRING(            \
+             expression) " failed: " message "\n"))
 
 // TODO: __builtin_assume can currently inhibit optimizations. Until this has been fixed and we can add
 //       assumptions without a clear optimization intent, disable that to avoid worsening the code generation.

diff  --git a/libcxx/test/libcxx/assertions/modes/enabling_assertions_enables_extensive_mode.pass.cpp b/libcxx/test/libcxx/assertions/modes/enabling_assertions_enables_extensive_mode.pass.cpp
index be08159d561bb6..a40ae84fa8e85d 100644
--- a/libcxx/test/libcxx/assertions/modes/enabling_assertions_enables_extensive_mode.pass.cpp
+++ b/libcxx/test/libcxx/assertions/modes/enabling_assertions_enables_extensive_mode.pass.cpp
@@ -10,8 +10,8 @@
 // This test ensures that enabling assertions with the legacy `_LIBCPP_ENABLE_ASSERTIONS` now enables the extensive
 // hardening mode.
 
-// `check_assertion.h` is only available starting from C++11 and requires Unix headers.
-// UNSUPPORTED: c++03, !has-unix-headers
+// `check_assertion.h` is only available starting from C++11 and requires Unix headers and regex support.
+// UNSUPPORTED: c++03, !has-unix-headers, no-localization
 // The ability to set a custom abort message is required to compare the assertion message.
 // XFAIL: availability-verbose_abort-missing
 // Note that GCC doesn't support `-Wno-macro-redefined`.

diff  --git a/libcxx/test/libcxx/assertions/modes/override_with_debug_mode.pass.cpp b/libcxx/test/libcxx/assertions/modes/override_with_debug_mode.pass.cpp
index 665babf59c259c..f49ad490780c33 100644
--- a/libcxx/test/libcxx/assertions/modes/override_with_debug_mode.pass.cpp
+++ b/libcxx/test/libcxx/assertions/modes/override_with_debug_mode.pass.cpp
@@ -8,10 +8,8 @@
 
 // This test ensures that we can override any hardening mode with the debug mode on a per-TU basis.
 
-// `check_assertion.h` is only available starting from C++11.
-// UNSUPPORTED: c++03
-// `check_assertion.h` requires Unix headers.
-// REQUIRES: has-unix-headers
+// `check_assertion.h` is only available starting from C++11 and requires Unix headers and regex support.
+// UNSUPPORTED: c++03, !has-unix-headers, no-localization
 // The ability to set a custom abort message is required to compare the assertion message.
 // XFAIL: availability-verbose_abort-missing
 // ADDITIONAL_COMPILE_FLAGS: -U_LIBCPP_HARDENING_MODE -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG

diff  --git a/libcxx/test/libcxx/assertions/modes/override_with_extensive_mode.pass.cpp b/libcxx/test/libcxx/assertions/modes/override_with_extensive_mode.pass.cpp
index e2b0ea7208118e..f5323c671c9b1f 100644
--- a/libcxx/test/libcxx/assertions/modes/override_with_extensive_mode.pass.cpp
+++ b/libcxx/test/libcxx/assertions/modes/override_with_extensive_mode.pass.cpp
@@ -8,10 +8,8 @@
 
 // This test ensures that we can override any hardening mode with the extensive hardening mode on a per-TU basis.
 
-// `check_assertion.h` is only available starting from C++11.
-// UNSUPPORTED: c++03
-// `check_assertion.h` requires Unix headers.
-// REQUIRES: has-unix-headers
+// `check_assertion.h` is only available starting from C++11 and requires Unix headers and regex support.
+// UNSUPPORTED: c++03, !has-unix-headers, no-localization
 // The ability to set a custom abort message is required to compare the assertion message.
 // XFAIL: availability-verbose_abort-missing
 // ADDITIONAL_COMPILE_FLAGS: -U_LIBCPP_HARDENING_MODE -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE

diff  --git a/libcxx/test/libcxx/assertions/modes/override_with_fast_mode.pass.cpp b/libcxx/test/libcxx/assertions/modes/override_with_fast_mode.pass.cpp
index b38afed17e567c..5ee22cc45f3bf0 100644
--- a/libcxx/test/libcxx/assertions/modes/override_with_fast_mode.pass.cpp
+++ b/libcxx/test/libcxx/assertions/modes/override_with_fast_mode.pass.cpp
@@ -8,10 +8,8 @@
 
 // This test ensures that we can override any hardening mode with the fast mode on a per-TU basis.
 
-// `check_assertion.h` is only available starting from C++11.
-// UNSUPPORTED: c++03
-// `check_assertion.h` requires Unix headers.
-// REQUIRES: has-unix-headers
+// `check_assertion.h` is only available starting from C++11 and requires Unix headers and regex support.
+// UNSUPPORTED: c++03, !has-unix-headers, no-localization
 // The ability to set a custom abort message is required to compare the assertion message.
 // XFAIL: availability-verbose_abort-missing
 // ADDITIONAL_COMPILE_FLAGS: -U_LIBCPP_HARDENING_MODE -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST

diff  --git a/libcxx/test/libcxx/assertions/modes/override_with_unchecked_mode.pass.cpp b/libcxx/test/libcxx/assertions/modes/override_with_unchecked_mode.pass.cpp
index 65707a9863d079..8ea8b731ec4d32 100644
--- a/libcxx/test/libcxx/assertions/modes/override_with_unchecked_mode.pass.cpp
+++ b/libcxx/test/libcxx/assertions/modes/override_with_unchecked_mode.pass.cpp
@@ -8,10 +8,8 @@
 
 // This test ensures that we can override any hardening mode with the unchecked mode on a per-TU basis.
 
-// `check_assertion.h` is only available starting from C++11.
-// UNSUPPORTED: c++03
-// `check_assertion.h` requires Unix headers.
-// REQUIRES: has-unix-headers
+// `check_assertion.h` is only available starting from C++11 and requires Unix headers and regex support.
+// UNSUPPORTED: c++03, !has-unix-headers, no-localization
 // ADDITIONAL_COMPILE_FLAGS: -U_LIBCPP_HARDENING_MODE -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE
 
 #include <cassert>

diff  --git a/libcxx/test/libcxx/containers/sequences/deque/asan_caterpillar.pass.cpp b/libcxx/test/libcxx/containers/sequences/deque/asan_caterpillar.pass.cpp
index b99b2737bce02b..d3c6e0493ee28f 100644
--- a/libcxx/test/libcxx/containers/sequences/deque/asan_caterpillar.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/deque/asan_caterpillar.pass.cpp
@@ -11,8 +11,8 @@
 // Regression test to error in deque::__annotate_from_to in deque,
 // with origin in deque::__add_back_capacity.
 
-// REQUIRES: has-unix-headers
-// UNSUPPORTED: c++03
+// `check_assertion.h` is only available starting from C++11 and requires Unix headers and regex support.
+// UNSUPPORTED: c++03, !has-unix-headers, no-localization
 
 #include <deque>
 #include <cstdio>

diff  --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/pstl.exception_handling.pass.cpp
index f7279655cb129f..dda642be85bc0a 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/pstl.exception_handling.pass.cpp
@@ -8,7 +8,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-exceptions
-// REQUIRES: has-unix-headers
+// `check_assertion.h` requires Unix headers and regex support.
+// UNSUPPORTED: !has-unix-headers, no-localization
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 

diff  --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/pstl.exception_handling.pass.cpp
index ffe4f1866ebe6f..bb8ab421722265 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/pstl.exception_handling.pass.cpp
@@ -8,7 +8,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-exceptions
-// REQUIRES: has-unix-headers
+// `check_assertion.h` requires Unix headers and regex support.
+// UNSUPPORTED: !has-unix-headers, no-localization
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 

diff  --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.exception_handling.pass.cpp
index 98fde95fb0e441..c02496bef42128 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.exception_handling.pass.cpp
@@ -8,7 +8,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-exceptions
-// REQUIRES: has-unix-headers
+// `check_assertion.h` requires Unix headers and regex support.
+// UNSUPPORTED: !has-unix-headers, no-localization
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 

diff  --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.rotate/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.rotate/pstl.exception_handling.pass.cpp
index d0b77661334d77..88d177a6e39f41 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.rotate/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.rotate/pstl.exception_handling.pass.cpp
@@ -8,7 +8,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-exceptions
-// REQUIRES: has-unix-headers
+// `check_assertion.h` requires Unix headers and regex support.
+// UNSUPPORTED: !has-unix-headers, no-localization
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 

diff  --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.exception_handling.pass.cpp
index 6c7149f4048ade..439204060e189b 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.exception_handling.pass.cpp
@@ -8,7 +8,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-exceptions
-// REQUIRES: has-unix-headers
+// `check_assertion.h` requires Unix headers and regex support.
+// UNSUPPORTED: !has-unix-headers, no-localization
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 

diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.all_of/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.all_of/pstl.exception_handling.pass.cpp
index ae5063c5264eaa..d1c031bdd97a2f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.all_of/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.all_of/pstl.exception_handling.pass.cpp
@@ -8,7 +8,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-exceptions
-// REQUIRES: has-unix-headers
+// `check_assertion.h` requires Unix headers and regex support.
+// UNSUPPORTED: !has-unix-headers, no-localization
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 

diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.any_of/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.any_of/pstl.exception_handling.pass.cpp
index 167c4ac4df95e3..58fe79b34c0082 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.any_of/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.any_of/pstl.exception_handling.pass.cpp
@@ -8,7 +8,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-exceptions
-// REQUIRES: has-unix-headers
+// `check_assertion.h` requires Unix headers and regex support.
+// UNSUPPORTED: !has-unix-headers, no-localization
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 

diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.equal/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.equal/pstl.exception_handling.pass.cpp
index 4c2f9946165e7a..1bcd858f3c02d7 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.equal/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.equal/pstl.exception_handling.pass.cpp
@@ -8,7 +8,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-exceptions
-// REQUIRES: has-unix-headers
+// `check_assertion.h` requires Unix headers and regex support.
+// UNSUPPORTED: !has-unix-headers, no-localization
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 

diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.exception_handling.pass.cpp
index 06b0810c257b3f..b0ee4f8d062ef0 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.exception_handling.pass.cpp
@@ -8,7 +8,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-exceptions
-// REQUIRES: has-unix-headers
+// `check_assertion.h` requires Unix headers and regex support.
+// UNSUPPORTED: !has-unix-headers, no-localization
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 

diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/pstl.exception_handling.pass.cpp
index ab15bdf5d8b99b..a63276f1e025df 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/pstl.exception_handling.pass.cpp
@@ -8,7 +8,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-exceptions
-// REQUIRES: has-unix-headers
+// `check_assertion.h` requires Unix headers and regex support.
+// UNSUPPORTED: !has-unix-headers, no-localization
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 

diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.none_of/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.none_of/pstl.exception_handling.pass.cpp
index 9a6d8d636200cf..26e6fea5904fe1 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.none_of/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.none_of/pstl.exception_handling.pass.cpp
@@ -8,7 +8,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-exceptions
-// REQUIRES: has-unix-headers
+// `check_assertion.h` requires Unix headers and regex support.
+// UNSUPPORTED: !has-unix-headers, no-localization
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 

diff  --git a/libcxx/test/std/algorithms/alg.sorting/alg.merge/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.merge/pstl.exception_handling.pass.cpp
index b56959809829b8..b48a5a9fa2b7d4 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.merge/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.merge/pstl.exception_handling.pass.cpp
@@ -8,7 +8,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-exceptions
-// REQUIRES: has-unix-headers
+// `check_assertion.h` requires Unix headers and regex support.
+// UNSUPPORTED: !has-unix-headers, no-localization
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 

diff  --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/pstl.exception_handling.pass.cpp
index 43ee937a8a185e..1dc603cfaa554f 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/pstl.exception_handling.pass.cpp
@@ -8,7 +8,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-exceptions
-// REQUIRES: has-unix-headers
+// `check_assertion.h` requires Unix headers and regex support.
+// UNSUPPORTED: !has-unix-headers, no-localization
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 

diff  --git a/libcxx/test/std/algorithms/numeric.ops/reduce/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/numeric.ops/reduce/pstl.exception_handling.pass.cpp
index 65a112774ff762..d52889b1be1479 100644
--- a/libcxx/test/std/algorithms/numeric.ops/reduce/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/numeric.ops/reduce/pstl.exception_handling.pass.cpp
@@ -8,7 +8,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-exceptions
-// REQUIRES: has-unix-headers
+// `check_assertion.h` requires Unix headers and regex support.
+// UNSUPPORTED: !has-unix-headers, no-localization
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 

diff  --git a/libcxx/test/std/algorithms/numeric.ops/transform.reduce/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/numeric.ops/transform.reduce/pstl.exception_handling.pass.cpp
index d2df251d5c8ad5..5ac04334f00053 100644
--- a/libcxx/test/std/algorithms/numeric.ops/transform.reduce/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/numeric.ops/transform.reduce/pstl.exception_handling.pass.cpp
@@ -8,7 +8,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-exceptions
-// REQUIRES: has-unix-headers
+// `check_assertion.h` requires Unix headers and regex support.
+// UNSUPPORTED: !has-unix-headers, no-localization
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 

diff  --git a/libcxx/test/support/check_assertion.h b/libcxx/test/support/check_assertion.h
index 01e296d9138d0c..83a46548fa9250 100644
--- a/libcxx/test/support/check_assertion.h
+++ b/libcxx/test/support/check_assertion.h
@@ -15,6 +15,7 @@
 #include <cstdio>
 #include <cstdlib>
 #include <exception>
+#include <regex>
 #include <string>
 #include <string_view>
 #include <utility>
@@ -234,18 +235,26 @@ struct DeathTest {
 };
 
 #ifdef _LIBCPP_VERSION
-void std::__libcpp_verbose_abort(char const* format, ...) {
-  // Extract information from the error message. This has to stay synchronized with
-  // how we format assertions in the library.
-  va_list list;
-  va_start(list, format);
-  char const* file = va_arg(list, char const*);
-  int line = va_arg(list, int);
-  char const* expression = va_arg(list, char const*); (void)expression;
-  char const* message = va_arg(list, char const*);
-  va_end(list);
-
-  if (GlobalMatcher().Matches(file, line, message)) {
+void std::__libcpp_verbose_abort(char const* printf_format, ...) {
+  // Extract information from the error message. This has to stay synchronized with how we format assertions in the
+  // library.
+  va_list args;
+  va_start(args, printf_format);
+  char const* message = va_arg(args, char const*);
+
+  std::regex message_format("(.*):(\\d+): assertion (.*) failed: (.*)\\n");
+
+  std::cmatch match_result;
+  bool has_match = std::regex_match(message, match_result, message_format);
+  assert(has_match);
+  assert(match_result.size() == 5);
+
+  std::string file = match_result[1];
+  int line         = std::stoi(match_result[2]);
+  // Omitting `expression` in `match_result[3]`
+  std::string failure_reason = match_result[4];
+
+  if (GlobalMatcher().Matches(file.c_str(), line, failure_reason.c_str())) {
     std::exit(DeathTest::RK_MatchFound);
   }
   std::exit(DeathTest::RK_MatchFailure);

diff  --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index 13337f16dc7118..2268034bae2045 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -168,6 +168,9 @@ def is_modulemap_header(header):
     if header == "__config_site":
         return False
 
+    if header == "__assertion_handler":
+        return False
+
     # exclude libc++abi files
     if header in ["cxxabi.h", "__cxxabi_config.h"]:
         return False

diff  --git a/libcxx/vendor/llvm/default_assertion_handler.in b/libcxx/vendor/llvm/default_assertion_handler.in
new file mode 100644
index 00000000000000..111d305a16f7c4
--- /dev/null
+++ b/libcxx/vendor/llvm/default_assertion_handler.in
@@ -0,0 +1,23 @@
+// -*- 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___ASSERTION_HANDLER
+#define _LIBCPP___ASSERTION_HANDLER
+
+#include <__config>
+#include <__verbose_abort>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+// TODO(hardening): in production, trap rather than abort.
+#define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_ABORT("%s", message)
+
+#endif // _LIBCPP___ASSERTION_HANDLER


        


More information about the libcxx-commits mailing list