-
Notifications
You must be signed in to change notification settings - Fork 15k
[libc++][hardening] Implement support for assertion semantics. #148172
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -147,6 +147,36 @@ _LIBCPP_HARDENING_MODE_EXTENSIVE, \ | |
| _LIBCPP_HARDENING_MODE_DEBUG | ||
| # endif | ||
|
|
||
| // Hardening assertion semantics mirror the evaluation semantics of P3100 Contracts: | ||
| // - `ignore` does not evaluate the assertion; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that is incorrect. We do evaluate the assertion, but then we do nothing with that. Let's make this documentation accurate. That being said, let's also file a Github issue to later go back and fix this: we should mirror what the actual C++26 contracts do, and not evaluate the assertion. Since that's going to require a rework of our assertion machinery, let's not do that right now. |
||
| // - `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; | ||
| // - P3471 "Standard Library Hardening" wording precludes using the Contracts `ignore` semantic for hardened | ||
| // preconditions in the Library; allowing this semantic to be used is a libc++ vendor extension. | ||
var-const marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // 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 | ||
|
|
@@ -489,7 +519,7 @@ typedef __char32_t char32_t; | |
| # ifndef _LIBCPP_NO_ABI_TAG | ||
| # define _LIBCPP_HIDE_FROM_ABI \ | ||
| _LIBCPP_HIDDEN _LIBCPP_EXCLUDE_FROM_EXPLICIT_INSTANTIATION \ | ||
| __attribute__((__abi_tag__(_LIBCPP_TOSTRING(_LIBCPP_ODR_SIGNATURE)))) | ||
| __attribute__((__abi_tag__(_LIBCPP_TOSTRING(_LIBCPP_ODR_SIGNATURE)))) | ||
| # else | ||
| # define _LIBCPP_HIDE_FROM_ABI _LIBCPP_HIDDEN _LIBCPP_EXCLUDE_FROM_EXPLICIT_INSTANTIATION | ||
| # endif | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| // -*- 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_HARDENING_FAILURE | ||
| #define _LIBCPP___LOG_HARDENING_FAILURE | ||
|
|
||
| #include <__config> | ||
|
|
||
| #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) | ||
| # pragma GCC system_header | ||
| #endif | ||
|
|
||
| _LIBCPP_BEGIN_NAMESPACE_STD | ||
|
|
||
| // This function should never be called directly from the code -- it should only be called through the | ||
| // `_LIBCPP_LOG_HARDENING_FAILURE` macro. | ||
| _LIBCPP_AVAILABILITY_LOG_HARDENING_FAILURE _LIBCPP_OVERRIDABLE_FUNC_VIS void | ||
ldionne marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| __libcpp_log_hardening_failure(const char* message) _NOEXCEPT; | ||
|
|
||
| // _LIBCPP_LOG_HARDENING_FAILURE(message) | ||
| // | ||
| // This macro is used to log a hardening failure without terminating the program (as is the case if the `observe` | ||
| // assertion semantic is used). Where possible, it logs in a way that indicates a fatal error (which might include | ||
| // capturing the stack trace). | ||
| #if !defined(_LIBCPP_LOG_HARDENING_FAILURE) | ||
|
|
||
| # if !_LIBCPP_AVAILABILITY_HAS_LOG_HARDENING_FAILURE | ||
| // The decltype is there to suppress -Wunused warnings in this configuration. | ||
| void __use(const char*); | ||
| # define _LIBCPP_LOG_HARDENING_FAILURE(message) (decltype(::std::__use(message))()) | ||
ldionne marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| # else | ||
| # define _LIBCPP_LOG_HARDENING_FAILURE(message) ::std::__libcpp_log_hardening_failure(message) | ||
| # endif | ||
|
|
||
| #endif // !defined(_LIBCPP_LOG_HARDENING_FAILURE) | ||
|
|
||
| _LIBCPP_END_NAMESPACE_STD | ||
|
|
||
| #endif // _LIBCPP___LOG_HARDENING_FAILURE | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| // -*- 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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include <__config> | ||
| #include <__log_hardening_failure> | ||
| #include <cstdio> | ||
|
|
||
| #ifdef __BIONIC__ | ||
| # include <syslog.h> | ||
| extern "C" void android_set_abort_message(const char* msg); | ||
| #endif // __BIONIC__ | ||
|
|
||
| #if defined(__APPLE__) && __has_include(<os/reason_private.h>) | ||
| # include <os/reason_private.h> | ||
| #endif | ||
|
|
||
| _LIBCPP_BEGIN_NAMESPACE_STD | ||
|
|
||
| _LIBCPP_WEAK void __libcpp_log_hardening_failure(const char* message) noexcept { | ||
ldionne marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // On Apple platforms, use the `os_fault_with_payload` OS function that simulates a crash. | ||
| #if defined(__APPLE__) && __has_include(<os/reason_private.h>) | ||
| os_fault_with_payload( | ||
| /*reason_namespace=*/OS_REASON_SECURITY, | ||
| /*reason_code=*/0, | ||
| /*payload=*/nullptr, | ||
| /*payload_size=*/0, | ||
| /*reason_string=*/message, | ||
| /*reason_flags=*/0); | ||
|
|
||
| #elif defined(__BIONIC__) | ||
| // Show error in tombstone. | ||
| android_set_abort_message(message); | ||
|
|
||
| // Show error in logcat. | ||
| openlog("libc++", 0, 0); | ||
| syslog(LOG_CRIT, "%s", message); | ||
| closelog(); | ||
|
|
||
| #else | ||
| fprintf(stderr, "%s", message); | ||
| #endif | ||
| } | ||
|
|
||
| _LIBCPP_END_NAMESPACE_STD | ||
Uh oh!
There was an error while loading. Please reload this page.