Skip to content

[libc++][hardening] Introduce assertion semantics #148268

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

Conversation

var-const
Copy link
Member

@var-const var-const commented Jul 11, 2025

Assertion semantics closely mimic C++26 Contracts evaluation semantics. This brings our implementation closer in line with C++26 Library Hardening (one particular benefit is that using the observe semantic makes adopting hardening easier for projects).

Copy link

github-actions bot commented Jul 11, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@var-const var-const changed the title varconst/hardening semantics introduce [libc++][hardening] Introduce assertion semantics Jul 13, 2025
@var-const var-const marked this pull request as ready for review July 13, 2025 03:07
@var-const var-const requested review from a team as code owners July 13, 2025 03:07
@var-const var-const added libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. hardening Issues related to the hardening effort labels Jul 13, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 13, 2025

@llvm/pr-subscribers-github-workflow
@llvm/pr-subscribers-libcxxabi

@llvm/pr-subscribers-libcxx

Author: Konstantin Varlamov (var-const)

Changes
  • [libc++][hardening][NFC] Introduce _LIBCPP_VERBOSE_TRAP macro.
  • [libc++][hardening] Introduce a dylib function to log hardening errors.
  • [libc++][hardening] Introduce assertion semantics.

Patch is 78.09 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/148268.diff

