From edaec04c5e112d0ee5a8f3b919efe54a8b05e8d0 Mon Sep 17 00:00:00 2001 From: Konstantin Varlamov Date: Thu, 11 Jan 2024 11:41:14 -0800 Subject: [PATCH] WIP [libc++][hardening] Overhaul the termination mechanism for hardening This patch: 1. Changes how the program is terminated when a hardening assertion is triggered (from aborting to trapping). 2. Introduces a new mechanism for overriding the default (this can be done via CMake configuration or by defining a macro; link-time overriding is no longer supported). When hitting a hardening assertion in production, we want to stop the program as soon as possible. From a security perspective, trapping is preferable to calling `std::abort`, so use `__builtin_trap` in this case (with the intention of switching to `__builtin_verbose_trap` once that becomes available, see https://discourse.llvm.org/t/rfc-adding-builtin-verbose-trap-string-literal/75845). This also improves the code size because we no longer need to do a function call to `__verbose_abort` that in the general case cannot be inlined. In the debug hardening mode, the security aspect is less important since it's not intended to be run in production. For that reason, keep using `__verbose_abort` in debug mode. --- libcxx/include/CMakeLists.txt | 1 + libcxx/include/__assert | 25 ++++++++++++++++++++++++- libcxx/include/__config_site.in | 1 + libcxx/include/__verbose_trap | 23 +++++++++++++++++++++++ libcxx/include/module.modulemap.in | 4 ++++ libcxx/utils/generate_iwyu_mapping.py | 2 ++ 6 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 libcxx/include/__verbose_trap diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 0fe3ab44d2466..fa5140dea6584 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -865,6 +865,7 @@ set(files __utility/unreachable.h __variant/monostate.h __verbose_abort + __verbose_trap algorithm any array diff --git a/libcxx/include/__assert b/libcxx/include/__assert index d4af7e6c7192a..3885c0ac0fb51 100644 --- a/libcxx/include/__assert +++ b/libcxx/include/__assert @@ -12,17 +12,40 @@ #include <__config> #include <__verbose_abort> +#include <__verbose_trap> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header #endif +// _LIBCPP_ASSERT_IMPL + +#if defined(_LIBCPP_VERBOSE_TRAP_IMPL_OVERRIDE) + +#define _LIBCPP_ASSERT_IMPL(error_message, ...) _LIBCPP_VERBOSE_TRAP_IMPL_OVERRIDE(__VA_ARGS__) + +#elif _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG + +#define _LIBCPP_ASSERT_IMPL(error_message, ...) ((void)error_message, _LIBCPP_VERBOSE_ABORT(__VA_ARGS__)) + +#else + +void __use(const char*, ...); +#define _LIBCPP_ASSERT_IMPL(error_message, ...) (decltype(__use(__VA_ARGS__))(), _LIBCPP_VERBOSE_TRAP(error_message)) + +#endif + +// _LIBCPP_ASSERT + #define _LIBCPP_ASSERT(expression, message) \ (__builtin_expect(static_cast(expression), 1) \ ? (void)0 \ - : _LIBCPP_VERBOSE_ABORT( \ + : _LIBCPP_ASSERT_IMPL( \ + message, \ "%s:%d: assertion %s failed: %s\n", __builtin_FILE(), __builtin_LINE(), #expression, message)) +// _LIBCPP_ASSUME + // TODO: __builtin_assume can currently inhibit optimizations. Until this has been fixed and we can add // assumptions without a clear optimization intent, disable that to avoid worsening the code generation. // See https://discourse.llvm.org/t/llvm-assume-blocks-optimization/71609 for a discussion. diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in index 7c002c5bfcf8e..1227b232dab9a 100644 --- a/libcxx/include/__config_site.in +++ b/libcxx/include/__config_site.in @@ -38,6 +38,7 @@ // Hardening. #cmakedefine _LIBCPP_HARDENING_MODE_DEFAULT @_LIBCPP_HARDENING_MODE_DEFAULT@ +#cmakedefine _LIBCPP_VERBOSE_TRAP_IMPL_OVERRIDE(...) @_LIBCPP_VERBOSE_TRAP_IMPL_OVERRIDE@ // __USE_MINGW_ANSI_STDIO gets redefined on MinGW #ifdef __clang__ diff --git a/libcxx/include/__verbose_trap b/libcxx/include/__verbose_trap new file mode 100644 index 0000000000000..dfb491d712cc2 --- /dev/null +++ b/libcxx/include/__verbose_trap @@ -0,0 +1,23 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___VERBOSE_TRAP +#define _LIBCPP___VERBOSE_TRAP + +#include <__availability> +#include <__config> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +// TODO: use `__builtin_verbose_trap(message) once available +#define _LIBCPP_VERBOSE_TRAP(message) ((void)message, __builtin_trap()) + +#endif // _LIBCPP___VERBOSE_TRAP diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index d10670d4faaff..02f8e4ea20a74 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -630,6 +630,10 @@ module std_private_verbose_abort [system] { header "__verbose_abort" export * } +module std_private_verbose_trap [system] { + header "__verbose_trap" + export * +} module std_private_algorithm_adjacent_find [system] { header "__algorithm/adjacent_find.h" } module std_private_algorithm_all_of [system] { header "__algorithm/all_of.h" } diff --git a/libcxx/utils/generate_iwyu_mapping.py b/libcxx/utils/generate_iwyu_mapping.py index 343538a6cae48..698e434c2ffee 100644 --- a/libcxx/utils/generate_iwyu_mapping.py +++ b/libcxx/utils/generate_iwyu_mapping.py @@ -89,6 +89,8 @@ def generate_map(include): continue elif i == "__verbose_abort": continue + elif i == "__verbose_trap": + continue else: panic(i)