brintos

brintos / llvm-project-archived public Read only

0
0
Text · 21.0 KiB · 1360518 Raw
570 lines · plain
1.. _hardening:2 3===============4Hardening Modes5===============6 7.. contents::8   :local:9 10.. _using-hardening-modes:11 12Using hardening modes13=====================14 15libc++ provides several hardening modes, where each mode enables a set of16assertions that prevent undefined behavior caused by violating preconditions of17the standard library. Different hardening modes make different trade-offs18between the amount of checking and runtime performance. The available hardening19modes are:20 21- **Unchecked mode/none**, which disables all hardening checks.22- **Fast mode**, which contains a set of security-critical checks that can be23  done with relatively little overhead in constant time and are intended to be24  used in production. We recommend most projects adopt this.25- **Extensive mode**, which contains all the checks from fast mode and some26  additional checks for undefined behavior that incur relatively little overhead27  but aren't security-critical. Production builds requiring a broader set of28  checks than fast mode should consider enabling extensive mode. The additional29  rigour impacts performance more than fast mode: we recommend benchmarking to30  determine if that is acceptable for your program.31- **Debug mode**, which enables all the available checks in the library,32  including heuristic checks that might have significant performance overhead as33  well as internal library assertions. This mode should be used in34  non-production environments (such as test suites, CI, or local development).35  We do not commit to a particular level of performance in this mode.36  In particular, this mode is *not* intended to be used in production.37 38.. note::39 40   Enabling hardening has no impact on the ABI.41 42.. _notes-for-users:43 44Notes for users45---------------46 47As a libc++ user, consult with your vendor to determine the level of hardening48enabled by default.49 50Users wishing for a different hardening level to their vendor default are able51to control the level by passing **one** of the following options to the compiler:52 53- ``-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE``54- ``-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST``55- ``-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE``56- ``-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG``57 58.. warning::59 60   The exact numeric values of these macros are unspecified and users should not61   rely on them (e.g. expect the values to be sorted in any way).62 63.. warning::64 65   If you would prefer to override the hardening level on a per-translation-unit66   basis, you must do so **before** including any headers to avoid `ODR issues`_.67 68.. _`ODR issues`: https://en.cppreference.com/w/cpp/language/definition#:~:text=is%20ill%2Dformed.-,One%20Definition%20Rule,-Only%20one%20definition69 70.. note::71 72   Since the static and shared library components of libc++ are built by the73   vendor, setting this macro will have no impact on the hardening mode for the74   pre-built components. Most libc++ code is header-based, so a user-provided75   value for ``_LIBCPP_HARDENING_MODE`` will be mostly respected.76 77In some cases, users might want to override the assertion semantic used by the78library. This can be done similarly to setting the hardening mode; please refer79to the :ref:`relevant section <assertion-semantics>`.80 81Notes for vendors82-----------------83 84Vendors can set the default hardening mode by providing85``LIBCXX_HARDENING_MODE`` as a configuration option, with the possible values of86``none``, ``fast``, ``extensive`` and ``debug``. The default value is ``none``87which doesn't enable any hardening checks (this mode is sometimes called the88``unchecked`` mode).89 90This option controls both the hardening mode that the precompiled library is91built with and the default hardening mode that users will build with. If set to92``none``, the precompiled library will not contain any assertions, and user code93will default to building without assertions.94 95Vendors can also override the way the program is terminated when an assertion96fails by :ref:`providing a custom header <override-assertion-handler>`.97 98Assertion categories99====================100 101Inside the library, individual assertions are grouped into different102*categories*. Each hardening mode enables a different set of assertion103categories; categories provide an additional layer of abstraction that makes it104easier to reason about the high-level semantics of a hardening mode.105 106.. note::107 108  Users are not intended to interact with these categories directly -- the109  categories are considered internal to the library and subject to change.110 111- ``valid-element-access`` -- checks that any attempts to access a container112  element, whether through the container object or through an iterator, are113  valid and do not attempt to go out of bounds or otherwise access114  a non-existent element. This also includes operations that set up an imminent115  invalid access (e.g. incrementing an end iterator). For iterator checks to116  work, bounded iterators must be enabled in the ABI. Types like117  ``std::optional`` and ``std::function`` are considered containers (with at118  most one element) for the purposes of this check.119 120- ``valid-input-range`` -- checks that ranges (whether expressed as an iterator121  pair, an iterator and a sentinel, an iterator and a count, or122  a ``std::range``) given as input to library functions are valid:123  - the sentinel is reachable from the begin iterator;124  - TODO(hardening): both iterators refer to the same container.125 126  ("input" here refers to "an input given to an algorithm", not to an iterator127  category)128 129  Violating assertions in this category leads to an out-of-bounds access.130 131- ``non-null`` -- checks that the pointer being dereferenced is not null. On132  most modern platforms, the zero address does not refer to an actual location133  in memory, so a null pointer dereference would not compromise the memory134  security of a program (however, it is still undefined behavior that can result135  in strange errors due to compiler optimizations).136 137- ``non-overlapping-ranges`` -- for functions that take several ranges as138  arguments, checks that those ranges do not overlap.139 140- ``valid-deallocation`` -- checks that an attempt to deallocate memory is valid141  (e.g. the given object was allocated by the given allocator). Violating this142  category typically results in a memory leak.143 144- ``valid-external-api-call`` -- checks that a call to an external API doesn't145  fail in an unexpected manner. This includes triggering documented cases of146  undefined behavior in an external library (like attempting to unlock an147  unlocked mutex in pthreads). Any API external to the library falls under this148  category (from system calls to compiler intrinsics). We generally don't expect149  these failures to compromise memory safety or otherwise create an immediate150  security issue.151 152- ``compatible-allocator`` -- checks any operations that exchange nodes between153  containers to make sure the containers have compatible allocators.154 155- ``argument-within-domain`` -- checks that the given argument is within the156  domain of valid arguments for the function. Violating this typically produces157  an incorrect result (e.g. ``std::clamp`` returns the original value without158  clamping it due to incorrect functors) or puts an object into an invalid state159  (e.g. a string view where only a subset of elements is accessible). This160  category is for assertions violating which doesn't cause any immediate issues161  in the library -- whatever the consequences are, they will happen in the user162  code.163 164- ``pedantic`` -- checks preconditions that are imposed by the C++ standard,165  but violating which happens to be benign in libc++.166 167- ``semantic-requirement`` -- checks that the given argument satisfies the168  semantic requirements imposed by the C++ standard. Typically, there is no169  simple way to completely prove that a semantic requirement is satisfied;170  thus, this would often be a heuristic check and it might be quite expensive.171 172- ``internal`` -- checks that internal invariants of the library hold. These173  assertions don't depend on user input.174 175- ``uncategorized`` -- for assertions that haven't been properly classified yet.176  This category is an escape hatch used for some existing assertions in the177  library; all new code should have its assertions properly classified.178 179Mapping between the hardening modes and the assertion categories180================================================================181 182.. list-table::183    :header-rows: 1184    :widths: auto185 186    * - Category name187      - ``fast``188      - ``extensive``189      - ``debug``190    * - ``valid-element-access``191      - ✅192      - ✅193      - ✅194    * - ``valid-input-range``195      - ✅196      - ✅197      - ✅198    * - ``non-null``199      - ❌200      - ✅201      - ✅202    * - ``non-overlapping-ranges``203      - ❌204      - ✅205      - ✅206    * - ``valid-deallocation``207      - ❌208      - ✅209      - ✅210    * - ``valid-external-api-call``211      - ❌212      - ✅213      - ✅214    * - ``compatible-allocator``215      - ❌216      - ✅217      - ✅218    * - ``argument-within-domain``219      - ❌220      - ✅221      - ✅222    * - ``pedantic``223      - ❌224      - ✅225      - ✅226    * - ``semantic-requirement``227      - ❌228      - ❌229      - ✅230    * - ``internal``231      - ❌232      - ❌233      - ✅234    * - ``uncategorized``235      - ❌236      - ✅237      - ✅238 239.. note::240 241  At the moment, each subsequent hardening mode is a strict superset of the242  previous one (in other words, each subsequent mode only enables additional243  assertion categories without disabling any), but this won't necessarily be244  true for any hardening modes that might be added in the future.245 246.. note::247 248  The categories enabled by each mode are subject to change. Users should not249  rely on the precise assertions enabled by a mode at a given point in time.250  However, the library does guarantee to keep the hardening modes stable and251  to fulfill the semantics documented here.252 253Hardening assertion failure254===========================255 256In production modes (``fast`` and ``extensive``), a hardening assertion failure257immediately ``_traps <https://clang.llvm.org/docs/LanguageExtensions.html#builtin-verbose-trap>``258the program. This is the safest approach that also minimizes the code size259penalty as the failure handler maps to a single instruction. The downside is260that the failure provides no additional details other than the stack trace261(which might also be affected by optimizations).262 263In the ``debug`` mode, an assertion failure terminates the program in an264unspecified manner and also outputs the associated error message to the error265output. This is less secure and increases the size of the binary (among other266things, it has to store the error message strings) but makes the failure easier267to debug. It also allows testing the error messages in our test suite.268 269This default behavior can be customized by users via :ref:`assertion semantics270<assertion-semantics>`; it can also be completely overridden by vendors by271providing a :ref:`custom assertion failure handler272<override-assertion-handler>`.273 274.. _assertion-semantics:275 276Assertion semantics277-------------------278 279.. warning::280 281  Assertion semantics are currently an experimental feature.282 283.. note::284 285  Assertion semantics are not available in the C++03 mode.286 287What happens when an assertion fails depends on the assertion semantic being288used. Four assertion semantics are available, based on C++26 Contracts289evaluation semantics:290 291- ``ignore`` evaluates the assertion but has no effect if it fails (note that it292  differs from the Contracts ``ignore`` semantic which would not evaluate293  the assertion at all);294- ``observe`` logs an error (indicating, if possible on the platform, that the295  error is fatal) but continues execution;296- ``quick-enforce`` terminates the program as fast as possible via a trap297  instruction. It is the default semantic for the production modes (``fast`` and298  ``extensive``);299- ``enforce`` logs an error and then terminates the program. It is the default300  semantic for the ``debug`` mode.301 302Notes:303 304- Continuing execution after a hardening check fails results in undefined305  behavior; the ``observe`` semantic is meant to make adopting hardening easier306  but should not be used outside of the adoption period;307- C++26 wording for Library Hardening precludes a conforming Hardened308  implementation from using the Contracts ``ignore`` semantic when evaluating309  hardened preconditions in the Library. Libc++ allows using this semantic for310  hardened preconditions, but please be aware that using ``ignore`` does not311  produce a conforming "Hardened" implementation, unlike the other semantics312  above.313 314The default assertion semantics are as follows:315 316- ``fast``: ``quick-enforce``;317- ``extensive``: ``quick-enforce``;318- ``debug``: ``enforce``.319 320The default assertion semantics can be overridden by passing **one** of the321following options to the compiler:322 323- ``-D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_IGNORE``324- ``-D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_OBSERVE``325- ``-D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE``326- ``-D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_ENFORCE``327 328All the :ref:`same notes <notes-for-users>` apply to setting this macro as for329setting ``_LIBCPP_HARDENING_MODE``.330 331Notes for vendors332-----------------333 334Similarly to hardening modes, vendors can set the default assertion semantic by335providing ``LIBCXX_ASSERTION_SEMANTIC`` as a configuration option, with the336possible values of ``hardening_dependent``, ``ignore``, ``observe``,337``quick_enforce`` and ``enforce``. The default value is ``hardening_dependent``338which is a special value that instructs the library to select the semantic based339on the hardening mode in effect (the mapping is described in340:ref:`the main section on assertion semantics <assertion-semantics>`).341 342This option controls both the assertion semantic that the precompiled library is343built with and the default assertion semantic that users will build with.344 345.. _override-assertion-handler:346 347Overriding the assertion failure handler348----------------------------------------349 350Vendors can override the default assertion handler mechanism by following these351steps:352 353- create a header file that provides a definition of a macro called354  ``_LIBCPP_ASSERTION_HANDLER``. The macro will be invoked when a hardening355  assertion fails, with a single parameter containing a null-terminated string356  with the error message.357- when configuring the library, provide the path to custom header (relative to358  the root of the repository) via the CMake variable359  ``LIBCXX_ASSERTION_HANDLER_FILE``.360 361Note that almost all libc++ headers include the assertion handler header which362means it should not include anything non-trivial from the standard library to363avoid creating circular dependencies.364 365There is no existing mechanism for users to override the assertion handler366because the ability to do the override other than at configure-time carries an367unavoidable code size penalty that would otherwise be imposed on all users,368whether they require such customization or not. Instead, we let vendors decide369what's right on their platform for their users -- a vendor who wishes to provide370this capability is free to do so, e.g. by declaring the assertion handler as an371overridable function.372 373ABI374===375 376Setting a hardening mode does **not** affect the ABI. Each mode uses the subset377of checks available in the current ABI configuration which is determined by the378platform.379 380It is important to stress that whether a particular check is enabled depends on381the combination of the selected hardening mode and the hardening-related ABI382options. Some checks require changing the ABI from the "default" to store383additional information in the library classes -- e.g. checking whether an384iterator is valid upon dereference generally requires storing data about bounds385inside the iterator object. Using ``std::span`` as an example, setting the386hardening mode to ``fast`` will always enable the ``valid-element-access``387checks when accessing elements via a ``std::span`` object, but whether388dereferencing a ``std::span`` iterator does the equivalent check depends on the389ABI configuration.390 391ABI options392-----------393 394Vendors can use some ABI options at CMake configuration time (when building libc++395itself) to enable additional hardening checks. This is done by passing these396macros as ``-DLIBCXX_ABI_DEFINES="_LIBCPP_ABI_FOO;_LIBCPP_ABI_BAR;etc"`` at397CMake configuration time. The available options are:398 399- ``_LIBCPP_ABI_BOUNDED_ITERATORS`` -- changes the iterator type of select400  containers (see below) to a bounded iterator that keeps track of whether it's401  within the bounds of the original container and asserts valid bounds on every402  dereference.403 404  ABI impact: changes the iterator type of the relevant containers.405 406  Supported containers:407 408  - ``span``;409  - ``string_view``.410 411- ``_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING`` -- changes the iterator type of412  ``basic_string`` to a bounded iterator that keeps track of whether it's within413  the bounds of the original container and asserts it on every dereference and414  when performing iterator arithmetics.415 416  ABI impact: changes the iterator type of ``basic_string`` and its417  specializations, such as ``string`` and ``wstring``.418 419- ``_LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR`` -- changes the iterator type of420  ``vector`` to a bounded iterator that keeps track of whether it's within the421  bounds of the original container and asserts it on every dereference and when422  performing iterator arithmetics. Note: this doesn't yet affect423  ``vector<bool>``.424 425  ABI impact: changes the iterator type of ``vector`` (except ``vector<bool>``).426 427- ``_LIBCPP_ABI_BOUNDED_UNIQUE_PTR`` -- tracks the bounds of the array stored inside428  a ``std::unique_ptr<T[]>``, allowing it to trap when accessed out-of-bounds. This429  requires the ``std::unique_ptr`` to be created using an API like ``std::make_unique``430  or ``std::make_unique_for_overwrite``, otherwise the bounds information is not available431  to the library.432 433  ABI impact: changes the layout of ``std::unique_ptr<T[]>``, and the representation434              of a few library types that use ``std::unique_ptr`` internally, such as435              the unordered containers.436 437- ``_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY`` -- changes the iterator type of ``std::array`` to a438  bounded iterator that keeps track of whether it's within the bounds of the container and asserts it439  on every dereference and when performing iterator arithmetic.440 441  ABI impact: changes the iterator type of ``std::array``, its size and its layout.442 443ABI tags444--------445 446We use ABI tags to allow translation units built with different hardening modes447to interact with each other without causing ODR violations. Knowing how448hardening modes are encoded into the ABI tags might be useful to examine449a binary and determine whether it was built with hardening enabled.450 451.. warning::452  We don't commit to the encoding scheme used by the ABI tags being stable453  between different releases of libc++. The tags themselves are never stable, by454  design -- new releases increase the version number. The following describes455  the state of the latest release and is for informational purposes only.456 457The first character of an ABI tag encodes the hardening mode:458 459- ``f`` -- [f]ast mode;460- ``s`` -- extensive ("[s]afe") mode;461- ``d`` -- [d]ebug mode;462- ``n`` -- [n]one mode.463 464The second character of an ABI tag encodes the assertion semantic:465 466- ``i`` -- [i]gnore semantic;467- ``o`` -- [o]bserve semantic;468- ``q`` -- [q]uick-enforce semantic;469- ``e`` -- [e]nforce semantic.470 471Hardened containers status472==========================473 474.. list-table::475    :header-rows: 1476    :widths: auto477 478    * - Name479      - Member functions480      - Iterators (ABI-dependent)481    * - ``span``482      - ✅483      - ✅484    * - ``string_view``485      - ✅486      - ✅487    * - ``array``488      - ✅489      - ❌490    * - ``vector``491      - ✅492      - ✅ (see note)493    * - ``string``494      - ✅495      - ✅ (see note)496    * - ``list``497      - ✅498      - ❌499    * - ``forward_list``500      - ✅501      - ❌502    * - ``deque``503      - ✅504      - ❌505    * - ``map``506      - ❌507      - ❌508    * - ``set``509      - ❌510      - ❌511    * - ``multimap``512      - ❌513      - ❌514    * - ``multiset``515      - ❌516      - ❌517    * - ``unordered_map``518      - Partial519      - Partial520    * - ``unordered_set``521      - Partial522      - Partial523    * - ``unordered_multimap``524      - Partial525      - Partial526    * - ``unordered_multiset``527      - Partial528      - Partial529    * - ``mdspan``530      - ✅531      - ❌532    * - ``optional``533      - ✅534      - N/A535    * - ``function``536      - ❌537      - N/A538    * - ``variant``539      - N/A540      - N/A541    * - ``any``542      - N/A543      - N/A544    * - ``expected``545      - ✅546      - N/A547    * - ``valarray``548      - Partial549      - N/A550    * - ``bitset``551      - ✅552      - N/A553 554Note: for ``vector`` and ``string``, the iterator does not check for555invalidation (accesses made via an invalidated iterator still lead to undefined556behavior)557 558Note: ``vector<bool>`` iterator is not currently hardened.559 560Testing561=======562 563Please see :ref:`Testing documentation <testing-hardening-assertions>`.564 565Further reading566===============567 568- `Hardening RFC <https://discourse.llvm.org/t/rfc-hardening-in-libc/73925>`_:569  contains some of the design rationale.570