38 Files Affected:

  • (modified) libcxx/include/CMakeLists.txt (+3)
  • (modified) libcxx/include/__config (+34)
  • (modified) libcxx/include/__configuration/availability.h (+5)
  • (added) libcxx/include/__cxx03/__verbose_trap (+36)
  • (added) libcxx/include/__log_error (+50)
  • (added) libcxx/include/__verbose_trap (+36)
  • (modified) libcxx/include/module.modulemap.in (+6)
  • (modified) libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist (+3-2)
  • (modified) libcxx/lib/abi/i686-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist (+3-2)
  • (modified) libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist (+3-2)
  • (modified) libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist (+3-2)
  • (modified) libcxx/lib/abi/x86_64-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist (+3-2)
  • (modified) libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist (+3-2)
  • (modified) libcxx/src/CMakeLists.txt (+1)
  • (added) libcxx/src/log_error.cpp (+61)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_array.pass.cpp (+15-6)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_integral.pass.cpp (+3-3)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.conversion.pass.cpp (+2-2)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.extents.pass.cpp (+4-1)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.layout_right.pass.cpp (+2-2)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.conversion.pass.cpp (+3-3)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.ctor.extents.pass.cpp (+2-1)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.ctor.layout_left.pass.cpp (+2-2)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.conversion.pass.cpp (+10-8)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.non_unique.pass.cpp (+2-2)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.pass.cpp (+4-4)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_span.non_unique.pass.cpp (+2-2)
  • (modified) libcxx/test/libcxx/thread/thread.latch/assert.ctor.pass.cpp (+5-4)
  • (modified) libcxx/test/libcxx/thread/thread.semaphore/assert.ctor.pass.cpp (+1-1)
  • (modified) libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.ambig/assert.ctor.pass.cpp (+2)
  • (modified) libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.nonexist/assert.ctor.pass.cpp (+2)
  • (modified) libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_local.pass.cpp (+1)
  • (modified) libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys.pass.cpp (+2)
  • (modified) libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys_choose.pass.cpp (+2)
  • (modified) libcxx/test/support/check_assertion.h (+113-18)
  • (modified) libcxx/utils/libcxx/test/features.py (+8)
  • (modified) libcxx/vendor/llvm/default_assertion_handler.in (+19-15)
  • (modified) libcxxabi/src/demangle/DemangleConfig.h (+7)
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index c8e6d28584623..7052716bbb6f0 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -535,6 +535,7 @@ set(files
   __locale_dir/time.h
   __locale_dir/wbuffer_convert.h
   __locale_dir/wstring_convert.h
+  __log_error
   __math/abs.h
   __math/copysign.h
   __math/error_functions.h
@@ -944,6 +945,7 @@ set(files
   __vector/vector_bool.h
   __vector/vector_bool_formatter.h
   __verbose_abort
+  __verbose_trap
   algorithm
   any
   array
@@ -1569,6 +1571,7 @@ set(files
   __cxx03/__utility/unreachable.h
   __cxx03/__variant/monostate.h
   __cxx03/__verbose_abort
+  __cxx03/__verbose_trap
   __cxx03/algorithm
   __cxx03/array
   __cxx03/atomic
diff --git a/libcxx/include/__config b/libcxx/include/__config
index d940461c30234..ef0c8b48b658b 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -147,6 +147,40 @@ _LIBCPP_HARDENING_MODE_EXTENSIVE, \
 _LIBCPP_HARDENING_MODE_DEBUG
 #  endif
 
+// Hardening assertion semantics generally mirror the evaluation semantics of C++26 Contracts:
+// - `ignore` evaluates the assertion but doesn't do anything if it fails (note that it differs from the Contracts
+//   `ignore` semantic which wouldn't evaluate the assertion at all);
+// - `observe` logs an error (indicating, if possible, that the error is fatal) and continues execution;
+// - `quick-enforce` terminates the program as fast as possible (via trapping);
+// - `enforce` logs an error and then terminates the program.
+//
+// Notes:
+// - Continuing execution after a hardening check fails results in undefined behavior; the `observe` semantic is meant
+//   to make adopting hardening easier but should not be used outside of this scenario;
+// - C++26 wording for Library Hardening precludes a conforming Hardened implementation from using the Contracts
+//   `ignore` semantic when evaluating hardened preconditions in the Library. Libc++ allows using this semantic for
+//   hardened preconditions, however, be aware that using `ignore` does not produce a conforming "Hardened"
+//   implementation, unlike the other semantics above.
+// clang-format off
+#  define _LIBCPP_ASSERTION_SEMANTIC_IGNORE        (1 << 1)
+#  define _LIBCPP_ASSERTION_SEMANTIC_OBSERVE       (1 << 2)
+#  define _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE (1 << 3)
+#  define _LIBCPP_ASSERTION_SEMANTIC_ENFORCE       (1 << 4)
+// clang-format on
+
+// Allow users to define an arbitrary assertion semantic; otherwise, use the default mapping from modes to semantics.
+// The default is for production-capable modes to use `quick-enforce` (i.e., trap) and for the `debug` mode to use
+// `enforce` (i.e., log and abort).
+#  ifndef _LIBCPP_ASSERTION_SEMANTIC
+
+#    if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+#      define _LIBCPP_ASSERTION_SEMANTIC _LIBCPP_ASSERTION_SEMANTIC_ENFORCE
+#    else
+#      define _LIBCPP_ASSERTION_SEMANTIC _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE
+#    endif
+
+#  endif // _LIBCPP_ASSERTION_SEMANTIC
+
 // } HARDENING
 
 #  define _LIBCPP_TOSTRING2(x) #x
diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index ae58e36b508b4..5de0b98ba22a9 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -304,6 +304,11 @@
 #define _LIBCPP_AVAILABILITY_HAS_VERBOSE_ABORT _LIBCPP_INTRODUCED_IN_LLVM_15
 #define _LIBCPP_AVAILABILITY_VERBOSE_ABORT _LIBCPP_INTRODUCED_IN_LLVM_15_ATTRIBUTE
 
+// This controls whether the library provides a function to log errors without terminating the program (used in
+// particular by the `observe` assertion semantic).
+#define _LIBCPP_AVAILABILITY_HAS_LOG_ERROR _LIBCPP_INTRODUCED_IN_LLVM_21
+#define _LIBCPP_AVAILABILITY_LOG_ERROR _LIBCPP_INTRODUCED_IN_LLVM_21_ATTRIBUTE
+
 // This controls the availability of the C++17 std::pmr library,
 // which is implemented in large part in the built library.
 //
diff --git a/libcxx/include/__cxx03/__verbose_trap b/libcxx/include/__cxx03/__verbose_trap
new file mode 100644
index 0000000000000..755124b97a5ac
--- /dev/null
+++ b/libcxx/include/__cxx03/__verbose_trap
@@ -0,0 +1,36 @@
+// -*- 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___CXX03___VERBOSE_TRAP
+#define _LIBCPP___CXX03___VERBOSE_TRAP
+
+#include <__cxx03/__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if __has_builtin(__builtin_verbose_trap)
+// AppleClang shipped a slightly different version of __builtin_verbose_trap from the upstream
+// version before upstream Clang actually got the builtin.
+// TODO: Remove once AppleClang supports the two-arguments version of the builtin.
+#  if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1700
+#    define _LIBCPP_VERBOSE_TRAP(message) __builtin_verbose_trap(message)
+#  else
+#    define _LIBCPP_VERBOSE_TRAP(message) __builtin_verbose_trap("libc++", message)
+#  endif
+#else
+#  define _LIBCPP_VERBOSE_TRAP(message) ((void)message, __builtin_trap())
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___CXX03___VERBOSE_TRAP
diff --git a/libcxx/include/__log_error b/libcxx/include/__log_error
new file mode 100644
index 0000000000000..9f308898247c4
--- /dev/null
+++ b/libcxx/include/__log_error
@@ -0,0 +1,50 @@
+// -*- 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___LOG_ERROR
+#define _LIBCPP___LOG_ERROR
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+enum class _LogErrorReason {
+  // Where possible, it logs in a way that indicates a fatal error (which might include capturing the stack trace).
+  _HardeningFailure
+};
+
+// This function should never be called directly from the code -- it should only be called through the
+// `_LIBCPP_LOG_ERROR` macro.
+_LIBCPP_AVAILABILITY_LOG_ERROR _LIBCPP_EXPORTED_FROM_ABI void
+__log_error(_LogErrorReason __reason, const char* __message) _NOEXCEPT;
+
+// _LIBCPP_LOG_ERROR(message)
+//
+// This macro is used to log an error without terminating the program (as is the case for hardening failures if the
+// `observe` assertion semantic is used, for example).
+
+#if !defined(_LIBCPP_LOG_ERROR)
+
+#  if !_LIBCPP_AVAILABILITY_HAS_LOG_ERROR
+// The decltype is there to suppress -Wunused warnings in this configuration.
+void __use(const char*);
+#    define _LIBCPP_LOG_ERROR(__message) (decltype(::std::__use(__message))())
+#  else
+#    define _LIBCPP_LOG_ERROR(__reason, __message) ::std::__log_error(__reason, __message)
+#  endif
+
+#endif // !defined(_LIBCPP_LOG_ERROR)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___LOG_ERROR
diff --git a/libcxx/include/__verbose_trap b/libcxx/include/__verbose_trap
new file mode 100644
index 0000000000000..13ea727738c3b
--- /dev/null
+++ b/libcxx/include/__verbose_trap
@@ -0,0 +1,36 @@
+// -*- 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 <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if __has_builtin(__builtin_verbose_trap)
+// AppleClang shipped a slightly different version of __builtin_verbose_trap from the upstream
+// version before upstream Clang actually got the builtin.
+// TODO: Remove once AppleClang supports the two-arguments version of the builtin.
+#  if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1700
+#    define _LIBCPP_VERBOSE_TRAP(message) __builtin_verbose_trap(message)
+#  else
+#    define _LIBCPP_VERBOSE_TRAP(message) __builtin_verbose_trap("libc++", message)
+#  endif
+#else
+#  define _LIBCPP_VERBOSE_TRAP(message) ((void)message, __builtin_trap())
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___VERBOSE_TRAP
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 45b9c72a05b82..8d29bded96169 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2353,9 +2353,15 @@ module std [system] {
     header "__std_mbstate_t.h"
     export *
   }
+  module log_error {
+    header "__log_error"
+  }
   module verbose_abort {
     header "__verbose_abort"
   }
+  module verbose_trap {
+    header "__verbose_trap"
+  }
   module internal_assert {
     header "__assert"
     export *
diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 162757c7e37ec..bc6bdca5b8a2d 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -534,6 +534,7 @@
 {'is_defined': True, 'name': '__ZNKSt3__115basic_streambufIwNS_11char_traitsIwEEE6getlocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE3strEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__115error_condition7messageEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__117bad_function_call4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__117moneypunct_bynameIcLb0EE11do_groupingEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__117moneypunct_bynameIcLb0EE13do_neg_formatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__117moneypunct_bynameIcLb0EE13do_pos_formatEv', 'type': 'FUNC'}
@@ -944,6 +945,7 @@
 {'is_defined': True, 'name': '__ZNSt3__110to_wstringEx', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__110to_wstringEy', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__111__call_onceERVmPvPFvS2_E', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__111__log_errorENS_15_LogErrorReasonEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__111__money_getIcE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_SF_Ri', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__111__money_getIwE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERwS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS9_IwNSA_IwEENSC_IwEEEESJ_SJ_Ri', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__111__money_putIcE13__gather_infoEbbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_Ri', 'type': 'FUNC'}
@@ -1125,6 +1127,7 @@
 {'is_defined': True, 'name': '__ZNSt3__112system_errorD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112system_errorD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112system_errorD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__113__hash_memoryEPKvm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE11__read_modeEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE12__write_modeEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4openEPKcj', 'type': 'FUNC'}
@@ -1305,7 +1308,6 @@
 {'is_defined': True, 'name': '__ZNSt3__113shared_futureIvED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__113shared_futureIvED2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__113shared_futureIvEaSERKS1_', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__113__hash_memoryEPKvm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__114__num_get_base10__get_baseERNS_8ios_baseE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__114__num_get_base5__srcE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__114__num_put_base12__format_intEPcPKcbj', 'type': 'FUNC'}
@@ -1508,7 +1510,6 @@
 {'is_defined': True, 'name': '__ZNSt3__117bad_function_callD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117bad_function_callD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117bad_function_callD2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNKSt3__117bad_function_call4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117iostream_categoryEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIcLb0EE4initEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/i686-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
index 4b6f3548ce495..6211fa7e181ee 100644
--- a/libcxx/lib/abi/i686-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -170,6 +170,7 @@
 {'is_defined': True, 'name': '_ZNKSt6__ndk115basic_streambufIwNS_11char_traitsIwEEE6getlocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE3strEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk115error_condition7messageEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt6__ndk117bad_function_call4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk117moneypunct_bynameIcLb0EE11do_groupingEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk117moneypunct_bynameIcLb0EE13do_neg_formatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk117moneypunct_bynameIcLb0EE13do_pos_formatEv', 'type': 'FUNC'}
@@ -580,6 +581,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk110to_wstringEx', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk110to_wstringEy', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk111__call_onceERVmPvPFvS2_E', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk111__log_errorENS_15_LogErrorReasonEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk111__money_getIcE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_SF_Ri', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk111__money_getIwE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERwS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS9_IwNSA_IwEENSC_IwEEEESJ_SJ_Ri', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk111__money_putIcE13__gather_infoEbbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_Ri', 'type': 'FUNC'}
@@ -761,6 +763,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk112system_errorD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk112system_errorD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk112system_errorD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk113__hash_memoryEPKvj', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEE11__read_modeEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEE12__write_modeEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEE4openEPKcj', 'type': 'FUNC'}
@@ -941,7 +944,6 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk113shared_futureIvED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk113shared_futureIvED2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk113shared_futureIvEaSERKS1_', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk113__hash_memoryEPKvj', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk114__num_get_base10__get_baseERNS_8ios_baseE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk114__num_get_base5__srcE', 'size': 33, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk114__num_put_base12__format_intEPcPKcbj', 'type': 'FUNC'}
@@ -1144,7 +1146,6 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk117bad_function_callD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk117bad_function_callD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk117bad_function_callD2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt6__ndk117bad_function_call4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk117iostream_categoryEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk117moneypunct_bynameIcLb0EE4initEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 2b85596bd87f6..b788ad3631211 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -96,6 +96,7 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNKSt3__115__codecvt_utf16IwLb1EE6do_outERPcPKwS5_RS5_S2_S2_S3_', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNKSt3__115__codecvt_utf16IwLb1EE9do_lengthERPcPKcS5_m', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNKSt3__115error_condition7messageEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNKSt3__117bad_function_call4whatEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNKSt3__118__time_get_storageIcE15__do_date_orderEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNKSt3__118__time_get_storageIwE15__do_date_orderEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNKSt3__119__shared_weak_count13__get_deleterERKSt9type_info', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
@@ -311,6 +312,7 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__110to_wstringEx', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__110to_wstringEy', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__111__call_onceERVmPvPFvS2_E', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__111__log_errorENS_15_LogErrorReasonEPKc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__111regex_errorC1ENS_15regex_constants10error_typeE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__111regex_errorC2ENS_15regex_constants10error_typeE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__111regex_errorD0Ev', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
@@ -410,6 +412,7 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__112system_errorD0Ev', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__112system_errorD1Ev', 'storage_mapping_cla...
[truncated]

#endif

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't very pretty, but I can't think of a better way, assuming we don't want to add new functionality to the frozen headers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks reasonable to me.

@var-const var-const force-pushed the varconst/hardening-semantics-introduce branch from ba3053d to 2ad957b Compare July 14, 2025 18:22

.. _assertion-semantics:

Assertion semantics
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General comment: we need to mention (or at least plan for) what happens when C++26 Contracts are implemented. In particular, there will be a time when Clang and libc++ will implement Contracts and the Contracts runtime (class std::contract_violation, etc). Furthermore, we'll want libc++ to be a valid C++26 Hardened Implementation, which requires that hardening failures be treated as contract violations.

When that's the case, what should happen with _LIBCPP_ASSERTION_SEMANTIC?

Copy link
Member Author

@var-const var-const Jul 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think our design is constrained by the fact that we want to support hardening in older language modes where Contracts would not be available even when using a new compiler (after all, a major motivation for hardening is making existing code bases more secure). Support for the observe semantic is important for adopting hardening in production, so I think it would be a significant limitation if this semantic were only available for projects that can use very recent compiler versions. It makes having some form of assertion semantics unavoidable.

I also think it makes sense to have assertion semantics mimic the actual Contracts semantics -- it's simple(r) conceptually and might make migrating to Contracts easier. We could have e.g. a boolean flag ("observe"/"not observe") instead but I think it would be creating unnecessary differences without buying us much.

If this is the case, then we cannot escape the situation where libc++ assertion semantics and actual Contracts evaluation semantics "clash" and need to choose the best (in the sense of "lesser of several evils") alternative. Summarizing our offline discussion, I think when a user sets the assertion semantic in an environment where Contract semantics are available, we can:

  • A. Make this an error.
  • B. Allow the Contracts semantic to silently take precedence (effectively override) the assertion semantic.
  • C. Do either A or B based on whether the assertion semantic matches the Contract semantic (if they match, setting the assertion semantic is a no-op, if they don't match, produce an error). While it's not impossible for a project to use different semantics based on the language mode, it seems very unlikely in practice.

(C) seems like the better option -- (A) would make it painful for projects to support more than one language mode, and (B) might allow silently downgrading the semantic to a non-terminating one, for example. IIUC, the evaluation semantic can be chosen at runtime, making it hard or impossible to catch mismatch, but these cases should be relatively rare, and I think (C) should generally cover this.

This also means that we would want to support assertion semantics long term, having support for Contracts doesn't automatically deprecate them.

#endif

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks reasonable to me.

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM after rebasing onto main.

Assertion semantics closely mimic C++26 Contracts evaluation semantics.
This brings our implementation closer in line with C++26 Library
Hardening (one particular benefit is that using the observe semantic
makes adopting hardening easier for projects).
@var-const var-const force-pushed the varconst/hardening-semantics-introduce branch from 17b5a0d to f187236 Compare July 15, 2025 05:34
@llvmbot llvmbot added libc++abi libc++abi C++ Runtime Library. Not libc++. github:workflow labels Jul 15, 2025
@var-const var-const merged commit 7345508 into llvm:main Jul 15, 2025
82 checks passed
philnik777 added a commit that referenced this pull request Jul 15, 2025
philnik777 added a commit that referenced this pull request Jul 15, 2025
Reverts #148268

It looks like this was based on #148266, which I reverted in #148787.
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Jul 15, 2025
… (#148822)

Reverts llvm/llvm-project#148268

It looks like this was based on #148266, which I reverted in #148787.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
github:workflow hardening Issues related to the hardening effort libc++abi libc++abi C++ Runtime Library. Not libc++. libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants