[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