diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt --- a/libcxx/CMakeLists.txt +++ b/libcxx/CMakeLists.txt @@ -67,6 +67,18 @@ By default, this is turned off. Turning it on results in a different ABI (additional symbols but also potentially different layouts of types), and one should not mix code built against a dylib that has debug mode and code built against a regular dylib." OFF) +option(LIBCXX_ENABLE_HARDENED_MODE + "Enable the hardened mode inside the compiled library and make it the default + when compiling user code. Note that users can override this setting in their + own code. Setting this to 'ON' implicitly enables assertions, unless + `LIBCXX_ENABLE_ASSERTIONS` has been explicitly set to 'OFF'. This does not + affect the ABI." OFF) +option(LIBCXX_ENABLE_HARDENED_DEBUG_MODE + "Enable the debug mode inside the compiled library and make it the default + when compiling user code. Note that users can override this setting in their + own code. Setting this to 'ON' implicitly enables assertions, unless + `LIBCXX_ENABLE_ASSERTIONS` has been explicitly set to 'OFF'. This does not + affect the ABI." OFF) 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 @@ -797,6 +809,16 @@ else() config_define(0 _LIBCPP_ENABLE_ASSERTIONS_DEFAULT) endif() +if (LIBCXX_ENABLE_HARDENED_MODE) + config_define(1 _LIBCPP_ENABLE_HARDENED_MODE_DEFAULT) +else() + config_define(0 _LIBCPP_ENABLE_HARDENED_MODE_DEFAULT) +endif() +if (LIBCXX_ENABLE_HARDENED_DEBUG_MODE) + config_define(1 _LIBCPP_ENABLE_HARDENED_DEBUG_MODE_DEFAULT) +else() + config_define(0 _LIBCPP_ENABLE_HARDENED_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-hardened-debug-mode.cmake b/libcxx/cmake/caches/Generic-hardened-debug-mode.cmake new file mode 100644 --- /dev/null +++ b/libcxx/cmake/caches/Generic-hardened-debug-mode.cmake @@ -0,0 +1 @@ +set(LIBCXX_ENABLE_HARDENED_DEBUG_MODE ON CACHE BOOL "") diff --git a/libcxx/cmake/caches/Generic-hardened-mode.cmake b/libcxx/cmake/caches/Generic-hardened-mode.cmake new file mode 100644 --- /dev/null +++ b/libcxx/cmake/caches/Generic-hardened-mode.cmake @@ -0,0 +1 @@ +set(LIBCXX_ENABLE_HARDENED_MODE ON CACHE BOOL "") diff --git a/libcxx/docs/DesignDocs/HardenedMode.rst b/libcxx/docs/DesignDocs/HardenedMode.rst new file mode 100644 --- /dev/null +++ b/libcxx/docs/DesignDocs/HardenedMode.rst @@ -0,0 +1,37 @@ +========== +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 by security-conscious projects. + +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 enable the hardened mode by building the library with the +``LIBCXX_ENABLE_HARDENED_MODE`` option, and similarly enable the debug mode by +building with the ``LIBCXX_ENABLE_HARDENED_DEBUG_MODE`` option. + +The hardened mode requires ``LIBCXX_ENABLE_ASSERTIONS`` to work. If +``LIBCXX_ENABLE_ASSERTIONS`` was not set explicitly, enabling +``LIBCXX_ENABLE_HARDENED_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 --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -71,6 +71,10 @@ 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. +- It is now possible to use CMake to enable the hardened mode by setting the ``LIBCXX_ENABLE_HARDENED_MODE`` variable, + and similarly to enable the debug mode using the ``LIBCXX_ENABLE_HARDENED_DEBUG_MODE`` variable (note that this is the + new debug mode distinct from the legacy debug mode that is being removed in this release). + Deprecations and Removals ------------------------- diff --git a/libcxx/docs/index.rst b/libcxx/docs/index.rst --- a/libcxx/docs/index.rst +++ b/libcxx/docs/index.rst @@ -185,6 +185,7 @@ DesignDocs/ExtendedCXX03Support DesignDocs/FeatureTestMacros DesignDocs/FileTimeType + DesignDocs/HardenedMode DesignDocs/HeaderRemovalPolicy DesignDocs/NoexceptPolicy DesignDocs/PSTLIntegration diff --git a/libcxx/include/__config b/libcxx/include/__config --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -191,6 +191,71 @@ # define _LIBCPP_ABI_BAD_FUNCTION_CALL_KEY_FUNCTION # endif +// 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_HARDENED_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_HARDENED_DEBUG_MODE 1 + +// Available checks: + +// TODO(hardening): add documentation for different 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_HARDENED_DEBUG_MODE +# define _LIBCPP_ENABLE_HARDENED_DEBUG_MODE _LIBCPP_ENABLE_HARDENED_DEBUG_MODE_DEFAULT +# endif +# if _LIBCPP_ENABLE_HARDENED_DEBUG_MODE != 0 && _LIBCPP_ENABLE_HARDENED_DEBUG_MODE != 1 +# error "_LIBCPP_ENABLE_HARDENED_DEBUG_MODE must be set to 0 or 1." +# endif + +# if _LIBCPP_ENABLE_HARDENED_MODE && _LIBCPP_ENABLE_HARDENED_DEBUG_MODE +# error "Only one of _LIBCPP_ENABLE_HARDENED_MODE and _LIBCPP_ENABLE_HARDENED_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_HARDENED_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 --- a/libcxx/include/__config_site.in +++ b/libcxx/include/__config_site.in @@ -35,6 +35,10 @@ #cmakedefine _LIBCPP_PSTL_CPU_BACKEND_SERIAL #cmakedefine _LIBCPP_PSTL_CPU_BACKEND_THREAD +// Hardening. +#cmakedefine01 _LIBCPP_ENABLE_HARDENED_MODE_DEFAULT +#cmakedefine01 _LIBCPP_ENABLE_HARDENED_DEBUG_MODE_DEFAULT + // __USE_MINGW_ANSI_STDIO gets redefined on MinGW #ifdef __clang__ # pragma clang diagnostic push diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml --- a/libcxx/utils/ci/buildkite-pipeline.yml +++ b/libcxx/utils/ci/buildkite-pipeline.yml @@ -486,6 +486,58 @@ 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}" + agents: + queue: "libcxx-builders" + os: "linux" + retry: + automatic: + - exit_status: -1 # Agent was lost + limit: 2 + timeout_in_minutes: 120 + + - label: "Hardened debug mode" + command: "libcxx/utils/ci/run-buildbot generic-hardened-debug-mode" + artifact_paths: + - "**/test-results.xml" + - "**/*.abilist" + env: + CC: "clang-${LLVM_HEAD_VERSION}" + CXX: "clang++-${LLVM_HEAD_VERSION}" + 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: "Debug mode" command: "libcxx/utils/ci/run-buildbot generic-debug-mode" artifact_paths: diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot --- a/libcxx/utils/ci/run-buildbot +++ b/libcxx/utils/ci/run-buildbot @@ -387,6 +387,18 @@ 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-hardened-debug-mode) + clean + generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-hardened-debug-mode.cmake" + check-runtimes + check-abi-list +;; generic-debug-mode) clean generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-debug-mode.cmake" diff --git a/libcxx/utils/libcxx/test/params.py b/libcxx/utils/libcxx/test/params.py --- a/libcxx/utils/libcxx/test/params.py +++ b/libcxx/utils/libcxx/test/params.py @@ -294,6 +294,30 @@ AddFeature("libcpp-has-assertions"), ], ), + Parameter( + name="enable_hardened_mode", + choices=[True, False], + type=bool, + default=False, + help="Whether to enable the hardened mode when compiling the test suite. This is only meaningful when " + "running the tests against libc++.", + actions=lambda enabled: [] if not enabled else [ + AddCompileFlag("-D_LIBCPP_ENABLE_HARDENED_MODE=1"), + AddFeature("libcpp-has-hardened-mode"), + ], + ), + Parameter( + name="enable_hardened_debug_mode", + choices=[True, False], + type=bool, + default=False, + help="Whether to enable the debug mode when compiling the test suite. This is only meaningful when " + "running the tests against libc++.", + actions=lambda enabled: [] if not enabled else [ + AddCompileFlag("-D_LIBCPP_ENABLE_HARDENED_DEBUG_MODE=1"), + AddFeature("libcpp-has-hardened-debug-mode"), + ], + ), Parameter( name="additional_features", type=list,