[libcxx-commits] [libcxx] b85e186 - [libc++][hardening] Add back the safe mode.

Konstantin Varlamov via libcxx-commits libcxx-commits at lists.llvm.org
Tue Sep 12 12:02:04 PDT 2023


Author: Konstantin Varlamov
Date: 2023-09-12T12:01:51-07:00
New Revision: b85e1862c119e96b405e0fdd81a1b2120ec947df

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

LOG: [libc++][hardening] Add back the safe mode.

The safe mode is in-between the hardened and the debug modes, extending
the checks contained in the hardened mode with certain checks that are
relatively cheap and prevent common sources of errors but aren't
security-critical. Thus, the safe mode trades off some performance for
a wider set of checks, but unlike the debug mode, it can still be used
in production.

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

Added: 
    libcxx/cmake/caches/Generic-safe-mode.cmake
    libcxx/test/libcxx/assertions/modes/enabling_assertions_enables_safe_mode.pass.cpp
    libcxx/test/libcxx/assertions/modes/hardened_and_safe_mutually_exclusive.verify.cpp
    libcxx/test/libcxx/assertions/modes/safe.pass.cpp
    libcxx/test/libcxx/assertions/modes/safe_and_debug_mutually_exclusive.verify.cpp
    libcxx/test/libcxx/assertions/modes/safe_mode_disabled_in_tu.pass.cpp
    libcxx/test/libcxx/assertions/modes/safe_mode_enabled_in_tu.pass.cpp
    libcxx/test/libcxx/assertions/modes/safe_mode_not_1_or_0.verify.cpp

Modified: 
    libcxx/CMakeLists.txt
    libcxx/docs/Hardening.rst
    libcxx/docs/ReleaseNotes/17.rst
    libcxx/docs/ReleaseNotes/18.rst
    libcxx/docs/UsingLibcxx.rst
    libcxx/include/__config
    libcxx/include/__config_site.in
    libcxx/test/libcxx/algorithms/alg.sorting/assert.min.max.pass.cpp
    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/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/unchecked.pass.cpp
    libcxx/test/libcxx/containers/unord/unord.map/assert.bucket.pass.cpp
    libcxx/test/libcxx/containers/unord/unord.map/assert.bucket_size.pass.cpp
    libcxx/test/libcxx/containers/unord/unord.map/assert.max_load_factor.pass.cpp
    libcxx/test/libcxx/containers/unord/unord.multimap/assert.bucket.pass.cpp
    libcxx/test/libcxx/containers/unord/unord.multimap/assert.bucket_size.pass.cpp
    libcxx/test/libcxx/containers/unord/unord.multimap/assert.max_load_factor.pass.cpp
    libcxx/test/libcxx/containers/unord/unord.multiset/assert.bucket.pass.cpp
    libcxx/test/libcxx/containers/unord/unord.multiset/assert.bucket_size.pass.cpp
    libcxx/test/libcxx/containers/unord/unord.multiset/assert.max_load_factor.pass.cpp
    libcxx/test/libcxx/containers/unord/unord.set/assert.bucket.pass.cpp
    libcxx/test/libcxx/containers/unord/unord.set/assert.bucket_size.pass.cpp
    libcxx/test/libcxx/containers/unord/unord.set/assert.max_load_factor.pass.cpp
    libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.index_operator.pass.cpp
    libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.index_operator.pass.cpp
    libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.size.pass.cpp
    libcxx/test/libcxx/experimental/memory/memory.polymorphic.allocator.class/memory.polymorphic.allocator.mem/assert.deallocate.pass.cpp
    libcxx/test/libcxx/experimental/memory/memory.resource.adaptor/memory.resource.adaptor.mem/assert.deallocate.pass.cpp
    libcxx/test/libcxx/iterators/assert.advance.pass.cpp
    libcxx/test/libcxx/iterators/assert.next.pass.cpp
    libcxx/test/libcxx/iterators/assert.prev.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.drop.while/assert.begin.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/assert.equal.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/assert.equal.pass.cpp
    libcxx/test/libcxx/ranges/range.factories/range.repeat.view/ctor.piecewise.pass.cpp
    libcxx/test/libcxx/ranges/range.factories/range.repeat.view/ctor.value.bound.pass.cpp
    libcxx/test/libcxx/strings/string.view/assert.ctor.length.pass.cpp
    libcxx/test/libcxx/strings/string.view/assert.ctor.pointer.pass.cpp
    libcxx/test/libcxx/thread/futures/futures.promise/assert.set_exception.pass.cpp
    libcxx/test/libcxx/thread/futures/futures.promise/assert.set_exception_at_thread_exit.pass.cpp
    libcxx/test/libcxx/thread/thread.barrier/assert.arrive.pass.cpp
    libcxx/test/libcxx/thread/thread.barrier/assert.ctor.pass.cpp
    libcxx/test/libcxx/thread/thread.latch/assert.arrive_and_wait.pass.cpp
    libcxx/test/libcxx/thread/thread.latch/assert.count_down.pass.cpp
    libcxx/test/libcxx/thread/thread.latch/assert.ctor.pass.cpp
    libcxx/test/libcxx/thread/thread.semaphore/assert.ctor.pass.cpp
    libcxx/test/libcxx/thread/thread.semaphore/assert.release.pass.cpp
    libcxx/test/libcxx/utilities/assert.exception_guard.no_exceptions.pass.cpp
    libcxx/test/std/algorithms/alg.sorting/alg.clamp/assert.ranges_clamp.pass.cpp
    libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/pop.heap/assert.pop_heap.pass.cpp
    libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/pop.heap/assert.ranges_pop_heap.pass.cpp
    libcxx/test/std/utilities/utility/utility.unreachable/assert.unreachable.pass.cpp
    libcxx/utils/ci/buildkite-pipeline.yml
    libcxx/utils/ci/run-buildbot
    libcxx/utils/libcxx/test/params.py

Removed: 
    libcxx/test/libcxx/assertions/modes/enabling_assertions_enables_hardened_mode.pass.cpp


