[libcxx-commits] [libcxx] d1367ca - [libc++][hardening][NFC] Add macros to enable hardened mode.

via libcxx-commits libcxx-commits at lists.llvm.org
Wed Jul 12 10:13:13 PDT 2023


Author: varconst
Date: 2023-07-12T10:12:58-07:00
New Revision: d1367ca46ee40dd76661e3f551515d77301568c0

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

LOG: [libc++][hardening][NFC] Add macros to enable hardened mode.

This patch only adds new configuration knobs -- the actual assertions
will be added in follow-up patches.

Differential Revision: https://reviews.llvm.org/D153902

Added: 
    libcxx/cmake/caches/Generic-debug-mode.cmake
    libcxx/cmake/caches/Generic-hardened-mode.cmake
    libcxx/docs/HardenedMode.rst
    libcxx/test/libcxx/assertions/modes/debug.pass.cpp
    libcxx/test/libcxx/assertions/modes/debug_mode_disabled_in_tu.pass.cpp
    libcxx/test/libcxx/assertions/modes/debug_mode_enabled_in_tu.pass.cpp
    libcxx/test/libcxx/assertions/modes/debug_mode_not_1_or_0.verify.cpp
    libcxx/test/libcxx/assertions/modes/debug_no_assertions.pass.cpp
    libcxx/test/libcxx/assertions/modes/hardened.pass.cpp
    libcxx/test/libcxx/assertions/modes/hardened_and_debug_mutually_exclusive.verify.cpp
    libcxx/test/libcxx/assertions/modes/hardened_mode_disabled_in_tu.pass.cpp
    libcxx/test/libcxx/assertions/modes/hardened_mode_enabled_in_tu.pass.cpp
    libcxx/test/libcxx/assertions/modes/hardened_mode_not_1_or_0.verify.cpp
    libcxx/test/libcxx/assertions/modes/hardened_no_assertions.pass.cpp
    libcxx/test/libcxx/assertions/modes/unchecked.pass.cpp

Modified: 
    libcxx/CMakeLists.txt
    libcxx/docs/ReleaseNotes.rst
    libcxx/docs/index.rst
    libcxx/include/__algorithm/comp_ref_type.h
    libcxx/include/__algorithm/three_way_comp_ref_type.h
    libcxx/include/__config
    libcxx/include/__config_site.in
    libcxx/include/__tree
    libcxx/test/CMakeLists.txt
    libcxx/test/libcxx/algorithms/alg.sorting/alg.heap.operations/make.heap/complexity.pass.cpp
    libcxx/test/libcxx/algorithms/alg.sorting/assert.sort.invalid_comparator.pass.cpp
    libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/complexity.pass.cpp
    libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/ranges_sort_heap.pass.cpp
    libcxx/test/std/algorithms/alg.sorting/alg.merge/inplace_merge_comp.pass.cpp
    libcxx/test/std/algorithms/alg.sorting/alg.three.way/lexicographical_compare_three_way_comp.pass.cpp
    libcxx/test/support/container_debug_tests.h
    libcxx/utils/ci/buildkite-pipeline.yml
    libcxx/utils/ci/run-buildbot
    libcxx/utils/libcxx/test/params.py

Removed: 
    


