Skip to content

Commit 64d413e

Browse files
authored
[libc++][hardening] Rework macros for enabling the hardening mode. (#70575)
1. Instead of using individual "boolean" macros, have an "enum" macro `_LIBCPP_HARDENING_MODE`. This avoids issues with macros being mutually exclusive and makes overriding the hardening mode within a TU more straightforward. 2. Rename the safe mode to debug-lite. This brings the code in line with the RFC: https://discourse.llvm.org/t/rfc-hardening-in-libc/73925 Fixes #65101
1 parent b8a0620 commit 64d413e

File tree

149 files changed

+363
-524
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

149 files changed

+363
-524
lines changed

libcxx/CMakeLists.txt

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ option(LIBCXX_ENABLE_FILESYSTEM
5959
available on the platform. This includes things like most parts of <filesystem> and
6060
others like <fstream>" ON)
6161
option(LIBCXX_INCLUDE_TESTS "Build the libc++ tests." ${LLVM_INCLUDE_TESTS})
62-
set(LIBCXX_SUPPORTED_HARDENING_MODES unchecked hardened safe debug)
63-
set(LIBCXX_HARDENING_MODE "unchecked" CACHE STRING
62+
set(LIBCXX_SUPPORTED_HARDENING_MODES none fast extensive debug)
63+
set(LIBCXX_HARDENING_MODE "none" CACHE STRING
6464
"Specify the default hardening mode to use. This mode will be used inside the
6565
compiled library and will be the default when compiling user code. Note that
6666
users can override this setting in their own code. This does not affect the
@@ -751,24 +751,16 @@ config_define_if_not(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS _LIBCPP_HAS_N
751751

752752
# TODO(LLVM 19): Produce a deprecation warning.
753753
if (LIBCXX_ENABLE_ASSERTIONS)
754-
set(LIBCXX_HARDENING_MODE "safe")
755-
endif()
756-
if (LIBCXX_HARDENING_MODE STREQUAL "hardened")
757-
config_define(1 _LIBCPP_ENABLE_HARDENED_MODE_DEFAULT)
758-
config_define(0 _LIBCPP_ENABLE_SAFE_MODE_DEFAULT)
759-
config_define(0 _LIBCPP_ENABLE_DEBUG_MODE_DEFAULT)
760-
elseif (LIBCXX_HARDENING_MODE STREQUAL "safe")
761-
config_define(0 _LIBCPP_ENABLE_HARDENED_MODE_DEFAULT)
762-
config_define(1 _LIBCPP_ENABLE_SAFE_MODE_DEFAULT)
763-
config_define(0 _LIBCPP_ENABLE_DEBUG_MODE_DEFAULT)
754+
set(LIBCXX_HARDENING_MODE "extensive")
755+
endif()
756+
if (LIBCXX_HARDENING_MODE STREQUAL "none")
757+
config_define(2 _LIBCPP_HARDENING_MODE_DEFAULT)
758+
elseif (LIBCXX_HARDENING_MODE STREQUAL "fast")
759+
config_define(4 _LIBCPP_HARDENING_MODE_DEFAULT)
760+
elseif (LIBCXX_HARDENING_MODE STREQUAL "extensive")
761+
config_define(16 _LIBCPP_HARDENING_MODE_DEFAULT)
764762
elseif (LIBCXX_HARDENING_MODE STREQUAL "debug")
765-
config_define(0 _LIBCPP_ENABLE_HARDENED_MODE_DEFAULT)
766-
config_define(0 _LIBCPP_ENABLE_SAFE_MODE_DEFAULT)
767-
config_define(1 _LIBCPP_ENABLE_DEBUG_MODE_DEFAULT)
768-
elseif (LIBCXX_HARDENING_MODE STREQUAL "unchecked")
769-
config_define(0 _LIBCPP_ENABLE_HARDENED_MODE_DEFAULT)
770-
config_define(0 _LIBCPP_ENABLE_SAFE_MODE_DEFAULT)
771-
config_define(0 _LIBCPP_ENABLE_DEBUG_MODE_DEFAULT)
763+
config_define(8 _LIBCPP_HARDENING_MODE_DEFAULT)
772764
endif()
773765

774766
if (LIBCXX_PSTL_CPU_BACKEND STREQUAL "serial")

libcxx/cmake/caches/Generic-hardened-mode.cmake

Lines changed: 0 additions & 1 deletion
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
set(LIBCXX_ENABLE_STD_MODULES ON CACHE BOOL "") # TODO MODULES Remove when enabled automatically.
2-
set(LIBCXX_HARDENING_MODE "safe" CACHE STRING "")
2+
set(LIBCXX_HARDENING_MODE "extensive" CACHE STRING "")
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
set(LIBCXX_HARDENING_MODE "hardened" CACHE STRING "")
1+
set(LIBCXX_HARDENING_MODE "fast" CACHE STRING "")
22
set(LIBCXX_ABI_DEFINES "_LIBCPP_ABI_BOUNDED_ITERATORS" CACHE STRING "")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
set(LIBCXX_HARDENING_MODE "fast" CACHE STRING "")

libcxx/docs/Hardening.rst

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,52 +10,63 @@ Hardening Modes
1010
Using hardening modes
1111
=====================
1212

13-
The hardened mode enables a set of security-critical assertions that prevent
14-
undefined behavior caused by violating preconditions of the standard library.
15-
These assertions can be done with relatively little overhead in constant time
16-
and are intended to be used in production.
17-
18-
In addition to the hardened mode, libc++ also provides two other hardening
19-
modes:
20-
- safe mode;
13+
libc++ provides several hardening modes, where each mode enables a set of
14+
assertions that prevent undefined behavior caused by violating preconditions of
15+
the standard library. Different hardening modes make different trade-offs
16+
between the amount of checking and runtime performance. The available hardening
17+
modes are:
18+
- fast mode;
19+
- extensive mode;
2120
- debug mode.
2221

23-
The safe mode contains all the checks from the hardened mode and additionally
22+
The fast mode contains a set of security-critical checks that can be done with
23+
relatively little overhead in constant time and are intended to be used in
24+
production. We recommend most projects to adopt the fast mode.
25+
26+
The extensive mode contains all the checks from the fast mode and additionally
2427
some checks for undefined behavior that incur relatively little overhead but
2528
aren't security-critical. While the performance penalty is somewhat more
26-
significant compared to the hardened mode, the safe mode is still intended to be
27-
usable in production.
29+
significant compared to the fast mode, the extensive mode is still intended to
30+
be usable in production.
2831

29-
The debug mode, in turn, contains all the checks from the safe mode and
30-
additionally more expensive checks that may affect the complexity of algorithms.
31-
The debug mode is intended to be used for testing, not in production.
32+
The debug mode enables all the available checks in the library, including
33+
internal assertions, some of which might be very expensive. This mode is
34+
intended to be used for testing, not in production.
3235

3336
Vendors can set the default hardening mode by using the
34-
``LIBCXX_HARDENING_MODE`` variable at CMake configuration time. Setting
35-
``LIBCXX_HARDENING_MODE`` to ``hardened`` enables the hardened mode, and
36-
similarly setting the variable to ``safe`` enables the safe mode, and to
37-
``debug`` enables the debug mode. The default value is ``unchecked`` which
38-
doesn't enable any hardening.
37+
``LIBCXX_HARDENING_MODE`` variable at CMake configuration time with the possible
38+
values of ``none``, ``fast``, ``extensive`` and ``debug``. The default value is
39+
``none`` which doesn't enable any hardening checks (this mode is sometimes
40+
called the ``unchecked`` mode).
3941

4042
When hardening is enabled, the compiled library is built with the corresponding
4143
mode enabled, **and** user code will be built with the same mode enabled by
42-
default. If the mode is set to "unchecked" at the CMake configuration time, the
44+
default. If the mode is set to "none" at the CMake configuration time, the
4345
compiled library will not contain any assertions and the default when building
4446
user code will be to have assertions disabled. As a user, you can consult your
4547
vendor to know which level of hardening is enabled by default.
4648

4749
Furthermore, independently of any vendor-selected default, users can always
48-
control which level of hardening is enabled in their code by defining
49-
``_LIBCPP_ENABLE_HARDENED_MODE=0|1`` (or ``_LIBCPP_ENABLE_SAFE_MODE=0|1``, or
50-
``_LIBCPP_ENABLE_DEBUG_MODE=0|1``) before including any libc++ header (we
51-
recommend passing ``-D_LIBCPP_ENABLE_HARDENED_MODE=X``, etc. to the compiler).
52-
Note that if the compiled library was built by the vendor in the unchecked mode,
53-
functions compiled inside the static or shared library won't have any hardening
54-
enabled even if the user compiles with hardening enabled (the same is true for
55-
the inverse case where the static or shared library was compiled **with**
56-
hardening enabled but the user tries to disable it). However, most of the code
57-
in libc++ is in the headers, so the user-selected value for
58-
``_LIBCPP_ENABLE_HARDENED|SAFE|DEBUG_MODE``, if any, will usually be respected.
50+
control which level of hardening is enabled in their code by defining the macro
51+
``_LIBCPP_HARDENING_MODE`` before including any libc++ headers (preferably by
52+
passing ``-D_LIBCPP_HARDENING_MODE=X`` to the compiler). The macro can be
53+
set to one of the following possible values:
54+
55+
- ``_LIBCPP_HARDENING_MODE_NONE``;
56+
- ``_LIBCPP_HARDENING_MODE_FAST``;
57+
- ``_LIBCPP_HARDENING_MODE_EXTENSIVE``;
58+
- ``_LIBCPP_HARDENING_MODE_DEBUG``.
59+
60+
The exact numeric values of these macros are unspecified and users should not
61+
rely on them (e.g. expect the values to be sorted in any way).
62+
63+
Note that if the compiled library was built by the vendor with the hardening
64+
mode set to "none", functions compiled inside the static or shared library won't
65+
have any hardening enabled even if the user compiles with hardening enabled (the
66+
same is true for the inverse case where the static or shared library was
67+
compiled **with** hardening enabled but the user tries to disable it). However,
68+
most of the code in libc++ is in the headers, so the user-selected value for
69+
``_LIBCPP_HARDENING_MODE``, if any, will usually be respected.
5970

6071
Enabling hardening has no impact on the ABI.
6172

libcxx/docs/ReleaseNotes/18.rst

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,14 @@ Improvements and New Features
5656
- ``std::ranges::count`` is now optimized for ``vector<bool>::iterator``, which
5757
can lead up to 350x performance improvements.
5858

59-
- The library now provides a hardened mode under which common cases of library undefined behavior will be turned into
60-
a reliable program termination. Vendors can configure whether the hardened mode is enabled by default with the
61-
``LIBCXX_HARDENING_MODE`` variable at CMake configuration time. Users can control whether the hardened mode is
62-
enabled on a per translation unit basis using the ``-D_LIBCPP_ENABLE_HARDENED_MODE=1`` macro. See
63-
:ref:`the hardening documentation <using-hardening-modes>` for more details.
64-
65-
- The library now provides a debug mode which is a superset of the safe mode, additionally enabling more expensive
66-
checks that are not suitable to be used in production. This replaces the legacy debug mode that was removed in this
67-
release. Unlike the legacy debug mode, this doesn't affect the ABI and doesn't require locking. Vendors can configure
68-
whether the debug mode is enabled by default with the ``LIBCXX_HARDENING_MODE`` variable at CMake configuration time.
69-
Users can control whether the debug mode is enabled on a per translation unit basis using the
70-
``-D_LIBCPP_ENABLE_DEBUG_MODE=1`` macro. See :ref:`the hardening documentation <using-hardening-modes>` for more
71-
details.
59+
- The library now provides several hardening modes under which common cases of library undefined behavior will be turned
60+
into a reliable program termination. The ``fast`` hardening mode enables a set of security-critical checks with
61+
minimal runtime overhead; the ``extensive`` hardening mode additionally enables relatively cheap checks that catch
62+
common logic errors but aren't necessarily security-critical; and the ``debug`` hardening mode enables all available
63+
checks, some of which might be very expensive. Vendors can configure which hardening mode is enabled by default with
64+
the ``LIBCXX_HARDENING_MODE`` variable at CMake configuration time. Users can control which hardening mode is enabled
65+
on a per translation unit basis using the ``_LIBCPP_HARDENING_MODE`` macro. See :ref:`the hardening documentation
66+
<using-hardening-modes>` for more details.
7267

7368
Deprecations and Removals
7469
-------------------------
@@ -88,8 +83,8 @@ LLVM 18
8883
~~~~~~~
8984

9085
- The ``_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED`` macro will not be honored anymore in LLVM 18.
91-
Please see the updated documentation about the safe libc++ mode and in particular the ``_LIBCPP_VERBOSE_ABORT``
92-
macro for details.
86+
Please see the updated documentation about the hardening modes in libc++ and in particular the
87+
``_LIBCPP_VERBOSE_ABORT`` macro for details.
9388

9489
- The headers ``<experimental/deque>``, ``<experimental/forward_list>``, ``<experimental/list>``,
9590
``<experimental/map>``, ``<experimental/memory_resource>``, ``<experimental/regex>``, ``<experimental/set>``,
@@ -101,10 +96,10 @@ LLVM 19
10196
~~~~~~~
10297

10398
- The ``LIBCXX_ENABLE_ASSERTIONS`` CMake variable that was used to enable the safe mode will be deprecated and setting
104-
it will trigger an error; use the ``LIBCXX_HARDENING_MODE`` variable with the value ``safe`` instead. Similarly, the
105-
``_LIBCPP_ENABLE_ASSERTIONS`` macro will be deprecated (setting it to ``1`` still enables the safe mode the LLVM 19
106-
release while also issuing a deprecation warning). See :ref:`the hardening documentation <using-hardening-modes>` for
107-
more details.
99+
it will trigger an error; use the ``LIBCXX_HARDENING_MODE`` variable with the value ``extensive`` instead. Similarly,
100+
the ``_LIBCPP_ENABLE_ASSERTIONS`` macro will be deprecated (setting it to ``1`` still enables the extensive mode the
101+
LLVM 19 release while also issuing a deprecation warning). See :ref:`the hardening documentation
102+
<using-hardening-modes>` for more details.
108103

109104
- The base template for ``std::char_traits`` has been marked as deprecated and will be removed in LLVM 19. If you
110105
are using ``std::char_traits`` with types other than ``char``, ``wchar_t``, ``char8_t``, ``char16_t``, ``char32_t``

libcxx/docs/UsingLibcxx.rst

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -223,14 +223,8 @@ safety annotations.
223223
``std::mutex`` and ``std::lock_guard``. By default, these annotations are
224224
disabled and must be manually enabled by the user.
225225

226-
**_LIBCPP_ENABLE_HARDENED_MODE**:
227-
This macro is used to enable the :ref:`hardened mode <using-hardening-modes>`.
228-
229-
**_LIBCPP_ENABLE_SAFE_MODE**:
230-
This macro is used to enable the :ref:`safe mode <using-hardening-modes>`.
231-
232-
**_LIBCPP_ENABLE_DEBUG_MODE**:
233-
This macro is used to enable the :ref:`debug mode <using-hardening-modes>`.
226+
**_LIBCPP_HARDENING_MODE**:
227+
This macro is used to choose the :ref:`hardening mode <using-hardening-modes>`.
234228

235229
**_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS**:
236230
This macro is used to disable all visibility annotations inside libc++.

libcxx/include/__algorithm/comp_ref_type.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ struct __debug_less
6565

6666
// Pass the comparator by lvalue reference. Or in debug mode, using a
6767
// debugging wrapper that stores a reference.
68-
#if _LIBCPP_ENABLE_DEBUG_MODE
68+
# if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
6969
template <class _Comp>
7070
using __comp_ref_type = __debug_less<_Comp>;
7171
#else

libcxx/include/__algorithm/three_way_comp_ref_type.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ struct __debug_three_way_comp {
5858

5959
// Pass the comparator by lvalue reference. Or in debug mode, using a
6060
// debugging wrapper that stores a reference.
61-
# if _LIBCPP_ENABLE_DEBUG_MODE
61+
# if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
6262
template <class _Comp>
6363
using __three_way_comp_ref_type = __debug_three_way_comp<_Comp>;
6464
# else

0 commit comments

Comments
 (0)