################################################################################
diff  --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index c729542d8cd370b..c0f281043c2b494 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -55,7 +55,7 @@ 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_SUPPORTED_HARDENING_MODES unchecked hardened safe 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
@@ -778,18 +778,25 @@ config_define_if_not(LIBCXX_ENABLE_TIME_ZONE_DATABASE _LIBCPP_HAS_NO_TIME_ZONE_D
 config_define_if_not(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS _LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS)
 if (LIBCXX_HARDENING_MODE STREQUAL "hardened")
   config_define(1 _LIBCPP_ENABLE_HARDENED_MODE_DEFAULT)
+  config_define(0 _LIBCPP_ENABLE_SAFE_MODE_DEFAULT)
+  config_define(0 _LIBCPP_ENABLE_DEBUG_MODE_DEFAULT)
+elseif (LIBCXX_HARDENING_MODE STREQUAL "safe")
+  config_define(0 _LIBCPP_ENABLE_HARDENED_MODE_DEFAULT)
+  config_define(1 _LIBCPP_ENABLE_SAFE_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(0 _LIBCPP_ENABLE_SAFE_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_SAFE_MODE_DEFAULT)
   config_define(0 _LIBCPP_ENABLE_DEBUG_MODE_DEFAULT)
 endif()
 # TODO(LLVM 19): Remove this after branching for LLVM 18, this is a simple
 # courtesy for vendors to be notified about this change.
 if (LIBCXX_ENABLE_ASSERTIONS)
-  message(FATAL_ERROR "LIBCXX_ENABLE_ASSERTIONS has been replaced by LIBCXX_HARDENING_MODE=hardened")
+  message(FATAL_ERROR "LIBCXX_ENABLE_ASSERTIONS has been replaced by LIBCXX_HARDENING_MODE=safe")
 endif()
 
 if (LIBCXX_PSTL_CPU_BACKEND STREQUAL "serial")

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

diff  --git a/libcxx/docs/Hardening.rst b/libcxx/docs/Hardening.rst
index c006c1f86ecf78e..ec115d3d6012bfe 100644
--- a/libcxx/docs/Hardening.rst
+++ b/libcxx/docs/Hardening.rst
@@ -1,30 +1,41 @@
-=============
-Hardened Mode
-=============
+===============
+Hardening Modes
+===============
 
 .. contents::
    :local:
 
-.. _using-hardened-mode:
+.. _using-hardening-modes:
 
-Using the hardened mode
-=======================
+Using hardening modes
+=====================
 
 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.
+In addition to the hardened mode, libc++ also provides two other hardening
+modes:
+- safe mode;
+- debug mode.
+
+The safe mode contains all the checks from the hardened mode and additionally
+some checks for undefined behavior that incur relatively little overhead but
+aren't security-critical. While the performance penalty is somewhat more
+significant compared to the hardened mode, the safe mode is still intended to be
+usable in production.
+
+The debug mode, in turn, contains all the checks from the safe 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 hardening mode by using the
 ``LIBCXX_HARDENING_MODE`` variable at CMake configuration time. 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 any hardening.
+similarly setting the variable to ``safe`` enables the safe mode, and to
+``debug`` enables the debug mode. The default value is ``unchecked`` which
+doesn't enable any hardening.
 
 When hardening is enabled, the compiled library is built with the corresponding
 mode enabled, **and** user code will be built with the same mode enabled by
@@ -35,17 +46,16 @@ vendor to know which level of hardening is enabled by default.
 
 Furthermore, independently of any vendor-selected default, users can always
 control which level of hardening is enabled in their code by defining
-``_LIBCPP_ENABLE_HARDENED_MODE=0|1`` (or ``_LIBCPP_ENABLE_DEBUG_MODE=0|1``)
-before including any libc++ header (we recommend passing
-``-D_LIBCPP_ENABLE_HARDENED_MODE=X`` or ``-D_LIBCPP_ENABLE_DEBUG_MODE=X`` to the
-compiler). Note that if the compiled library was built by the vendor in the
-unchecked mode, functions compiled inside the static or shared library won't
-have any hardening enabled even if the user compiles with hardening enabled (the
-same is true for the inverse case where the static or shared library was
-compiled **with** hardening enabled but the user tries to disable it). However,
-most of the code in libc++ is in the headers, so the user-selected value for
-``_LIBCPP_ENABLE_HARDENED_MODE`` or ``_LIBCPP_ENABLE_DEBUG_MODE`` (if any) will
-usually be respected.
+``_LIBCPP_ENABLE_HARDENED_MODE=0|1`` (or ``_LIBCPP_ENABLE_SAFE_MODE=0|1``, or
+``_LIBCPP_ENABLE_DEBUG_MODE=0|1``) before including any libc++ header (we
+recommend passing ``-D_LIBCPP_ENABLE_HARDENED_MODE=X``, etc. to the compiler).
+Note that if the compiled library was built by the vendor in the unchecked mode,
+functions compiled inside the static or shared library won't have any hardening
+enabled even if the user compiles with hardening enabled (the same is true for
+the inverse case where the static or shared library was compiled **with**
+hardening enabled but the user tries to disable it). However, most of the code
+in libc++ is in the headers, so the user-selected value for
+``_LIBCPP_ENABLE_HARDENED|SAFE|DEBUG_MODE``, if any, will usually be respected.
 
 Enabling hardening has no impact on the ABI.
 

diff  --git a/libcxx/docs/ReleaseNotes/17.rst b/libcxx/docs/ReleaseNotes/17.rst
index 4f9f17bdb5d7a9d..763dc3494ebfc73 100644
--- a/libcxx/docs/ReleaseNotes/17.rst
+++ b/libcxx/docs/ReleaseNotes/17.rst
@@ -102,19 +102,6 @@ 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/Hardening.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/Hardening.rst`` for more details.
-
 - ASan container annotations have been extended to cover all allocators in ``std::vector``.
 
 - ASan annotations have been added to the ``std::deque`` container, to detect container overflows.

diff  --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index baf270e5a45c8e4..83dade3bf2f0134 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -55,10 +55,36 @@ Implemented Papers
 Improvements and New Features
 -----------------------------
 
+- 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
+  :ref:`the hardening documentation <using-hardening-modes>` for more details.
+
+- The safe mode is now a part of hardening modes. Vendors can configure whether the safe mode is enabled by default
+  with the ``LIBCXX_HARDENING_MODE`` variable at CMake configuration time. Users can control whether the safe mode
+  is enabled on a per translation unit basis using the ``-D_LIBCPP_ENABLE_SAFE_MODE=1`` macro. The
+  ``_LIBCPP_ENABLE_ASSERTIONS`` macro that was previously used to enable the safe mode is now deprecated. See
+  :ref:`the hardening documentation <using-hardening-modes>` for more details.
+
+- The library now provides a debug mode which is a superset of the safe 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 :ref:`the hardening documentation <using-hardening-modes>` for more
+  details.
 
 Deprecations and Removals
 -------------------------
 
+- The "safe" mode is now controlled via the new generalized support for hardening. The ``LIBCXX_ENABLE_ASSERTIONS``
+  CMake variable that was used to enable the safe mode is now deprecated and setting it will trigger an error; use the
+  ``LIBCXX_HARDENING_MODE`` variable with the value ``safe`` instead. Similarly, the ``_LIBCPP_ENABLE_ASSERTIONS`` macro
+  is deprecated (setting it to ``1`` still enables the safe mode in this release while also issuing a deprecation
+  warning). ``_LIBCPP_ENABLE_ASSERTIONS`` will be removed entirely in the next release and setting it will become an
+  error. See :ref:`the hardening documentation <using-hardening-modes>` for more details.
+
 - The base template for ``std::char_traits`` has been removed. If you are using
   ``std::char_traits`` with types other than ``char``, ``wchar_t``, ``char8_t``,
   ``char16_t``, ``char32_t`` or a custom character type for which you

diff  --git a/libcxx/docs/UsingLibcxx.rst b/libcxx/docs/UsingLibcxx.rst
index 83ddbaa3c8382fc..9db25f12a6a7898 100644
--- a/libcxx/docs/UsingLibcxx.rst
+++ b/libcxx/docs/UsingLibcxx.rst
@@ -223,10 +223,13 @@ safety annotations.
   disabled and must be manually enabled by the user.
 
 **_LIBCPP_ENABLE_HARDENED_MODE**:
-  This macro is used to enable the :ref:`hardened mode <using-hardened-mode>`.
+  This macro is used to enable the :ref:`hardened mode <using-hardening-modes>`.
+
+**_LIBCPP_ENABLE_SAFE_MODE**:
+  This macro is used to enable the :ref:`safe mode <using-hardening-modes>`.
 
 **_LIBCPP_ENABLE_DEBUG_MODE**:
-  This macro is used to enable the :ref:`debug mode <using-hardened-mode>`.
+  This macro is used to enable the :ref:`debug mode <using-hardening-modes>`.
 
 **_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS**:
   This macro is used to disable all visibility annotations inside libc++.

diff  --git a/libcxx/include/__config b/libcxx/include/__config
index 69305d31144ad94..bf2564e2732ba98 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -204,26 +204,32 @@
 
 // TODO(hardening): remove this in LLVM 19.
 // This is for backward compatibility -- make enabling `_LIBCPP_ENABLE_ASSERTIONS` (which predates hardening modes)
-// equivalent to setting the hardened mode.
+// equivalent to setting the safe mode.
 #  ifdef _LIBCPP_ENABLE_ASSERTIONS
-#    warning "_LIBCPP_ENABLE_ASSERTIONS is deprecated, please use _LIBCPP_ENABLE_HARDENED_MODE instead."
+#    warning "_LIBCPP_ENABLE_ASSERTIONS is deprecated, please use _LIBCPP_ENABLE_SAFE_MODE instead."
 #    if _LIBCPP_ENABLE_ASSERTIONS != 0 && _LIBCPP_ENABLE_ASSERTIONS != 1
 #      error "_LIBCPP_ENABLE_ASSERTIONS must be set to 0 or 1"
 #    endif
 #    if _LIBCPP_ENABLE_ASSERTIONS
-#      define _LIBCPP_ENABLE_HARDENED_MODE 1
+#      define _LIBCPP_ENABLE_SAFE_MODE 1
 #    endif
 #  endif
 
 // 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`.
+// `_LIBCPP_ENABLE_SAFE_MODE` and `_LIBCPP_ENABLE_DEBUG_MODE`.
 //
 // #define _LIBCPP_ENABLE_HARDENED_MODE 1
 
+// Enables the safe mode which extends the hardened mode with checks that are relatively cheap and prevent common types
+// of errors but are not security-critical. Mutually exclusive with `_LIBCPP_ENABLE_HARDENED_MODE` and
+// `_LIBCPP_ENABLE_DEBUG_MODE`.
+//
+// #define _LIBCPP_ENABLE_SAFE_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`.
+// Mutually exclusive with `_LIBCPP_ENABLE_HARDENED_MODE` and `_LIBCPP_ENABLE_SAFE_MODE`.
 //
 // #define _LIBCPP_ENABLE_DEBUG_MODE 1
 
@@ -258,6 +264,13 @@
 #    error "_LIBCPP_ENABLE_HARDENED_MODE must be set to 0 or 1."
 #  endif
 
+#  ifndef _LIBCPP_ENABLE_SAFE_MODE
+#    define _LIBCPP_ENABLE_SAFE_MODE _LIBCPP_ENABLE_SAFE_MODE_DEFAULT
+#  endif
+#  if _LIBCPP_ENABLE_SAFE_MODE != 0 && _LIBCPP_ENABLE_SAFE_MODE != 1
+#    error "_LIBCPP_ENABLE_SAFE_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
@@ -265,13 +278,16 @@
 #    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."
+#  if (_LIBCPP_ENABLE_HARDENED_MODE && _LIBCPP_ENABLE_SAFE_MODE) ||                                                    \
+      (_LIBCPP_ENABLE_HARDENED_MODE && _LIBCPP_ENABLE_DEBUG_MODE) ||                                                   \
+      (_LIBCPP_ENABLE_SAFE_MODE && _LIBCPP_ENABLE_DEBUG_MODE)
+#    error                                                                                                             \
+        "Only one of _LIBCPP_ENABLE_HARDENED_MODE, _LIBCPP_ENABLE_SAFE_MODE and _LIBCPP_ENABLE_DEBUG_MODE can be enabled."
 #  endif
 
+// clang-format off
 // Hardened mode checks.
 
-// clang-format off
 #  if _LIBCPP_ENABLE_HARDENED_MODE
 
 // Enabled checks.
@@ -285,6 +301,19 @@
 #    define _LIBCPP_ASSERT_INTERNAL(expression, message)                 _LIBCPP_ASSUME(expression)
 #    define _LIBCPP_ASSERT_UNCATEGORIZED(expression, message)            _LIBCPP_ASSUME(expression)
 
+// Safe mode checks.
+
+#  elif _LIBCPP_ENABLE_SAFE_MODE
+
+// Enabled checks.
+#    define _LIBCPP_ASSERT_VALID_INPUT_RANGE(expression, message)        _LIBCPP_ASSERT(expression, message)
+#    define _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(expression, message)     _LIBCPP_ASSERT(expression, message)
+#    define _LIBCPP_ASSERT_NON_OVERLAPPING_RANGES(expression, message)   _LIBCPP_ASSERT(expression, message)
+#    define _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(expression, message)     _LIBCPP_ASSERT(expression, message)
+#    define _LIBCPP_ASSERT_UNCATEGORIZED(expression, message)            _LIBCPP_ASSERT(expression, message)
+// Disabled checks.
+#    define _LIBCPP_ASSERT_INTERNAL(expression, message)                 _LIBCPP_ASSUME(expression)
+
 // Debug mode checks.
 
 #  elif _LIBCPP_ENABLE_DEBUG_MODE

diff  --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index 238d001da0eedd5..c85cbcd02c441b9 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -37,6 +37,7 @@
 
 // Hardening.
 #cmakedefine01 _LIBCPP_ENABLE_HARDENED_MODE_DEFAULT
+#cmakedefine01 _LIBCPP_ENABLE_SAFE_MODE_DEFAULT
 #cmakedefine01 _LIBCPP_ENABLE_DEBUG_MODE_DEFAULT
 
 // __USE_MINGW_ANSI_STDIO gets redefined on MinGW

diff  --git a/libcxx/test/libcxx/algorithms/alg.sorting/assert.min.max.pass.cpp b/libcxx/test/libcxx/algorithms/alg.sorting/assert.min.max.pass.cpp
index 09a2d34cde2beb1..7bbf2735a90d909 100644
--- a/libcxx/test/libcxx/algorithms/alg.sorting/assert.min.max.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/alg.sorting/assert.min.max.pass.cpp
@@ -10,7 +10,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 #include <algorithm>

diff  --git a/libcxx/test/libcxx/assertions/modes/debug.pass.cpp b/libcxx/test/libcxx/assertions/modes/debug.pass.cpp
index 8cba5c9c12d3c84..781fa57c9cf88a4 100644
--- a/libcxx/test/libcxx/assertions/modes/debug.pass.cpp
+++ b/libcxx/test/libcxx/assertions/modes/debug.pass.cpp
@@ -19,9 +19,9 @@
 #include "check_assertion.h"
 
 int main(int, char**) {
-  _LIBCPP_ASSERT_UNCATEGORIZED(true, "Should not fire");
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(true, "Should not fire");
   TEST_LIBCPP_ASSERT_FAILURE([] {
-    _LIBCPP_ASSERT_UNCATEGORIZED(false, "Should fire");
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(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
index 32e5ad9685e797a..fc8158ca0d696ca 100644
--- 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
@@ -6,16 +6,18 @@
 //
 //===----------------------------------------------------------------------===//
 
-// This test ensures that we can disable the debug mode on a per-TU basis regardless of how the library was built.
+// This test ensures that we can disable the debug mode on a per-TU basis.
 
-// UNSUPPORTED: libcpp-hardening-mode=hardened
+// Other hardening modes would still make the assertions fire (disabling the debug mode doesn't disable e.g. the
+// hardened mode).
+// REQUIRES: libcpp-hardening-mode=debug
 // 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");
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(true, "Should not fire");
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(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
index dbf3cb7b2e7e498..78373b992569cb4 100644
--- 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
@@ -6,10 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 
-// This test ensures that we can enable the debug mode on a per-TU basis regardless of how the library was built.
+// This test ensures that we can enable the debug mode on a per-TU basis.
 
-// Hardened mode would additionally trigger the error that hardened and debug modes are mutually exclusive.
-// UNSUPPORTED: libcpp-hardening-mode=hardened
+// Other hardening modes would additionally trigger the error that they are mutually exclusive.
+// REQUIRES: libcpp-hardening-mode=unchecked
 // `check_assertion.h` is only available starting from C++11 and requires Unix headers.
 // UNSUPPORTED: c++03, !has-unix-headers
 // The ability to set a custom abort message is required to compare the assertion message.
@@ -20,9 +20,9 @@
 #include "check_assertion.h"
 
 int main(int, char**) {
-  _LIBCPP_ASSERT_UNCATEGORIZED(true, "Should not fire");
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(true, "Should not fire");
   TEST_LIBCPP_ASSERT_FAILURE([] {
-    _LIBCPP_ASSERT_UNCATEGORIZED(false, "Should fire");
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(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
index 7ff4eacfdcefeed..b3b249fc0e15cbb 100644
--- 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
@@ -8,8 +8,8 @@
 
 // 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-hardening-mode=hardened
+// Other hardening modes would additionally trigger the error that they are mutually exclusive.
+// REQUIRES: libcpp-hardening-mode=unchecked
 // Modules build produces a 
diff erent error ("Could not build module 'std'").
 // UNSUPPORTED: clang-modules-build
 // ADDITIONAL_COMPILE_FLAGS: -Wno-macro-redefined -D_LIBCPP_ENABLE_DEBUG_MODE=2

diff  --git a/libcxx/test/libcxx/assertions/modes/enabling_assertions_enables_hardened_mode.pass.cpp b/libcxx/test/libcxx/assertions/modes/enabling_assertions_enables_safe_mode.pass.cpp
similarity index 78%
rename from libcxx/test/libcxx/assertions/modes/enabling_assertions_enables_hardened_mode.pass.cpp
rename to libcxx/test/libcxx/assertions/modes/enabling_assertions_enables_safe_mode.pass.cpp
index 48243a58e74627d..a4e3d5978155827 100644
--- a/libcxx/test/libcxx/assertions/modes/enabling_assertions_enables_hardened_mode.pass.cpp
+++ b/libcxx/test/libcxx/assertions/modes/enabling_assertions_enables_safe_mode.pass.cpp
@@ -7,14 +7,14 @@
 //===----------------------------------------------------------------------===//
 
 // TODO(hardening): remove in LLVM 19.
-// This test ensures that enabling assertions now enables the hardened mode.
+// This test ensures that enabling assertions now enables the safe mode.
 
+// Other hardening modes would additionally trigger the error that they are mutually exclusive.
+// REQUIRES: libcpp-hardening-mode=unchecked
 // `check_assertion.h` is only available starting from C++11 and requires Unix headers.
 // UNSUPPORTED: c++03, !has-unix-headers
 // The ability to set a custom abort message is required to compare the assertion message.
 // XFAIL: availability-verbose_abort-missing
-// Debug mode is mutually exclusive with hardened mode.
-// UNSUPPORTED: libcpp-hardening-mode=debug
 // Ignore the warning about `_LIBCPP_ENABLE_ASSERTIONS` being deprecated.
 // ADDITIONAL_COMPILE_FLAGS: -Wno-error -D_LIBCPP_ENABLE_ASSERTIONS=1
 
@@ -22,7 +22,7 @@
 #include "check_assertion.h"
 
 int main(int, char**) {
-  static_assert(_LIBCPP_ENABLE_HARDENED_MODE == 1, "Hardened mode should be implicitly enabled");
+  static_assert(_LIBCPP_ENABLE_SAFE_MODE == 1, "Safe mode should be implicitly enabled");
 
   _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(true, "Should not fire");
   TEST_LIBCPP_ASSERT_FAILURE([] {

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
index d17684e7bcc0ddf..d7b2610687578ff 100644
--- 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
@@ -8,10 +8,11 @@
 
 // This test verifies that `_LIBCPP_ENABLE_HARDENED_MODE` and `_LIBCPP_ENABLE_DEBUG_MODE` are mutually exclusive.
 
+// REQUIRES: libcpp-hardening-mode=unchecked
 // Modules build produces a 
diff erent error ("Could not build module 'std'").
 // UNSUPPORTED: clang-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.}}
+// expected-error@*:*  {{Only one of _LIBCPP_ENABLE_HARDENED_MODE, _LIBCPP_ENABLE_SAFE_MODE and _LIBCPP_ENABLE_DEBUG_MODE can be enabled.}}

diff  --git a/libcxx/test/libcxx/assertions/modes/hardened_and_safe_mutually_exclusive.verify.cpp b/libcxx/test/libcxx/assertions/modes/hardened_and_safe_mutually_exclusive.verify.cpp
new file mode 100644
index 000000000000000..464f9b86de236c4
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/modes/hardened_and_safe_mutually_exclusive.verify.cpp
@@ -0,0 +1,18 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_SAFE_MODE` are mutually exclusive.
+
+// REQUIRES: libcpp-hardening-mode=unchecked
+// Modules build produces a 
diff erent error ("Could not build module 'std'").
+// UNSUPPORTED: clang-modules-build
+// ADDITIONAL_COMPILE_FLAGS: -Wno-macro-redefined -D_LIBCPP_ENABLE_HARDENED_MODE=1 -D_LIBCPP_ENABLE_SAFE_MODE=1
+
+#include <cassert>
+
+// expected-error@*:*  {{Only one of _LIBCPP_ENABLE_HARDENED_MODE, _LIBCPP_ENABLE_SAFE_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
index 642773c2e76347b..161275977166a58 100644
--- 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
@@ -6,16 +6,18 @@
 //
 //===----------------------------------------------------------------------===//
 
-// This test ensures that we can disable the hardened mode on a per-TU basis regardless of how the library was built.
+// This test ensures that we can disable the hardened mode on a per-TU basis.
 
-// UNSUPPORTED: libcpp-hardening-mode=debug
+// Other hardening modes would still make the assertions fire (disabling the hardened mode doesn't disable e.g. the
+// debug mode).
+// REQUIRES: libcpp-hardening-mode=hardened
 // 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");
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(true, "Should not fire");
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(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
index f4ceace9f45f571..e652ee338ca8e96 100644
--- 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
@@ -6,10 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 
-// This test ensures that we can enable the hardened mode on a per-TU basis regardless of how the library was built.
+// This test ensures that we can enable the hardened mode on a per-TU basis.
 
-// Debug mode would additionally trigger the error that hardened and debug modes are mutually exclusive.
-// UNSUPPORTED: libcpp-hardening-mode=debug
+// Other hardening modes would additionally trigger the error that they are mutually exclusive.
+// REQUIRES: libcpp-hardening-mode=unchecked
 // `check_assertion.h` is only available starting from C++11.
 // UNSUPPORTED: c++03
 // `check_assertion.h` requires Unix headers.

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
index fc9b41d3c0f0469..f11148d63a95348 100644
--- 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
@@ -8,8 +8,8 @@
 
 // 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-hardening-mode=debug
+// Other hardening modes would additionally trigger the error that they are mutually exclusive.
+// REQUIRES: libcpp-hardening-mode=unchecked
 // Modules build produces a 
diff erent error ("Could not build module 'std'").
 // UNSUPPORTED: clang-modules-build
 // ADDITIONAL_COMPILE_FLAGS: -Wno-macro-redefined -D_LIBCPP_ENABLE_HARDENED_MODE=2

diff  --git a/libcxx/test/libcxx/assertions/modes/safe.pass.cpp b/libcxx/test/libcxx/assertions/modes/safe.pass.cpp
new file mode 100644
index 000000000000000..7174fd3078d63b9
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/modes/safe.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 safe mode has been
+// enabled by default.
+
+// REQUIRES: libcpp-hardening-mode=safe
+// `check_assertion.h` is only available starting from C++11.
+// UNSUPPORTED: c++03
+// `check_assertion.h` requires Unix headers.
+// REQUIRES: has-unix-headers
+// XFAIL: availability-verbose_abort-missing
+
+#include <cassert>
+#include "check_assertion.h"
+
+int main(int, char**) {
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(true, "Should not fire");
+  TEST_LIBCPP_ASSERT_FAILURE([] {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(false, "Should fire");
+  }(), "Should fire");
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/assertions/modes/safe_and_debug_mutually_exclusive.verify.cpp b/libcxx/test/libcxx/assertions/modes/safe_and_debug_mutually_exclusive.verify.cpp
new file mode 100644
index 000000000000000..5ef9bab0abe66fd
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/modes/safe_and_debug_mutually_exclusive.verify.cpp
@@ -0,0 +1,18 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_SAFE_MODE` and `_LIBCPP_ENABLE_DEBUG_MODE` are mutually exclusive.
+
+// REQUIRES: libcpp-hardening-mode=unchecked
+// Modules build produces a 
diff erent error ("Could not build module 'std'").
+// UNSUPPORTED: clang-modules-build
+// ADDITIONAL_COMPILE_FLAGS: -Wno-macro-redefined -D_LIBCPP_ENABLE_SAFE_MODE=1 -D_LIBCPP_ENABLE_DEBUG_MODE=1
+
+#include <cassert>
+
+// expected-error@*:*  {{Only one of _LIBCPP_ENABLE_HARDENED_MODE, _LIBCPP_ENABLE_SAFE_MODE and _LIBCPP_ENABLE_DEBUG_MODE can be enabled.}}

diff  --git a/libcxx/test/libcxx/assertions/modes/safe_mode_disabled_in_tu.pass.cpp b/libcxx/test/libcxx/assertions/modes/safe_mode_disabled_in_tu.pass.cpp
new file mode 100644
index 000000000000000..81d155705f1924e
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/modes/safe_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 safe mode on a per-TU basis.
+
+// Other hardening modes would still make the assertions fire (disabling the safe mode doesn't disable e.g. the debug
+// mode).
+// REQUIRES: libcpp-hardening-mode=safe
+// ADDITIONAL_COMPILE_FLAGS: -Wno-macro-redefined -D_LIBCPP_ENABLE_SAFE_MODE=0
+
+#include <cassert>
+
+int main(int, char**) {
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(true, "Should not fire");
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(false, "Also should not fire");
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/assertions/modes/safe_mode_enabled_in_tu.pass.cpp b/libcxx/test/libcxx/assertions/modes/safe_mode_enabled_in_tu.pass.cpp
new file mode 100644
index 000000000000000..4f9b236f920db4e
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/modes/safe_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 safe mode on a per-TU basis.
+
+// Other hardening modes would additionally trigger the error that they are mutually exclusive.
+// REQUIRES: libcpp-hardening-mode=unchecked
+// `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_SAFE_MODE=1
+
+#include <cassert>
+#include "check_assertion.h"
+
+int main(int, char**) {
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(true, "Should not fire");
+  TEST_LIBCPP_ASSERT_FAILURE([] {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(false, "Should fire");
+  }(), "Should fire");
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/assertions/modes/safe_mode_not_1_or_0.verify.cpp b/libcxx/test/libcxx/assertions/modes/safe_mode_not_1_or_0.verify.cpp
new file mode 100644
index 000000000000000..578d1123a3e80dc
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/modes/safe_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 safe mode to a value other than `0` or `1` triggers a compile-time error.
+
+// Other hardening modes would additionally trigger the error that they are mutually exclusive.
+// REQUIRES: libcpp-hardening-mode=unchecked
+// Modules build produces a 
diff erent error ("Could not build module 'std'").
+// UNSUPPORTED: clang-modules-build
+// ADDITIONAL_COMPILE_FLAGS: -Wno-macro-redefined -D_LIBCPP_ENABLE_SAFE_MODE=2
+
+#include <cassert>
+
+// expected-error@*:*  {{_LIBCPP_ENABLE_SAFE_MODE must be set to 0 or 1.}}

diff  --git a/libcxx/test/libcxx/assertions/modes/unchecked.pass.cpp b/libcxx/test/libcxx/assertions/modes/unchecked.pass.cpp
index 3a6edf2c335a077..0e0d86983081990 100644
--- a/libcxx/test/libcxx/assertions/modes/unchecked.pass.cpp
+++ b/libcxx/test/libcxx/assertions/modes/unchecked.pass.cpp
@@ -17,9 +17,9 @@ bool executed_condition = false;
 bool f() { executed_condition = true; return false; }
 
 int main(int, char**) {
-  _LIBCPP_ASSERT_UNCATEGORIZED(true, "Should not fire");
-  _LIBCPP_ASSERT_UNCATEGORIZED(false, "Also should not fire");
-  _LIBCPP_ASSERT_UNCATEGORIZED(f(), "Should not execute anything");
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(true, "Should not fire");
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(false, "Also should not fire");
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(f(), "Should not execute anything");
   assert(!executed_condition); // Really make sure we did not execute anything.
 
   return 0;

diff  --git a/libcxx/test/libcxx/containers/unord/unord.map/assert.bucket.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.map/assert.bucket.pass.cpp
index 97fd4a07a1cbfb8..72114e6e4623bb9 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/assert.bucket.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/assert.bucket.pass.cpp
@@ -12,7 +12,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 #include <unordered_map>

diff  --git a/libcxx/test/libcxx/containers/unord/unord.map/assert.bucket_size.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.map/assert.bucket_size.pass.cpp
index 9541b82b83e42c7..b8191f8df089b15 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/assert.bucket_size.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/assert.bucket_size.pass.cpp
@@ -16,7 +16,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 #include <unordered_map>

diff  --git a/libcxx/test/libcxx/containers/unord/unord.map/assert.max_load_factor.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.map/assert.max_load_factor.pass.cpp
index b02e95499563036..8d7b2906c7cbc92 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/assert.max_load_factor.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/assert.max_load_factor.pass.cpp
@@ -17,7 +17,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 #include <unordered_map>

diff  --git a/libcxx/test/libcxx/containers/unord/unord.multimap/assert.bucket.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/assert.bucket.pass.cpp
index ceafa41a73e13bf..728c9fb2415f608 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/assert.bucket.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/assert.bucket.pass.cpp
@@ -16,7 +16,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 #include <unordered_map>

diff  --git a/libcxx/test/libcxx/containers/unord/unord.multimap/assert.bucket_size.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/assert.bucket_size.pass.cpp
index 1ae8cc64b1fd1c2..c07e6e3af8d11e2 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/assert.bucket_size.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/assert.bucket_size.pass.cpp
@@ -16,7 +16,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 #include <unordered_map>

diff  --git a/libcxx/test/libcxx/containers/unord/unord.multimap/assert.max_load_factor.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/assert.max_load_factor.pass.cpp
index 0ebfc85f68e754d..fa126bfcd775b5a 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/assert.max_load_factor.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/assert.max_load_factor.pass.cpp
@@ -17,7 +17,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 #include <unordered_map>

diff  --git a/libcxx/test/libcxx/containers/unord/unord.multiset/assert.bucket.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/assert.bucket.pass.cpp
index 22c0d345c317957..5f29e6b2b9545bf 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multiset/assert.bucket.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/assert.bucket.pass.cpp
@@ -16,7 +16,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 #include <unordered_set>

diff  --git a/libcxx/test/libcxx/containers/unord/unord.multiset/assert.bucket_size.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/assert.bucket_size.pass.cpp
index af3793602a5c899..21606acbe253fe4 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multiset/assert.bucket_size.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/assert.bucket_size.pass.cpp
@@ -16,7 +16,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 #include <unordered_set>

diff  --git a/libcxx/test/libcxx/containers/unord/unord.multiset/assert.max_load_factor.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/assert.max_load_factor.pass.cpp
index 2fa7dd490f2bd11..053f9f96b457f98 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multiset/assert.max_load_factor.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/assert.max_load_factor.pass.cpp
@@ -17,7 +17,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 #include <unordered_set>

diff  --git a/libcxx/test/libcxx/containers/unord/unord.set/assert.bucket.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.set/assert.bucket.pass.cpp
index 07867734b730b14..69cbaf94359c44a 100644
--- a/libcxx/test/libcxx/containers/unord/unord.set/assert.bucket.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.set/assert.bucket.pass.cpp
@@ -16,7 +16,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 #include <unordered_set>

diff  --git a/libcxx/test/libcxx/containers/unord/unord.set/assert.bucket_size.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.set/assert.bucket_size.pass.cpp
index 1c4d8259f851fe1..e88033c0f482fd8 100644
--- a/libcxx/test/libcxx/containers/unord/unord.set/assert.bucket_size.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.set/assert.bucket_size.pass.cpp
@@ -16,7 +16,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 #include <unordered_set>

diff  --git a/libcxx/test/libcxx/containers/unord/unord.set/assert.max_load_factor.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.set/assert.max_load_factor.pass.cpp
index fd0a91d76cd5f11..0d79d0f9bb0a0e1 100644
--- a/libcxx/test/libcxx/containers/unord/unord.set/assert.max_load_factor.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.set/assert.max_load_factor.pass.cpp
@@ -17,7 +17,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 #include <unordered_set>

diff  --git a/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.index_operator.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.index_operator.pass.cpp
index 361811f7485fd77..2f3ac2b9de16c06 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.index_operator.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.index_operator.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 // <mdspan>

diff  --git a/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.index_operator.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.index_operator.pass.cpp
index 96a7f217f954563..c6e67efa03fd7bf 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.index_operator.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.index_operator.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 // <mdspan>

diff  --git a/libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.size.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.size.pass.cpp
index b4298435af60f84..a10d89bc86d4b69 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.size.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.size.pass.cpp
@@ -6,7 +6,7 @@
 //===----------------------------------------------------------------------===//
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 // <mdspan>

diff  --git a/libcxx/test/libcxx/experimental/memory/memory.polymorphic.allocator.class/memory.polymorphic.allocator.mem/assert.deallocate.pass.cpp b/libcxx/test/libcxx/experimental/memory/memory.polymorphic.allocator.class/memory.polymorphic.allocator.mem/assert.deallocate.pass.cpp
index 97ff41e8466bbfd..e12e0fc7280d1f6 100644
--- a/libcxx/test/libcxx/experimental/memory/memory.polymorphic.allocator.class/memory.polymorphic.allocator.mem/assert.deallocate.pass.cpp
+++ b/libcxx/test/libcxx/experimental/memory/memory.polymorphic.allocator.class/memory.polymorphic.allocator.mem/assert.deallocate.pass.cpp
@@ -14,7 +14,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 

diff  --git a/libcxx/test/libcxx/experimental/memory/memory.resource.adaptor/memory.resource.adaptor.mem/assert.deallocate.pass.cpp b/libcxx/test/libcxx/experimental/memory/memory.resource.adaptor/memory.resource.adaptor.mem/assert.deallocate.pass.cpp
index 1cf365498221014..99b1ad6e6957af1 100644
--- a/libcxx/test/libcxx/experimental/memory/memory.resource.adaptor/memory.resource.adaptor.mem/assert.deallocate.pass.cpp
+++ b/libcxx/test/libcxx/experimental/memory/memory.resource.adaptor/memory.resource.adaptor.mem/assert.deallocate.pass.cpp
@@ -14,7 +14,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 

diff  --git a/libcxx/test/libcxx/iterators/assert.advance.pass.cpp b/libcxx/test/libcxx/iterators/assert.advance.pass.cpp
index 87ee8978521458c..4fe78cc3ff1167d 100644
--- a/libcxx/test/libcxx/iterators/assert.advance.pass.cpp
+++ b/libcxx/test/libcxx/iterators/assert.advance.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 // <list>

diff  --git a/libcxx/test/libcxx/iterators/assert.next.pass.cpp b/libcxx/test/libcxx/iterators/assert.next.pass.cpp
index 5de43f16fad37fc..2bf2e5702b6d080 100644
--- a/libcxx/test/libcxx/iterators/assert.next.pass.cpp
+++ b/libcxx/test/libcxx/iterators/assert.next.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 // <list>

diff  --git a/libcxx/test/libcxx/iterators/assert.prev.pass.cpp b/libcxx/test/libcxx/iterators/assert.prev.pass.cpp
index bdcdc64aa57257f..a8600e51eb81ded 100644
--- a/libcxx/test/libcxx/iterators/assert.prev.pass.cpp
+++ b/libcxx/test/libcxx/iterators/assert.prev.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 // <list>

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.drop.while/assert.begin.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.drop.while/assert.begin.pass.cpp
index e90c63c5fc8b48a..dcca2dff79e76f8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.drop.while/assert.begin.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop.while/assert.begin.pass.cpp
@@ -13,7 +13,7 @@
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: no-exceptions
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 #include <ranges>

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/assert.equal.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/assert.equal.pass.cpp
index daccc6092782122..a15c5bacadb045c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/assert.equal.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/assert.equal.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 // <ranges>

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/assert.equal.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/assert.equal.pass.cpp
index 3036130a6122669..e72900bdb4eb04a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/assert.equal.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/assert.equal.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 // <ranges>

diff  --git a/libcxx/test/libcxx/ranges/range.factories/range.repeat.view/ctor.piecewise.pass.cpp b/libcxx/test/libcxx/ranges/range.factories/range.repeat.view/ctor.piecewise.pass.cpp
index 1d9c321e0088540..2b2632081246632 100644
--- a/libcxx/test/libcxx/ranges/range.factories/range.repeat.view/ctor.piecewise.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.factories/range.repeat.view/ctor.piecewise.pass.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // REQUIRES: has-unix-headers
 // XFAIL: availability-verbose_abort-missing
 

diff  --git a/libcxx/test/libcxx/ranges/range.factories/range.repeat.view/ctor.value.bound.pass.cpp b/libcxx/test/libcxx/ranges/range.factories/range.repeat.view/ctor.value.bound.pass.cpp
index b9c745a9633aae7..a648e41c78d7540 100644
--- a/libcxx/test/libcxx/ranges/range.factories/range.repeat.view/ctor.value.bound.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.factories/range.repeat.view/ctor.value.bound.pass.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // REQUIRES: has-unix-headers
 // XFAIL: availability-verbose_abort-missing
 

diff  --git a/libcxx/test/libcxx/strings/string.view/assert.ctor.length.pass.cpp b/libcxx/test/libcxx/strings/string.view/assert.ctor.length.pass.cpp
index fa24b31e4196685..9eb12cbe07bd3b4 100644
--- a/libcxx/test/libcxx/strings/string.view/assert.ctor.length.pass.cpp
+++ b/libcxx/test/libcxx/strings/string.view/assert.ctor.length.pass.cpp
@@ -7,9 +7,8 @@
 //===----------------------------------------------------------------------===//
 
 // REQUIRES: has-unix-headers
-
 // UNSUPPORTED: c++03, c++11
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 // Construct a string_view from an invalid length

diff  --git a/libcxx/test/libcxx/strings/string.view/assert.ctor.pointer.pass.cpp b/libcxx/test/libcxx/strings/string.view/assert.ctor.pointer.pass.cpp
index f481507f2ebcb35..37d1f21cf30825e 100644
--- a/libcxx/test/libcxx/strings/string.view/assert.ctor.pointer.pass.cpp
+++ b/libcxx/test/libcxx/strings/string.view/assert.ctor.pointer.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, c++11, c++14
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 // Construct a string_view from a null pointer

diff  --git a/libcxx/test/libcxx/thread/futures/futures.promise/assert.set_exception.pass.cpp b/libcxx/test/libcxx/thread/futures/futures.promise/assert.set_exception.pass.cpp
index 14a4e25594c7af2..0796e7dd36bf069 100644
--- a/libcxx/test/libcxx/thread/futures/futures.promise/assert.set_exception.pass.cpp
+++ b/libcxx/test/libcxx/thread/futures/futures.promise/assert.set_exception.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, no-threads
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 // <future>

diff  --git a/libcxx/test/libcxx/thread/futures/futures.promise/assert.set_exception_at_thread_exit.pass.cpp b/libcxx/test/libcxx/thread/futures/futures.promise/assert.set_exception_at_thread_exit.pass.cpp
index 565018b1366784d..7e2c14ef64d4785 100644
--- a/libcxx/test/libcxx/thread/futures/futures.promise/assert.set_exception_at_thread_exit.pass.cpp
+++ b/libcxx/test/libcxx/thread/futures/futures.promise/assert.set_exception_at_thread_exit.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, no-threads
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 // <future>

diff  --git a/libcxx/test/libcxx/thread/thread.barrier/assert.arrive.pass.cpp b/libcxx/test/libcxx/thread/thread.barrier/assert.arrive.pass.cpp
index f67d3d48afe3f8d..f9b0891548dd0a1 100644
--- a/libcxx/test/libcxx/thread/thread.barrier/assert.arrive.pass.cpp
+++ b/libcxx/test/libcxx/thread/thread.barrier/assert.arrive.pass.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 
 // XFAIL: availability-verbose_abort-missing
 

diff  --git a/libcxx/test/libcxx/thread/thread.barrier/assert.ctor.pass.cpp b/libcxx/test/libcxx/thread/thread.barrier/assert.ctor.pass.cpp
index 5b4a0af586f827c..25a2e6a099cd7b7 100644
--- a/libcxx/test/libcxx/thread/thread.barrier/assert.ctor.pass.cpp
+++ b/libcxx/test/libcxx/thread/thread.barrier/assert.ctor.pass.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 
 // XFAIL: availability-verbose_abort-missing
 

diff  --git a/libcxx/test/libcxx/thread/thread.latch/assert.arrive_and_wait.pass.cpp b/libcxx/test/libcxx/thread/thread.latch/assert.arrive_and_wait.pass.cpp
index dfeca1674efa204..4a9ccadd7c255ba 100644
--- a/libcxx/test/libcxx/thread/thread.latch/assert.arrive_and_wait.pass.cpp
+++ b/libcxx/test/libcxx/thread/thread.latch/assert.arrive_and_wait.pass.cpp
@@ -17,7 +17,7 @@
 // Make sure that calling arrive_and_wait with a negative value triggers an assertion.
 
 // REQUIRES: has-unix-headers
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 #include <latch>

diff  --git a/libcxx/test/libcxx/thread/thread.latch/assert.count_down.pass.cpp b/libcxx/test/libcxx/thread/thread.latch/assert.count_down.pass.cpp
index 8f0696937dff3f7..67185b11a40698d 100644
--- a/libcxx/test/libcxx/thread/thread.latch/assert.count_down.pass.cpp
+++ b/libcxx/test/libcxx/thread/thread.latch/assert.count_down.pass.cpp
@@ -18,7 +18,7 @@
 // higher than the internal counter triggers an assertion.
 
 // REQUIRES: has-unix-headers
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 #include <latch>

diff  --git a/libcxx/test/libcxx/thread/thread.latch/assert.ctor.pass.cpp b/libcxx/test/libcxx/thread/thread.latch/assert.ctor.pass.cpp
index 35a7d09b09c0328..5cd1272d44ff2d6 100644
--- a/libcxx/test/libcxx/thread/thread.latch/assert.ctor.pass.cpp
+++ b/libcxx/test/libcxx/thread/thread.latch/assert.ctor.pass.cpp
@@ -17,7 +17,7 @@
 // Make sure that calling latch with a negative value triggers an assertion
 
 // REQUIRES: has-unix-headers
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 #include <latch>

diff  --git a/libcxx/test/libcxx/thread/thread.semaphore/assert.ctor.pass.cpp b/libcxx/test/libcxx/thread/thread.semaphore/assert.ctor.pass.cpp
index ec41b6fa84db4f2..5a3b87814cb3d4f 100644
--- a/libcxx/test/libcxx/thread/thread.semaphore/assert.ctor.pass.cpp
+++ b/libcxx/test/libcxx/thread/thread.semaphore/assert.ctor.pass.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 
 // XFAIL: availability-verbose_abort-missing
 

diff  --git a/libcxx/test/libcxx/thread/thread.semaphore/assert.release.pass.cpp b/libcxx/test/libcxx/thread/thread.semaphore/assert.release.pass.cpp
index 4d731cd7be42380..b4219d339234c8f 100644
--- a/libcxx/test/libcxx/thread/thread.semaphore/assert.release.pass.cpp
+++ b/libcxx/test/libcxx/thread/thread.semaphore/assert.release.pass.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 
 // XFAIL: availability-verbose_abort-missing
 

diff  --git a/libcxx/test/libcxx/utilities/assert.exception_guard.no_exceptions.pass.cpp b/libcxx/test/libcxx/utilities/assert.exception_guard.no_exceptions.pass.cpp
index 2ef3caeefac7052..dc5fb946850acf6 100644
--- a/libcxx/test/libcxx/utilities/assert.exception_guard.no_exceptions.pass.cpp
+++ b/libcxx/test/libcxx/utilities/assert.exception_guard.no_exceptions.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 // ADDITIONAL_COMPILE_FLAGS: -fno-exceptions
 

diff  --git a/libcxx/test/std/algorithms/alg.sorting/alg.clamp/assert.ranges_clamp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.clamp/assert.ranges_clamp.pass.cpp
index 7ae56e34fc60ecd..fdc3d3acc80f2e5 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.clamp/assert.ranges_clamp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.clamp/assert.ranges_clamp.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 // <algorithm>

diff  --git a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/pop.heap/assert.pop_heap.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/pop.heap/assert.pop_heap.pass.cpp
index 2fedc78991d1b93..bb6ff68a4a85a73 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/pop.heap/assert.pop_heap.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/pop.heap/assert.pop_heap.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 // <algorithm>

diff  --git a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/pop.heap/assert.ranges_pop_heap.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/pop.heap/assert.ranges_pop_heap.pass.cpp
index 3f7d60fa5cbb61f..eb540d6c6428ff7 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/pop.heap/assert.ranges_pop_heap.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/pop.heap/assert.ranges_pop_heap.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 // <algorithm>

diff  --git a/libcxx/test/std/utilities/utility/utility.unreachable/assert.unreachable.pass.cpp b/libcxx/test/std/utilities/utility/utility.unreachable/assert.unreachable.pass.cpp
index fffe3636b13e9ae..12c45f83c13c8f2 100644
--- a/libcxx/test/std/utilities/utility/utility.unreachable/assert.unreachable.pass.cpp
+++ b/libcxx/test/std/utilities/utility/utility.unreachable/assert.unreachable.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// UNSUPPORTED: !libcpp-hardening-mode=debug
+// REQUIRES: libcpp-hardening-mode={{safe|debug}}
 // XFAIL: availability-verbose_abort-missing
 
 // Make sure that reaching std::unreachable() with assertions enabled triggers an assertion.

diff  --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index a08e85b67256232..870c8f5b39be32d 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -499,6 +499,24 @@ steps:
           limit: 2
     timeout_in_minutes: 120
 
+  - label: "Safe mode"
+    command: "libcxx/utils/ci/run-buildbot generic-safe-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:

diff  --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index db9efe3bf480ce7..84f9b4a747aa41b 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -400,6 +400,12 @@ generic-hardened-mode)
     check-runtimes
     check-abi-list
 ;;
+generic-safe-mode)
+    clean
+    generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-safe-mode.cmake"
+    check-runtimes
+    check-abi-list
+;;
 generic-debug-mode)
     clean
     generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-debug-mode.cmake"

diff  --git a/libcxx/utils/libcxx/test/params.py b/libcxx/utils/libcxx/test/params.py
index 6a454b0fce765c2..e05c6689964c734 100644
--- a/libcxx/utils/libcxx/test/params.py
+++ b/libcxx/utils/libcxx/test/params.py
@@ -293,15 +293,16 @@ def getModuleFlag(cfg, enable_modules):
     ),
     Parameter(
         name="hardening_mode",
-        choices=["unchecked", "hardened", "debug"],
+        choices=["unchecked", "hardened", "safe", "debug"],
         type=str,
         default="unchecked",
-        help="Whether to enable the hardened mode or the debug mode when compiling the test suite. This is only "
+        help="Whether to enable one of the hardening modes 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_SAFE_MODE=1")     if hardening_mode == "safe" else None,
                 AddCompileFlag("-D_LIBCPP_ENABLE_DEBUG_MODE=1")    if hardening_mode == "debug" else None,
                 AddFeature("libcpp-hardening-mode={}".format(hardening_mode)),
             ],


        


More information about the libcxx-commits mailing list