################################################################################
diff  --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index bae8340461fd03..e67e0db6bab6fb 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -59,6 +59,16 @@ option(LIBCXX_ENABLE_FILESYSTEM
    available on the platform. This includes things like most parts of <filesystem> and
    others like <fstream>" ON)
 option(LIBCXX_INCLUDE_TESTS "Build the libc++ tests." ${LLVM_INCLUDE_TESTS})
+set(LIBCXX_SUPPORTED_HARDENING_MODES unchecked hardened debug)
+set(LIBCXX_HARDENING_MODE "unchecked" CACHE STRING
+  "Specify the default hardening mode to use. This mode will be used inside the
+   compiled library and will be the default when compiling user code. Note that
+   users can override this setting in their own code. This does not affect the
+   ABI. Supported values are ${LIBCXX_SUPPORTED_HARDENING_MODES}.")
+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()
 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
@@ -784,6 +794,16 @@ if (LIBCXX_ENABLE_ASSERTIONS)
 else()
   config_define(0 _LIBCPP_ENABLE_ASSERTIONS_DEFAULT)
 endif()
+if (LIBCXX_HARDENING_MODE STREQUAL "hardened")
+  config_define(1 _LIBCPP_ENABLE_HARDENED_MODE_DEFAULT)
+  config_define(0 _LIBCPP_ENABLE_DEBUG_MODE_DEFAULT)
+elseif (LIBCXX_HARDENING_MODE STREQUAL "debug")
+  config_define(0 _LIBCPP_ENABLE_HARDENED_MODE_DEFAULT)
+  config_define(1 _LIBCPP_ENABLE_DEBUG_MODE_DEFAULT)
+elseif (LIBCXX_HARDENING_MODE STREQUAL "unchecked")
+  config_define(0 _LIBCPP_ENABLE_HARDENED_MODE_DEFAULT)
+  config_define(0 _LIBCPP_ENABLE_DEBUG_MODE_DEFAULT)
+endif()
 
 if (LIBCXX_PSTL_CPU_BACKEND STREQUAL "serial")
   config_define(1 _LIBCPP_PSTL_CPU_BACKEND_SERIAL)

diff  --git a/libcxx/cmake/caches/Generic-debug-mode.cmake b/libcxx/cmake/caches/Generic-debug-mode.cmake
new file mode 100644
index 00000000000000..1d401ba69ab80e
--- /dev/null
+++ b/libcxx/cmake/caches/Generic-debug-mode.cmake
@@ -0,0 +1 @@
+set(LIBCXX_HARDENING_MODE "debug" CACHE STRING "")

diff  --git a/libcxx/cmake/caches/Generic-hardened-mode.cmake b/libcxx/cmake/caches/Generic-hardened-mode.cmake
new file mode 100644
index 00000000000000..66c18741d198b2
--- /dev/null
+++ b/libcxx/cmake/caches/Generic-hardened-mode.cmake
@@ -0,0 +1 @@
+set(LIBCXX_HARDENING_MODE "hardened" CACHE STRING "")

diff  --git a/libcxx/docs/HardenedMode.rst b/libcxx/docs/HardenedMode.rst
new file mode 100644
index 00000000000000..eb53e7c4fd060c
--- /dev/null
+++ b/libcxx/docs/HardenedMode.rst
@@ -0,0 +1,41 @@
+=============
+Hardened Mode
+=============
+
+.. contents::
+   :local:
+
+.. _using-hardened-mode:
+
+Using the hardened mode
+=======================
+
+The hardened mode enables a set of security-critical assertions that prevent
+undefined behavior caused by violating preconditions of the standard library.
+These assertions can be done with relatively little overhead in constant time
+and are intended to be used in production.
+
+In addition to the hardened mode, libc++ also provides the debug mode which
+contains all the checks from the hardened mode and additionally more expensive
+checks that may affect the complexity of algorithms. The debug mode is intended
+to be used for testing, not in production.
+
+Vendors can set the default hardened mode by using the ``LIBCXX_HARDENING_MODE``
+CMake variable. Setting ``LIBCXX_HARDENING_MODE`` to ``hardened`` enables the
+hardened mode, and similarly setting the variable to ``debug`` enables the debug
+mode. The default value is ``unchecked`` which doesn't enable the hardened mode.
+Users can control whether the hardened mode or the debug mode is enabled
+on a per translation unit basis by setting the ``_LIBCPP_ENABLE_HARDENED_MODE``
+or ``_LIBCPP_ENABLE_DEBUG_MODE`` macro to ``1``.
+
+The hardened mode requires ``LIBCXX_ENABLE_ASSERTIONS`` to work. If
+``LIBCXX_ENABLE_ASSERTIONS`` was not set explicitly, enabling the hardened mode
+(or the debug mode) will implicitly enable ``LIBCXX_ENABLE_ASSERTIONS``. If
+``LIBCXX_ENABLE_ASSERTIONS`` was explicitly disabled, this will effectively
+disable the hardened mode.
+
+Enabling the hardened mode (or the debug mode) has no impact on the ABI.
+
+Iterator bounds checking
+------------------------
+TODO(hardening)

diff  --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index 32fd158f32fdb2..2e9f0ea0644150 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -72,14 +72,25 @@ Improvements and New Features
   Anything that does not rely on having an actual filesystem available will now work, such as ``std::filesystem::path``,
   ``std::filesystem::perms`` and similar classes.
 
+- The library now provides a hardened mode under which common cases of library undefined behavior will be turned into
+  a reliable program termination. Vendors can configure whether the hardened mode is enabled by default with the
+  ``LIBCXX_HARDENING_MODE`` variable at CMake configuration time. Users can control whether the hardened mode is
+  enabled on a per translation unit basis using the ``-D_LIBCPP_ENABLE_HARDENED_MODE=1`` macro. See
+  ``libcxx/docs/HardenedMode.rst`` for more details.
+
+- The library now provides a debug mode which is a superset of the hardened mode, additionally enabling more expensive
+  checks that are not suitable to be used in production. This replaces the legacy debug mode that was removed in this
+  release. Unlike the legacy debug mode, this doesn't affect the ABI and doesn't require locking. Vendors can configure
+  whether the debug mode is enabled by default with the ``LIBCXX_HARDENING_MODE`` variable at CMake configuration time.
+  Users can control whether the debug mode is enabled on a per translation unit basis using the
+  ``-D_LIBCPP_ENABLE_DEBUG_MODE=1`` macro. See ``libcxx/docs/HardenedMode.rst`` for more details.
+
 Deprecations and Removals
 -------------------------
 
-- The legacy debug mode has been removed in this release. Defining the macro
-  `_LIBCPP_ENABLE_DEBUG_MODE` is now a no-op, and the `LIBCXX_ENABLE_DEBUG_MODE`
-  CMake variable has been removed. The legacy debug mode will be replaced by
-  finer-grained hardened modes. For additional context, refer to the `Discourse
-  post
+- The legacy debug mode has been removed in this release. Setting the macro ``_LIBCPP_ENABLE_DEBUG_MODE`` to ``1`` now
+  enables the new debug mode which is part of hardening (see the "Improvements and New Features" section above). The
+  ``LIBCXX_ENABLE_DEBUG_MODE`` CMake variable has been removed. For additional context, refer to the `Discourse post
   <https://discourse.llvm.org/t/rfc-removing-the-legacy-debug-mode-from-libc/71026>`_.
 
 - The ``<experimental/coroutine>`` header has been removed in this release. The ``<coroutine>`` header

diff  --git a/libcxx/docs/index.rst b/libcxx/docs/index.rst
index 8696b5aff1c862..c107dc46702327 100644
--- a/libcxx/docs/index.rst
+++ b/libcxx/docs/index.rst
@@ -40,6 +40,7 @@ Getting Started with libc++
    TestingLibcxx
    Contributing
    Modules
+   HardenedMode
    ReleaseProcedure
    Status/Cxx14
    Status/Cxx17

diff  --git a/libcxx/include/__algorithm/comp_ref_type.h b/libcxx/include/__algorithm/comp_ref_type.h
index bd463192b57c6e..d16bd0f5310003 100644
--- a/libcxx/include/__algorithm/comp_ref_type.h
+++ b/libcxx/include/__algorithm/comp_ref_type.h
@@ -65,8 +65,7 @@ struct __debug_less
 
 // Pass the comparator by lvalue reference. Or in debug mode, using a
 // debugging wrapper that stores a reference.
-// TODO(varconst): update to be used in the new debug mode (or delete entirely).
-#ifdef _LIBCPP_ENABLE_DEBUG_MODE
+#if _LIBCPP_ENABLE_DEBUG_MODE
 template <class _Comp>
 using __comp_ref_type = __debug_less<_Comp>;
 #else

diff  --git a/libcxx/include/__algorithm/three_way_comp_ref_type.h b/libcxx/include/__algorithm/three_way_comp_ref_type.h
index 097fa89dfc0cc3..19c102f4c96c30 100644
--- a/libcxx/include/__algorithm/three_way_comp_ref_type.h
+++ b/libcxx/include/__algorithm/three_way_comp_ref_type.h
@@ -58,8 +58,7 @@ struct __debug_three_way_comp {
 
 // Pass the comparator by lvalue reference. Or in debug mode, using a
 // debugging wrapper that stores a reference.
-// TODO(varconst): update to be used in the new debug mode (or delete entirely).
-#  ifdef _LIBCPP_ENABLE_DEBUG_MODE
+#  if _LIBCPP_ENABLE_DEBUG_MODE
 template <class _Comp>
 using __three_way_comp_ref_type = __debug_three_way_comp<_Comp>;
 #  else

diff  --git a/libcxx/include/__config b/libcxx/include/__config
index 37a5f6b8f00b4d..4091d18da03dff 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -206,6 +206,71 @@
 
 // } ABI
 
+// HARDENING {
+
+// Enables the hardened mode which consists of all checks intended to be used in production. Hardened mode prioritizes
+// security-critical checks that can be done with relatively little overhead in constant time. Mutually exclusive with
+// `_LIBCPP_ENABLE_DEBUG_MODE`.
+//
+// #define _LIBCPP_ENABLE_HARDENED_MODE 1
+
+// Enables the debug mode which contains all the checks from the hardened mode and additionally more expensive checks
+// that may affect the complexity of algorithms. The debug mode is intended to be used for testing, not in production.
+// Mutually exclusive with `_LIBCPP_ENABLE_HARDENED_MODE`.
+//
+// #define _LIBCPP_ENABLE_DEBUG_MODE 1
+
+// Available checks:
+
+// TODO(hardening): add documentation for 
diff erent checks here.
+
+#  ifndef _LIBCPP_ENABLE_HARDENED_MODE
+#    define _LIBCPP_ENABLE_HARDENED_MODE _LIBCPP_ENABLE_HARDENED_MODE_DEFAULT
+#  endif
+#  if _LIBCPP_ENABLE_HARDENED_MODE != 0 && _LIBCPP_ENABLE_HARDENED_MODE != 1
+#    error "_LIBCPP_ENABLE_HARDENED_MODE must be set to 0 or 1."
+#  endif
+
+#  ifndef _LIBCPP_ENABLE_DEBUG_MODE
+#    define _LIBCPP_ENABLE_DEBUG_MODE _LIBCPP_ENABLE_DEBUG_MODE_DEFAULT
+#  endif
+#  if _LIBCPP_ENABLE_DEBUG_MODE != 0 && _LIBCPP_ENABLE_DEBUG_MODE != 1
+#    error "_LIBCPP_ENABLE_DEBUG_MODE must be set to 0 or 1."
+#  endif
+
+#  if _LIBCPP_ENABLE_HARDENED_MODE && _LIBCPP_ENABLE_DEBUG_MODE
+#    error "Only one of _LIBCPP_ENABLE_HARDENED_MODE and _LIBCPP_ENABLE_DEBUG_MODE can be enabled."
+#  endif
+
+// Hardened mode checks.
+#  if _LIBCPP_ENABLE_HARDENED_MODE
+
+// Automatically enable assertions in hardened mode (unless the user explicitly turned them off).
+#    ifndef _LIBCPP_ENABLE_ASSERTIONS
+#      define _LIBCPP_ENABLE_ASSERTIONS 1
+#    endif
+
+// TODO(hardening): more checks to be added here...
+
+// Debug mode checks.
+#  elif _LIBCPP_ENABLE_DEBUG_MODE
+
+// Automatically enable assertions in debug mode (unless the user explicitly turned them off).
+#    ifndef _LIBCPP_ENABLE_ASSERTIONS
+#      define _LIBCPP_ENABLE_ASSERTIONS 1
+#    endif
+
+// TODO(hardening): more checks to be added here...
+
+// Disable all checks if neither the hardened mode nor the debug mode is enabled.
+#  else
+
+// TODO: more checks to be added here...
+
+#  endif // _LIBCPP_ENABLE_HARDENED_MODE
+
+// } HARDENING
+
 #  define _LIBCPP_TOSTRING2(x) #x
 #  define _LIBCPP_TOSTRING(x) _LIBCPP_TOSTRING2(x)
 

diff  --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index 5c5376541b98d2..e006f758098f82 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -34,6 +34,10 @@
 #cmakedefine _LIBCPP_PSTL_CPU_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_CPU_BACKEND_THREAD
 
+// Hardening.
+#cmakedefine01 _LIBCPP_ENABLE_HARDENED_MODE_DEFAULT
+#cmakedefine01 _LIBCPP_ENABLE_DEBUG_MODE_DEFAULT
+
 // __USE_MINGW_ANSI_STDIO gets redefined on MinGW
 #ifdef __clang__
 #  pragma clang diagnostic push

diff  --git a/libcxx/include/__tree b/libcxx/include/__tree
index 3ba9b7e74cf61b..e7761d2ad21657 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -376,8 +376,9 @@ __tree_remove(_NodePtr __root, _NodePtr __z) _NOEXCEPT
 {
     _LIBCPP_ASSERT_UNCATEGORIZED(__root != nullptr, "Root node should not be null");
     _LIBCPP_ASSERT_UNCATEGORIZED(__z != nullptr, "The node to remove should not be null");
-    // TODO: Use in the new debug mode:
-    // _LIBCPP_DEBUG_ASSERT(std::__tree_invariant(__root), "The tree invariants should hold");
+#if _LIBCPP_ENABLE_DEBUG_MODE
+    _LIBCPP_ASSERT_UNCATEGORIZED(std::__tree_invariant(__root), "The tree invariants should hold");
+#endif
     // __z will be removed from the tree.  Client still needs to destruct/deallocate it
     // __y is either __z, or if __z has two children, __tree_next(__z).
     // __y will have at most one child.

diff  --git a/libcxx/test/CMakeLists.txt b/libcxx/test/CMakeLists.txt
index 23081f6e1ec51e..306d610567feee 100644
--- a/libcxx/test/CMakeLists.txt
+++ b/libcxx/test/CMakeLists.txt
@@ -28,6 +28,8 @@ if (LIBCXX_ENABLE_ASSERTIONS)
   serialize_lit_param(enable_assertions True)
 endif()
 
+serialize_lit_param(hardening_mode "\"${LIBCXX_HARDENING_MODE}\"")
+
 if (CMAKE_CXX_COMPILER_TARGET)
   serialize_lit_param(target_triple "\"${CMAKE_CXX_COMPILER_TARGET}\"")
 else()

diff  --git a/libcxx/test/libcxx/algorithms/alg.sorting/alg.heap.operations/make.heap/complexity.pass.cpp b/libcxx/test/libcxx/algorithms/alg.sorting/alg.heap.operations/make.heap/complexity.pass.cpp
index 7840dad937ed06..a8032d032a4671 100644
--- a/libcxx/test/libcxx/algorithms/alg.sorting/alg.heap.operations/make.heap/complexity.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/alg.sorting/alg.heap.operations/make.heap/complexity.pass.cpp
@@ -64,7 +64,9 @@ int main(int, char**)
   std::make_heap(v.begin(), v.end());
   assert(stats.copied == 0);
   assert(stats.moved == 153'486);
+#if !_LIBCPP_ENABLE_DEBUG_MODE
   assert(stats.compared == 188'285);
+#endif
 
   assert(std::is_heap(v.begin(), v.end()));
 

diff  --git a/libcxx/test/libcxx/algorithms/alg.sorting/assert.sort.invalid_comparator.pass.cpp b/libcxx/test/libcxx/algorithms/alg.sorting/assert.sort.invalid_comparator.pass.cpp
index d95e5dc748397b..47ae97935b9eb5 100644
--- a/libcxx/test/libcxx/algorithms/alg.sorting/assert.sort.invalid_comparator.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/alg.sorting/assert.sort.invalid_comparator.pass.cpp
@@ -12,6 +12,10 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // XFAIL: availability-verbose_abort-missing
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 -D_LIBCPP_DEBUG_STRICT_WEAK_ORDERING_CHECK
+// When the debug mode is enabled, this test fails because we actually catch on the fly that the comparator is not
+// a strict-weak ordering before we catch that we'd dereference out-of-bounds inside std::sort, which leads to 
diff erent
+// errors than the ones tested below.
+// XFAIL: libcpp-has-debug-mode
 
 // This test uses a specific combination of an invalid comparator and sequence of values to
 // ensure that our sorting functions do not go out-of-bounds and satisfy strict weak ordering in that case.

diff  --git a/libcxx/test/libcxx/assertions/modes/debug.pass.cpp b/libcxx/test/libcxx/assertions/modes/debug.pass.cpp
new file mode 100644
index 00000000000000..7134c4bab94658
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/modes/debug.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This test ensures that assertions trigger without the user having to do anything when the debug mode has been enabled
+// by default.
+
+// UNSUPPORTED: !libcpp-has-debug-mode
+// `check_assertion.h` is only available starting from C++11.
+// UNSUPPORTED: c++03
+// `check_assertion.h` requires Unix headers.
+// REQUIRES: has-unix-headers
+
+#include <cassert>
+#include "check_assertion.h"
+
+int main(int, char**) {
+  _LIBCPP_ASSERT_UNCATEGORIZED(true, "Should not fire");
+  TEST_LIBCPP_ASSERT_FAILURE([] {
+    _LIBCPP_ASSERT_UNCATEGORIZED(false, "Should fire");
+  }(), "Should fire");
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/assertions/modes/debug_mode_disabled_in_tu.pass.cpp b/libcxx/test/libcxx/assertions/modes/debug_mode_disabled_in_tu.pass.cpp
new file mode 100644
index 00000000000000..8873754b95583a
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/modes/debug_mode_disabled_in_tu.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This test ensures that we can disable the debug mode on a per-TU basis regardless of how the library was built.
+
+// TODO(hardening): currently, explicitly enabling assertions enables all uncategorized assertions and overrides
+// disabling the debug mode.
+// UNSUPPORTED: libcpp-has-hardened-mode, libcpp-has-assertions
+// ADDITIONAL_COMPILE_FLAGS: -Wno-macro-redefined -D_LIBCPP_ENABLE_DEBUG_MODE=0
+
+#include <cassert>
+
+int main(int, char**) {
+  _LIBCPP_ASSERT_UNCATEGORIZED(true, "Should not fire");
+  _LIBCPP_ASSERT_UNCATEGORIZED(false, "Also should not fire");
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/assertions/modes/debug_mode_enabled_in_tu.pass.cpp b/libcxx/test/libcxx/assertions/modes/debug_mode_enabled_in_tu.pass.cpp
new file mode 100644
index 00000000000000..11627920b3b7f3
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/modes/debug_mode_enabled_in_tu.pass.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This test ensures that we can enable the debug mode on a per-TU basis regardless of how the library was built.
+
+// Hardened mode would additionally trigger the error that hardened and debug modes are mutually exclusive.
+// UNSUPPORTED: libcpp-has-hardened-mode
+// `check_assertion.h` is only available starting from C++11.
+// UNSUPPORTED: c++03
+// `check_assertion.h` requires Unix headers.
+// REQUIRES: has-unix-headers
+// The ability to set a custom abort message is required to compare the assertion message.
+// XFAIL: availability-verbose_abort-missing
+// ADDITIONAL_COMPILE_FLAGS: -Wno-macro-redefined -D_LIBCPP_ENABLE_DEBUG_MODE=1
+
+#include <cassert>
+#include "check_assertion.h"
+
+int main(int, char**) {
+  _LIBCPP_ASSERT_UNCATEGORIZED(true, "Should not fire");
+  TEST_LIBCPP_ASSERT_FAILURE([] {
+    _LIBCPP_ASSERT_UNCATEGORIZED(false, "Should fire");
+  }(), "Should fire");
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/assertions/modes/debug_mode_not_1_or_0.verify.cpp b/libcxx/test/libcxx/assertions/modes/debug_mode_not_1_or_0.verify.cpp
new file mode 100644
index 00000000000000..d4f542a6b4a776
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/modes/debug_mode_not_1_or_0.verify.cpp
@@ -0,0 +1,19 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This test verifies that setting the debug mode to a value other than `0` or `1` triggers a compile-time error.
+
+// Hardened mode would additionally trigger the error that hardened and debug modes are mutually exclusive.
+// UNSUPPORTED: libcpp-has-hardened-mode
+// Modules build produces a 
diff erent error ("Could not build module 'std'").
+// UNSUPPORTED: modules-build
+// ADDITIONAL_COMPILE_FLAGS: -Wno-macro-redefined -D_LIBCPP_ENABLE_DEBUG_MODE=2
+
+#include <cassert>
+
+// expected-error@*:*  {{_LIBCPP_ENABLE_DEBUG_MODE must be set to 0 or 1.}}

diff  --git a/libcxx/test/libcxx/assertions/modes/debug_no_assertions.pass.cpp b/libcxx/test/libcxx/assertions/modes/debug_no_assertions.pass.cpp
new file mode 100644
index 00000000000000..496b1b9a678827
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/modes/debug_no_assertions.pass.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that we can override whether assertions are enabled regardless of the hardening mode in use.
+
+// UNSUPPORTED: !libcpp-has-debug-mode
+// ADDITIONAL_COMPILE_FLAGS: -Wno-macro-redefined -D_LIBCPP_ENABLE_ASSERTIONS=0
+
+#include <cassert>
+
+int main(int, char**) {
+  _LIBCPP_ASSERT_UNCATEGORIZED(true, "Should not fire");
+  _LIBCPP_ASSERT_UNCATEGORIZED(false, "Also should not fire");
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/assertions/modes/hardened.pass.cpp b/libcxx/test/libcxx/assertions/modes/hardened.pass.cpp
new file mode 100644
index 00000000000000..643e8d9470d458
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/modes/hardened.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This test ensures that assertions trigger without the user having to do anything when the hardened mode has been
+// enabled by default.
+
+// UNSUPPORTED: !libcpp-has-hardened-mode
+// `check_assertion.h` is only available starting from C++11.
+// UNSUPPORTED: c++03
+// `check_assertion.h` requires Unix headers.
+// REQUIRES: has-unix-headers
+
+#include <cassert>
+#include "check_assertion.h"
+
+int main(int, char**) {
+  _LIBCPP_ASSERT_UNCATEGORIZED(true, "Should not fire");
+  TEST_LIBCPP_ASSERT_FAILURE([] {
+    _LIBCPP_ASSERT_UNCATEGORIZED(false, "Should fire");
+  }(), "Should fire");
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/assertions/modes/hardened_and_debug_mutually_exclusive.verify.cpp b/libcxx/test/libcxx/assertions/modes/hardened_and_debug_mutually_exclusive.verify.cpp
new file mode 100644
index 00000000000000..3e31d1d476bb1f
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/modes/hardened_and_debug_mutually_exclusive.verify.cpp
@@ -0,0 +1,17 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This test verifies that `_LIBCPP_ENABLE_HARDENED_MODE` and `_LIBCPP_ENABLE_DEBUG_MODE` are mutually exclusive.
+
+// Modules build produces a 
diff erent error ("Could not build module 'std'").
+// UNSUPPORTED: modules-build
+// ADDITIONAL_COMPILE_FLAGS: -Wno-macro-redefined -D_LIBCPP_ENABLE_HARDENED_MODE=1 -D_LIBCPP_ENABLE_DEBUG_MODE=1
+
+#include <cassert>
+
+// expected-error@*:*  {{Only one of _LIBCPP_ENABLE_HARDENED_MODE and _LIBCPP_ENABLE_DEBUG_MODE can be enabled.}}

diff  --git a/libcxx/test/libcxx/assertions/modes/hardened_mode_disabled_in_tu.pass.cpp b/libcxx/test/libcxx/assertions/modes/hardened_mode_disabled_in_tu.pass.cpp
new file mode 100644
index 00000000000000..6634d86a9a0965
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/modes/hardened_mode_disabled_in_tu.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This test ensures that we can disable the hardened mode on a per-TU basis regardless of how the library was built.
+
+// TODO(hardening): currently, explicitly enabling assertions enables all uncategorized assertions and overrides
+// disabling the hardened mode.
+// UNSUPPORTED: libcpp-has-debug-mode, libcpp-has-assertions
+// ADDITIONAL_COMPILE_FLAGS: -Wno-macro-redefined -D_LIBCPP_ENABLE_HARDENED_MODE=0
+
+#include <cassert>
+
+int main(int, char**) {
+  _LIBCPP_ASSERT_UNCATEGORIZED(true, "Should not fire");
+  _LIBCPP_ASSERT_UNCATEGORIZED(false, "Also should not fire");
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/assertions/modes/hardened_mode_enabled_in_tu.pass.cpp b/libcxx/test/libcxx/assertions/modes/hardened_mode_enabled_in_tu.pass.cpp
new file mode 100644
index 00000000000000..9bd7693ff71eb3
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/modes/hardened_mode_enabled_in_tu.pass.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This test ensures that we can enable the hardened mode on a per-TU basis regardless of how the library was built.
+
+// Debug mode would additionally trigger the error that hardened and debug modes are mutually exclusive.
+// UNSUPPORTED: libcpp-has-debug-mode
+// `check_assertion.h` is only available starting from C++11.
+// UNSUPPORTED: c++03
+// `check_assertion.h` requires Unix headers.
+// REQUIRES: has-unix-headers
+// The ability to set a custom abort message is required to compare the assertion message.
+// XFAIL: availability-verbose_abort-missing
+// ADDITIONAL_COMPILE_FLAGS: -Wno-macro-redefined -D_LIBCPP_ENABLE_HARDENED_MODE=1
+
+#include <cassert>
+#include "check_assertion.h"
+
+int main(int, char**) {
+  _LIBCPP_ASSERT_UNCATEGORIZED(true, "Should not fire");
+  TEST_LIBCPP_ASSERT_FAILURE([] {
+    _LIBCPP_ASSERT_UNCATEGORIZED(false, "Should fire");
+  }(), "Should fire");
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/assertions/modes/hardened_mode_not_1_or_0.verify.cpp b/libcxx/test/libcxx/assertions/modes/hardened_mode_not_1_or_0.verify.cpp
new file mode 100644
index 00000000000000..99dab47bfb9c2e
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/modes/hardened_mode_not_1_or_0.verify.cpp
@@ -0,0 +1,19 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This test verifies that setting the hardened mode to a value other than `0` or `1` triggers a compile-time error.
+
+// Debug mode would additionally trigger the error that hardened and debug modes are mutually exclusive.
+// UNSUPPORTED: libcpp-has-debug-mode
+// Modules build produces a 
diff erent error ("Could not build module 'std'").
+// UNSUPPORTED: modules-build
+// ADDITIONAL_COMPILE_FLAGS: -Wno-macro-redefined -D_LIBCPP_ENABLE_HARDENED_MODE=2
+
+#include <cassert>
+
+// expected-error@*:*  {{_LIBCPP_ENABLE_HARDENED_MODE must be set to 0 or 1.}}

diff  --git a/libcxx/test/libcxx/assertions/modes/hardened_no_assertions.pass.cpp b/libcxx/test/libcxx/assertions/modes/hardened_no_assertions.pass.cpp
new file mode 100644
index 00000000000000..704757c1138cfc
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/modes/hardened_no_assertions.pass.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that we can override whether assertions are enabled regardless of the hardening mode in use.
+
+// UNSUPPORTED: !libcpp-has-hardened-mode
+// ADDITIONAL_COMPILE_FLAGS: -Wno-macro-redefined -D_LIBCPP_ENABLE_ASSERTIONS=0
+
+#include <cassert>
+
+int main(int, char**) {
+  _LIBCPP_ASSERT_UNCATEGORIZED(true, "Should not fire");
+  _LIBCPP_ASSERT_UNCATEGORIZED(false, "Also should not fire");
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/assertions/modes/unchecked.pass.cpp b/libcxx/test/libcxx/assertions/modes/unchecked.pass.cpp
new file mode 100644
index 00000000000000..905011df8be502
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/modes/unchecked.pass.cpp
@@ -0,0 +1,24 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This test checks that if no hardening mode is defined (i.e., in the unchecked mode), by default assertions aren't
+// triggered.
+
+// UNSUPPORTED: libcpp-has-hardened-mode, libcpp-has-debug-mode
+
+#include <cassert>
+
+int main(int, char**) {
+  // TODO(hardening): remove the `#if` guard once `_LIBCPP_ENABLE_ASSERTIONS` no longer affects hardening modes.
+#if !_LIBCPP_ENABLE_ASSERTIONS
+  _LIBCPP_ASSERT_UNCATEGORIZED(true, "Should not fire");
+  _LIBCPP_ASSERT_UNCATEGORIZED(false, "Also should not fire");
+#endif
+
+  return 0;
+}

diff  --git a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/complexity.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/complexity.pass.cpp
index 701013e08d06a7..47b4f3bb19329f 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/complexity.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/complexity.pass.cpp
@@ -68,9 +68,13 @@ int main(int, char**) {
     std::sort_heap(first, last);
     LIBCPP_ASSERT(stats.copied == 0);
     LIBCPP_ASSERT(stats.moved <= 2 * n + n * logn);
+#if !_LIBCPP_ENABLE_DEBUG_MODE
     LIBCPP_ASSERT(stats.compared <= n * logn);
-    LIBCPP_ASSERT(std::is_sorted(first, last));
+    (void)debug_comparisons;
+#else
     LIBCPP_ASSERT(stats.compared <= 2 * n * logn + debug_comparisons);
+#endif
+    LIBCPP_ASSERT(std::is_sorted(first, last));
   }
   return 0;
 }

diff  --git a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/ranges_sort_heap.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/ranges_sort_heap.pass.cpp
index 551af24602c766..5723ed0d3db25e 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/ranges_sort_heap.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/ranges_sort_heap.pass.cpp
@@ -262,9 +262,13 @@ void test_complexity() {
     std::ranges::sort_heap(first, last, &MyInt::Comp);
     LIBCPP_ASSERT(stats.copied == 0);
     LIBCPP_ASSERT(stats.moved <= 2 * n + n * logn);
+#if !_LIBCPP_ENABLE_DEBUG_MODE
     LIBCPP_ASSERT(stats.compared <= n * logn);
-    LIBCPP_ASSERT(std::is_sorted(first, last, &MyInt::Comp));
+    (void)debug_comparisons;
+#else
     LIBCPP_ASSERT(stats.compared <= 2 * n * logn + debug_comparisons);
+#endif
+    LIBCPP_ASSERT(std::is_sorted(first, last, &MyInt::Comp));
   }
 }
 

diff  --git a/libcxx/test/std/algorithms/alg.sorting/alg.merge/inplace_merge_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.merge/inplace_merge_comp.pass.cpp
index 05ac2799a55a24..c82ad2623ee8b5 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.merge/inplace_merge_comp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.merge/inplace_merge_comp.pass.cpp
@@ -79,7 +79,9 @@ test_one(unsigned N, unsigned M)
         assert(ia[0] == static_cast<int>(N)-1);
         assert(ia[N-1] == 0);
         assert(std::is_sorted(ia, ia+N, std::greater<value_type>()));
+#if !_LIBCPP_ENABLE_DEBUG_MODE
         assert(pred.count() <= (N-1));
+#endif
     }
     delete [] ia;
 }

diff  --git a/libcxx/test/std/algorithms/alg.sorting/alg.three.way/lexicographical_compare_three_way_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.three.way/lexicographical_compare_three_way_comp.pass.cpp
index 77780054bb5f8e..dd413cfbaa2a92 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.three.way/lexicographical_compare_three_way_comp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.three.way/lexicographical_compare_three_way_comp.pass.cpp
@@ -156,7 +156,11 @@ constexpr void test_comparator_invocation_count() {
   // The comparator is invoked only `min(left.size(), right.size())` times
   test_lexicographical_compare<const int*, const int*>(
       std::array{0, 1, 2}, std::array{0, 1, 2, 3}, compare_last_digit_counting, std::strong_ordering::less);
+#if !_LIBCPP_ENABLE_DEBUG_MODE
   assert(compare_invocation_count <= 3);
+#else
+  assert(compare_invocation_count <= 6);
+#endif
 }
 
 // Check that it works with proxy iterators

diff  --git a/libcxx/test/support/container_debug_tests.h b/libcxx/test/support/container_debug_tests.h
index df5c03f775f658..07db855e58dc4a 100644
--- a/libcxx/test/support/container_debug_tests.h
+++ b/libcxx/test/support/container_debug_tests.h
@@ -14,7 +14,7 @@
 #error This header may only be used for libc++ tests
 #endif
 
-#ifndef _LIBCPP_ENABLE_DEBUG_MODE
+#if !_LIBCPP_ENABLE_DEBUG_MODE
 #error The library must be built with the debug mode enabled in order to use this header
 #endif
 

diff  --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index 48ff9ea7dfd6e4..0b0499250e8f96 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -486,6 +486,42 @@ steps:
           limit: 2
     timeout_in_minutes: 120
 
+  - label: "Hardened mode"
+    command: "libcxx/utils/ci/run-buildbot generic-hardened-mode"
+    artifact_paths:
+      - "**/test-results.xml"
+      - "**/*.abilist"
+    env:
+        CC: "clang-${LLVM_HEAD_VERSION}"
+        CXX: "clang++-${LLVM_HEAD_VERSION}"
+        ENABLE_CLANG_TIDY: "On"
+    agents:
+      queue: "libcxx-builders"
+      os: "linux"
+    retry:
+      automatic:
+        - exit_status: -1  # Agent was lost
+          limit: 2
+    timeout_in_minutes: 120
+
+  - label: "Debug mode"
+    command: "libcxx/utils/ci/run-buildbot generic-debug-mode"
+    artifact_paths:
+      - "**/test-results.xml"
+      - "**/*.abilist"
+    env:
+        CC: "clang-${LLVM_HEAD_VERSION}"
+        CXX: "clang++-${LLVM_HEAD_VERSION}"
+        ENABLE_CLANG_TIDY: "On"
+    agents:
+      queue: "libcxx-builders"
+      os: "linux"
+    retry:
+      automatic:
+        - exit_status: -1  # Agent was lost
+          limit: 2
+    timeout_in_minutes: 120
+
   - label: "With LLVM's libunwind"
     command: "libcxx/utils/ci/run-buildbot generic-with_llvm_unwinder"
     artifact_paths:

diff  --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index e1b7809df55cc4..6e20f167e540fd 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -388,6 +388,18 @@ generic-assertions)
     check-runtimes
     check-abi-list
 ;;
+generic-hardened-mode)
+    clean
+    generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-hardened-mode.cmake"
+    check-runtimes
+    check-abi-list
+;;
+generic-debug-mode)
+    clean
+    generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-debug-mode.cmake"
+    check-runtimes
+    check-abi-list
+;;
 generic-with_llvm_unwinder)
     clean
     generate-cmake -DLIBCXXABI_USE_LLVM_UNWINDER=ON

diff  --git a/libcxx/utils/libcxx/test/params.py b/libcxx/utils/libcxx/test/params.py
index 68cf1016efacbc..885e88048ec584 100644
--- a/libcxx/utils/libcxx/test/params.py
+++ b/libcxx/utils/libcxx/test/params.py
@@ -295,6 +295,23 @@ def getModuleFlag(cfg, enable_modules):
             AddFeature("libcpp-has-assertions"),
         ],
     ),
+    Parameter(
+        name="hardening_mode",
+        choices=["unchecked", "hardened", "debug"],
+        type=str,
+        default="unchecked",
+        help="Whether to enable the hardened mode or the debug mode when compiling the test suite. This is only "
+        "meaningful when running the tests against libc++.",
+        actions=lambda hardening_mode: filter(
+            None,
+            [
+                AddCompileFlag("-D_LIBCPP_ENABLE_HARDENED_MODE=1") if hardening_mode == "hardened" else None,
+                AddCompileFlag("-D_LIBCPP_ENABLE_DEBUG_MODE=1")    if hardening_mode == "debug" else None,
+                AddFeature("libcpp-has-hardened-mode")             if hardening_mode == "hardened" else None,
+                AddFeature("libcpp-has-debug-mode")                if hardening_mode == "debug" else None,
+            ],
+        ),
+    ),
     Parameter(
         name="additional_features",
         type=list,


        


More information about the libcxx-commits mailing list