Skip to content

[libc++][hardening] Rework macros for enabling the hardening mode. #70575

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 11 additions & 19 deletions libcxx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ 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 safe debug)
set(LIBCXX_HARDENING_MODE "unchecked" CACHE STRING
set(LIBCXX_SUPPORTED_HARDENING_MODES none fast extensive debug)
set(LIBCXX_HARDENING_MODE "none" 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
Expand Down Expand Up @@ -751,24 +751,16 @@ config_define_if_not(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS _LIBCPP_HAS_N

# TODO(LLVM 19): Produce a deprecation warning.
if (LIBCXX_ENABLE_ASSERTIONS)
set(LIBCXX_HARDENING_MODE "safe")
endif()
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)
set(LIBCXX_HARDENING_MODE "extensive")
endif()
if (LIBCXX_HARDENING_MODE STREQUAL "none")
config_define(2 _LIBCPP_HARDENING_MODE_DEFAULT)
elseif (LIBCXX_HARDENING_MODE STREQUAL "fast")
config_define(4 _LIBCPP_HARDENING_MODE_DEFAULT)
elseif (LIBCXX_HARDENING_MODE STREQUAL "extensive")
config_define(16 _LIBCPP_HARDENING_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)
config_define(8 _LIBCPP_HARDENING_MODE_DEFAULT)
endif()

if (LIBCXX_PSTL_CPU_BACKEND STREQUAL "serial")
Expand Down
1 change: 0 additions & 1 deletion libcxx/cmake/caches/Generic-hardened-mode.cmake

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
set(LIBCXX_ENABLE_STD_MODULES ON CACHE BOOL "") # TODO MODULES Remove when enabled automatically.
set(LIBCXX_HARDENING_MODE "safe" CACHE STRING "")
set(LIBCXX_HARDENING_MODE "extensive" CACHE STRING "")
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
set(LIBCXX_HARDENING_MODE "hardened" CACHE STRING "")
set(LIBCXX_HARDENING_MODE "fast" CACHE STRING "")
set(LIBCXX_ABI_DEFINES "_LIBCPP_ABI_BOUNDED_ITERATORS" CACHE STRING "")
1 change: 1 addition & 0 deletions libcxx/cmake/caches/Generic-hardening-mode-fast.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
set(LIBCXX_HARDENING_MODE "fast" CACHE STRING "")
73 changes: 42 additions & 31 deletions libcxx/docs/Hardening.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,52 +10,63 @@ Hardening Modes
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 two other hardening
modes:
- safe mode;
libc++ provides several hardening modes, where each mode enables a set of
assertions that prevent undefined behavior caused by violating preconditions of
the standard library. Different hardening modes make different trade-offs
between the amount of checking and runtime performance. The available hardening
modes are:
- fast mode;
- extensive mode;
- debug mode.

The safe mode contains all the checks from the hardened mode and additionally
The fast mode contains a set of security-critical checks that can be done with
relatively little overhead in constant time and are intended to be used in
production. We recommend most projects to adopt the fast mode.

The extensive mode contains all the checks from the fast 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.
significant compared to the fast mode, the extensive 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.
The debug mode enables all the available checks in the library, including
internal assertions, some of which might be very expensive. This 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 ``safe`` enables the safe mode, and to
``debug`` enables the debug mode. The default value is ``unchecked`` which
doesn't enable any hardening.
``LIBCXX_HARDENING_MODE`` variable at CMake configuration time with the possible
values of ``none``, ``fast``, ``extensive`` and ``debug``. The default value is
``none`` which doesn't enable any hardening checks (this mode is sometimes
called the ``unchecked`` mode).

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
default. If the mode is set to "unchecked" at the CMake configuration time, the
default. If the mode is set to "none" at the CMake configuration time, the
compiled library will not contain any assertions and the default when building
user code will be to have assertions disabled. As a user, you can consult your
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_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.
control which level of hardening is enabled in their code by defining the macro
``_LIBCPP_HARDENING_MODE`` before including any libc++ headers (preferably by
passing ``-D_LIBCPP_HARDENING_MODE=X`` to the compiler). The macro can be
set to one of the following possible values:

- ``_LIBCPP_HARDENING_MODE_NONE``;
- ``_LIBCPP_HARDENING_MODE_FAST``;
- ``_LIBCPP_HARDENING_MODE_EXTENSIVE``;
- ``_LIBCPP_HARDENING_MODE_DEBUG``.

The exact numeric values of these macros are unspecified and users should not
rely on them (e.g. expect the values to be sorted in any way).

Note that if the compiled library was built by the vendor with the hardening
mode set to "none", 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_HARDENING_MODE``, if any, will usually be respected.

Enabling hardening has no impact on the ABI.

Expand Down
33 changes: 14 additions & 19 deletions libcxx/docs/ReleaseNotes/18.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,14 @@ Improvements and New Features
- ``std::ranges::count`` is now optimized for ``vector<bool>::iterator``, which
can lead up to 350x performance improvements.

- 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 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.
- The library now provides several hardening modes under which common cases of library undefined behavior will be turned
into a reliable program termination. The ``fast`` hardening mode enables a set of security-critical checks with
minimal runtime overhead; the ``extensive`` hardening mode additionally enables relatively cheap checks that catch
common logic errors but aren't necessarily security-critical; and the ``debug`` hardening mode enables all available
checks, some of which might be very expensive. Vendors can configure which hardening mode is enabled by default with
the ``LIBCXX_HARDENING_MODE`` variable at CMake configuration time. Users can control which hardening mode is enabled
on a per translation unit basis using the ``_LIBCPP_HARDENING_MODE`` macro. See :ref:`the hardening documentation
<using-hardening-modes>` for more details.

Deprecations and Removals
-------------------------
Expand All @@ -87,8 +82,8 @@ LLVM 18
~~~~~~~

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

- The headers ``<experimental/deque>``, ``<experimental/forward_list>``, ``<experimental/list>``,
``<experimental/map>``, ``<experimental/memory_resource>``, ``<experimental/regex>``, ``<experimental/set>``,
Expand All @@ -100,10 +95,10 @@ LLVM 19
~~~~~~~

- The ``LIBCXX_ENABLE_ASSERTIONS`` CMake variable that was used to enable the safe mode will be 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 will be deprecated (setting it to ``1`` still enables the safe mode the LLVM 19
release while also issuing a deprecation warning). See :ref:`the hardening documentation <using-hardening-modes>` for
more details.
it will trigger an error; use the ``LIBCXX_HARDENING_MODE`` variable with the value ``extensive`` instead. Similarly,
the ``_LIBCPP_ENABLE_ASSERTIONS`` macro will be deprecated (setting it to ``1`` still enables the extensive mode the
LLVM 19 release while also issuing a deprecation warning). See :ref:`the hardening documentation
<using-hardening-modes>` for more details.

- The base template for ``std::char_traits`` has been marked as deprecated and will be removed in LLVM 19. If you
are using ``std::char_traits`` with types other than ``char``, ``wchar_t``, ``char8_t``, ``char16_t``, ``char32_t``
Expand Down
10 changes: 2 additions & 8 deletions libcxx/docs/UsingLibcxx.rst
Original file line number Diff line number Diff line change
Expand Up @@ -223,14 +223,8 @@ safety annotations.
``std::mutex`` and ``std::lock_guard``. By default, these annotations are
disabled and must be manually enabled by the user.

**_LIBCPP_ENABLE_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-hardening-modes>`.
**_LIBCPP_HARDENING_MODE**:
This macro is used to choose the :ref:`hardening mode <using-hardening-modes>`.

**_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS**:
This macro is used to disable all visibility annotations inside libc++.
Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/__algorithm/comp_ref_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ struct __debug_less

// Pass the comparator by lvalue reference. Or in debug mode, using a
// debugging wrapper that stores a reference.
#if _LIBCPP_ENABLE_DEBUG_MODE
# if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
template <class _Comp>
using __comp_ref_type = __debug_less<_Comp>;
#else
Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/__algorithm/three_way_comp_ref_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +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.
# if _LIBCPP_ENABLE_DEBUG_MODE
# if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
template <class _Comp>
using __three_way_comp_ref_type = __debug_three_way_comp<_Comp>;
# else
Expand Down
Loading