Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions libcxx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@ if (NOT "${LIBCXX_HARDENING_MODE}" IN_LIST LIBCXX_SUPPORTED_HARDENING_MODES)
message(FATAL_ERROR
"Unsupported hardening mode: '${LIBCXX_HARDENING_MODE}'. Supported values are ${LIBCXX_SUPPORTED_HARDENING_MODES}.")
endif()
set(LIBCXX_SUPPORTED_ASSERTION_SEMANTICS hardening_dependent ignore observe quick_enforce enforce)
set(LIBCXX_ASSERTION_SEMANTIC "hardening_dependent" CACHE STRING
"Specify the default assertion semantic to use. This semantic will be used
inside the compiled library and will be the default when compiling user code.
Note that users can override this setting in their own code. This does not
affect the ABI. Supported values are ${LIBCXX_SUPPORTED_ASSERTION_SEMANTICS}.
`hardening_dependent` is a special value that instructs the library to select
the assertion semantic based on the hardening mode in effect.")

if (NOT "${LIBCXX_ASSERTION_SEMANTIC}" IN_LIST LIBCXX_SUPPORTED_ASSERTION_SEMANTICS)
message(FATAL_ERROR
"Unsupported assertion semantic: '${LIBCXX_ASSERTION_SEMANTIC}'. Supported values are ${LIBCXX_SUPPORTED_ASSERTION_SEMANTICS}.")
endif()
set(LIBCXX_ASSERTION_HANDLER_FILE
"vendor/llvm/default_assertion_handler.in"
CACHE STRING
Expand Down Expand Up @@ -763,6 +776,17 @@ elseif (LIBCXX_HARDENING_MODE STREQUAL "extensive")
elseif (LIBCXX_HARDENING_MODE STREQUAL "debug")
config_define(8 _LIBCPP_HARDENING_MODE_DEFAULT)
endif()
if (LIBCXX_ASSERTION_SEMANTIC STREQUAL "hardening_dependent")
config_define(2 _LIBCPP_ASSERTION_SEMANTIC_DEFAULT)
elseif (LIBCXX_ASSERTION_SEMANTIC STREQUAL "ignore")
config_define(4 _LIBCPP_ASSERTION_SEMANTIC_DEFAULT)
elseif (LIBCXX_ASSERTION_SEMANTIC STREQUAL "observe")
config_define(8 _LIBCPP_ASSERTION_SEMANTIC_DEFAULT)
elseif (LIBCXX_ASSERTION_SEMANTIC STREQUAL "quick_enforce")
config_define(16 _LIBCPP_ASSERTION_SEMANTIC_DEFAULT)
elseif (LIBCXX_ASSERTION_SEMANTIC STREQUAL "enforce")
config_define(32 _LIBCPP_ASSERTION_SEMANTIC_DEFAULT)
endif()

if (LIBCXX_PSTL_BACKEND STREQUAL "serial")
config_define(1 _LIBCPP_PSTL_BACKEND_SERIAL)
Expand Down
14 changes: 13 additions & 1 deletion libcxx/include/__config
Original file line number Diff line number Diff line change
Expand Up @@ -291,14 +291,26 @@ typedef __char32_t char32_t;
# define _LIBCPP_HARDENING_SIG n // "none"
# endif

# if _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_OBSERVE
# define _LIBCPP_ASSERTION_SEMANTIC_SIG o
# elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE
# define _LIBCPP_ASSERTION_SEMANTIC_SIG q
# elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_ENFORCE
# define _LIBCPP_ASSERTION_SEMANTIC_SIG e
# else
# define _LIBCPP_ASSERTION_SEMANTIC_SIG i // `ignore`
# endif

# if !_LIBCPP_HAS_EXCEPTIONS
# define _LIBCPP_EXCEPTIONS_SIG n
# else
# define _LIBCPP_EXCEPTIONS_SIG e
# endif

# define _LIBCPP_ODR_SIGNATURE \
_LIBCPP_CONCAT(_LIBCPP_CONCAT(_LIBCPP_HARDENING_SIG, _LIBCPP_EXCEPTIONS_SIG), _LIBCPP_VERSION)
_LIBCPP_CONCAT( \
_LIBCPP_CONCAT(_LIBCPP_CONCAT(_LIBCPP_HARDENING_SIG, _LIBCPP_ASSERTION_SEMANTIC_SIG), _LIBCPP_EXCEPTIONS_SIG), \
_LIBCPP_VERSION)

// This macro marks a symbol as being hidden from libc++'s ABI. This is achieved
// on two levels:
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/__config_site.in
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

// Hardening.
#cmakedefine _LIBCPP_HARDENING_MODE_DEFAULT @_LIBCPP_HARDENING_MODE_DEFAULT@
#cmakedefine _LIBCPP_ASSERTION_SEMANTIC_DEFAULT @_LIBCPP_ASSERTION_SEMANTIC_DEFAULT@

// __USE_MINGW_ANSI_STDIO gets redefined on MinGW
#ifdef __clang__
Expand Down
72 changes: 53 additions & 19 deletions libcxx/include/__configuration/hardening.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,26 @@ _LIBCPP_HARDENING_MODE_EXTENSIVE, \
_LIBCPP_HARDENING_MODE_DEBUG
#endif

// Hardening assertion semantics generally mirror the evaluation semantics of C++26 Contracts:
// The library provides the macro `_LIBCPP_ASSERTION_SEMANTIC` for configuring the assertion semantic used by hardening;
// it can be set to one of the following values:
//
// - `_LIBCPP_ASSERTION_SEMANTIC_IGNORE`;
// - `_LIBCPP_ASSERTION_SEMANTIC_OBSERVE`;
// - `_LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE`;
// - `_LIBCPP_ASSERTION_SEMANTIC_ENFORCE`.
//
// libc++ 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.
//
// Additionally, a special `hardening-dependent` value selects the assertion semantic based on the hardening mode in
// effect: the production-capable modes (`fast` and `extensive`) map to `quick_enforce` and the `debug` mode maps to
// `enforce`. The `hardening-dependent` semantic cannot be selected explicitly, it is only used when no assertion
// semantic is provided by the user _and_ the library's default semantic is configured to be dependent on hardening.
//
// 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;
Expand All @@ -150,32 +163,53 @@ _LIBCPP_HARDENING_MODE_DEBUG
// 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)
# define _LIBCPP_ASSERTION_SEMANTIC_HARDENING_DEPENDENT (1 << 1)
# define _LIBCPP_ASSERTION_SEMANTIC_IGNORE (1 << 2)
# define _LIBCPP_ASSERTION_SEMANTIC_OBSERVE (1 << 3)
# define _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE (1 << 4)
# define _LIBCPP_ASSERTION_SEMANTIC_ENFORCE (1 << 5)
// 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

#else

// If the user attempts to configure the assertion semantic, check that it is allowed in the current environment.
#if defined(_LIBCPP_ASSERTION_SEMANTIC)
# if !_LIBCPP_HAS_EXPERIMENTAL_LIBRARY
# error "Assertion semantics are an experimental feature."
# endif
# if defined(_LIBCPP_CXX03_LANG)
# error "Assertion semantics are not available in the C++03 mode."
# endif
#endif // defined(_LIBCPP_ASSERTION_SEMANTIC)

// User-provided semantic takes top priority -- don't override if set.
#ifndef _LIBCPP_ASSERTION_SEMANTIC

#endif // _LIBCPP_ASSERTION_SEMANTIC
# ifndef _LIBCPP_ASSERTION_SEMANTIC_DEFAULT
# error _LIBCPP_ASSERTION_SEMANTIC_DEFAULT is not defined. This definition should be set at configuration time in \
the `__config_site` header, please make sure your installation of libc++ is not broken.
# endif

# if _LIBCPP_ASSERTION_SEMANTIC_DEFAULT != _LIBCPP_ASSERTION_SEMANTIC_HARDENING_DEPENDENT
# define _LIBCPP_ASSERTION_SEMANTIC _LIBCPP_ASSERTION_SEMANTIC_DEFAULT
# else
# 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_DEFAULT != _LIBCPP_ASSERTION_SEMANTIC_HARDENING_DEPENDENT

#endif // #ifndef _LIBCPP_ASSERTION_SEMANTIC

// Finally, validate the selected semantic (in case the user tries setting it to an incorrect value):
#if _LIBCPP_ASSERTION_SEMANTIC != _LIBCPP_ASSERTION_SEMANTIC_IGNORE && \
_LIBCPP_ASSERTION_SEMANTIC != _LIBCPP_ASSERTION_SEMANTIC_OBSERVE && \
_LIBCPP_ASSERTION_SEMANTIC != _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE && \
_LIBCPP_ASSERTION_SEMANTIC != _LIBCPP_ASSERTION_SEMANTIC_ENFORCE
# error _LIBCPP_ASSERTION_SEMANTIC must be set to one of the following values: \
_LIBCPP_ASSERTION_SEMANTIC_IGNORE, \
_LIBCPP_ASSERTION_SEMANTIC_OBSERVE, \
_LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE, \
_LIBCPP_ASSERTION_SEMANTIC_ENFORCE
#endif

#endif // _LIBCPP___CONFIGURATION_HARDENING_H
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// ABI tags have no effect in MSVC mode.
// XFAIL: msvc

// XFAIL: FROZEN-CXX03-HEADERS-FIXME

// Test that we encode the assertion semantic in an ABI tag to avoid ODR violations when linking TUs that have different
// values for it.

// Note that GCC doesn't support `-Wno-macro-redefined`.
// RUN: %{cxx} %s %{flags} %{compile_flags} -c -DTU1 -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_IGNORE -o %t.tu1.o
// RUN: %{cxx} %s %{flags} %{compile_flags} -c -DTU2 -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_OBSERVE -o %t.tu2.o
// RUN: %{cxx} %s %{flags} %{compile_flags} -c -DTU3 -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE -o %t.tu3.o
// RUN: %{cxx} %s %{flags} %{compile_flags} -c -DTU4 -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_ENFORCE -o %t.tu4.o
// RUN: %{cxx} %s %{flags} %{compile_flags} -c -DMAIN -o %t.main.o
// RUN: %{cxx} %t.tu1.o %t.tu2.o %t.tu3.o %t.tu4.o %t.main.o %{flags} %{link_flags} -o %t.exe
// RUN: %{exec} %t.exe

#include "test_macros.h"

// `ignore` assertion semantic.
#ifdef TU1
# include <__config>
_LIBCPP_HIDE_FROM_ABI TEST_NOINLINE inline int f() { return 1; }
int tu1() { return f(); }
#endif // TU1

// `observe` assertion semantic.
#ifdef TU2
# include <__config>
_LIBCPP_HIDE_FROM_ABI TEST_NOINLINE inline int f() { return 2; }
int tu2() { return f(); }
#endif // TU2

// `quick-enforce` assertion semantic.
#ifdef TU3
# include <__config>
_LIBCPP_HIDE_FROM_ABI TEST_NOINLINE inline int f() { return 3; }
int tu3() { return f(); }
#endif // TU3

// `enforce` assertion semantic.
#ifdef TU4
# include <__config>
_LIBCPP_HIDE_FROM_ABI TEST_NOINLINE inline int f() { return 4; }
int tu4() { return f(); }
#endif // TU4

#ifdef MAIN
# include <cassert>

int tu1();
int tu2();
int tu3();
int tu4();

int main(int, char**) {
assert(tu1() == 1);
assert(tu2() == 2);
assert(tu3() == 3);
assert(tu4() == 4);
return 0;
}
#endif // MAIN
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// This test verifies that setting the assertion semantic to a value that's not part of the predefined constants
// triggers a compile-time error.

// Modules build produces a different error ("Could not build module 'std'").
// UNSUPPORTED: clang-modules-build
// UNSUPPORTED: c++03, libcpp-has-no-experimental-hardening-observe-semantic
// REQUIRES: verify-support

// RUN: %{verify} -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=42
// `hardening-dependent` cannot be set as the semantic (it's only an indicator to use hardening-related logic to pick
// the final semantic).
// RUN: %{verify} -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_HARDENING_DEPENDENT
// Make sure that common cases of misuse produce readable errors. We deliberately disallow setting the assertion
// semantic as if it were a boolean flag.
// RUN: %{verify} -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=0
// RUN: %{verify} -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=1
// RUN: %{verify} -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC

#include <cassert>

// expected-error@*:* {{_LIBCPP_ASSERTION_SEMANTIC must be set to one of the following values: _LIBCPP_ASSERTION_SEMANTIC_IGNORE, _LIBCPP_ASSERTION_SEMANTIC_OBSERVE, _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE, _LIBCPP_ASSERTION_SEMANTIC_ENFORCE}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// This test ensures that we can override the assertion semantic used by any checked hardening mode with `enforce` on
// a per-TU basis (this is valid for the `debug` mode as well, though a no-op).

// `check_assertion.h` is only available starting from C++11 and requires Unix headers and regex support.
// REQUIRES: has-unix-headers
// UNSUPPORTED: c++03, no-localization
// UNSUPPORTED: libcpp-hardening-mode=none, libcpp-has-no-experimental-hardening-observe-semantic
// The ability to set a custom abort message is required to compare the assertion message.
// XFAIL: availability-verbose_abort-missing
// ADDITIONAL_COMPILE_FLAGS: -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_ENFORCE

#include <cassert>
#include "check_assertion.h"

int main(int, char**) {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(true, "Should not fire");
TEST_LIBCPP_ASSERT_FAILURE([] { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(false, "Should fire and log a message"); }(),
"Should fire and log a message");

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// This test ensures that we can override the assertion semantic used by any hardening mode with `ignore` on a per-TU
// basis (this is valid for the `none` mode as well, though a no-op).

// `check_assertion.h` is only available starting from C++11 and requires Unix headers and regex support.
// REQUIRES: has-unix-headers
// UNSUPPORTED: c++03, no-localization
// UNSUPPORTED: libcpp-has-no-experimental-hardening-observe-semantic
// ADDITIONAL_COMPILE_FLAGS: -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_IGNORE

#include <cassert>
#include "check_assertion.h"

int main(int, char**) {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(true, "Should not fire");
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(false, "Also should not fire");

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// This test ensures that we can override the assertion semantic used by any checked hardening mode with `observe` on
// a per-TU basis.

// `check_assertion.h` is only available starting from C++11 and requires Unix headers and regex support.
// REQUIRES: has-unix-headers
// UNSUPPORTED: c++03, no-localization
// UNSUPPORTED: libcpp-hardening-mode=none, libcpp-has-no-experimental-hardening-observe-semantic
// ADDITIONAL_COMPILE_FLAGS: -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_OBSERVE

#include <cassert>
#include "check_assertion.h"

int main(int, char**) {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(true, "Should not fire");
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(false, "Also should not fire");
// TODO(hardening): check that a message is logged.

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// This test ensures that we can override the assertion semantic used by any checked hardening mode with `quick-enforce`
// on a per-TU basis (this is valid for the `fast` and `extensive` modes as well, though a no-op).

// `check_assertion.h` is only available starting from C++11 and requires Unix headers and regex support.
// REQUIRES: has-unix-headers
// UNSUPPORTED: c++03, no-localization
// UNSUPPORTED: libcpp-hardening-mode=none, libcpp-has-no-experimental-hardening-observe-semantic
// ADDITIONAL_COMPILE_FLAGS: -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE

#include <cassert>
#include "check_assertion.h"

int main(int, char**) {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(true, "Should not fire");
TEST_LIBCPP_ASSERT_FAILURE(
[] { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(false, "Should fire without logging a message"); }(),
"The message should not matter");

return 0;
}
Loading
Loading