[libcxx-commits] [libcxx] WIP [libc++][hardening] Overhaul the termination mechanism for hardening (PR #77823)

Konstantin Varlamov via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jan 11 12:06:33 PST 2024


https://github.com/var-const created https://github.com/llvm/llvm-project/pull/77823

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.


>From edaec04c5e112d0ee5a8f3b919efe54a8b05e8d0 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconsteq at gmail.com>
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 0fe3ab44d2466e..fa5140dea65842 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 d4af7e6c7192ab..3885c0ac0fb512 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<bool>(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 7c002c5bfcf8e7..1227b232dab9af 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 00000000000000..dfb491d712cc21
--- /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 d10670d4faaffc..02f8e4ea20a740 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 343538a6cae481..698e434c2ffeec 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)
 



More information about the libcxx-commits mailing list