-
Notifications
You must be signed in to change notification settings - Fork 14.9k
Prepare libcxx and libcxxabi for pointer field protection. #151651
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: users/pcc/spr/main.prepare-libcxx-and-libcxxabi-for-pointer-field-protection
Are you sure you want to change the base?
Conversation
Created using spr 1.3.6-beta.1
@llvm/pr-subscribers-libcxx @llvm/pr-subscribers-libcxxabi Author: Peter Collingbourne (pcc) ChangesPointer field protection has the following characteristics:
Full diff: https://github.com/llvm/llvm-project/pull/151651.diff 7 Files Affected:
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 77a71b6cf1cae..60bbcb8fa61ed 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -484,8 +484,21 @@ typedef __char32_t char32_t;
# define _LIBCPP_EXCEPTIONS_SIG e
# endif
+# if !_LIBCPP_HAS_EXCEPTIONS
+# define _LIBCPP_EXCEPTIONS_SIG n
+# else
+# define _LIBCPP_EXCEPTIONS_SIG e
+# endif
+
+# if __has_feature(pointer_field_protection)
+# define _LIBCPP_PFP_SIG p
+# else
+# define _LIBCPP_PFP_SIG
+# 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_EXCEPTIONS_SIG), _LIBCPP_PFP_SIG), \
+ _LIBCPP_VERSION)
// This macro marks a symbol as being hidden from libc++'s ABI. This is achieved
// on two levels:
@@ -1262,6 +1275,12 @@ typedef __char32_t char32_t;
# define _LIBCPP_HAS_EXPLICIT_THIS_PARAMETER 0
# endif
+# if __has_feature(pointer_field_protection)
+# define _LIBCPP_NO_PFP [[clang::no_field_protection]]
+# else
+# define _LIBCPP_NO_PFP
+# endif
+
#endif // __cplusplus
#endif // _LIBCPP___CONFIG
diff --git a/libcxx/include/__type_traits/is_trivially_relocatable.h b/libcxx/include/__type_traits/is_trivially_relocatable.h
index 9b0e240de55f4..dc8b0e23360fd 100644
--- a/libcxx/include/__type_traits/is_trivially_relocatable.h
+++ b/libcxx/include/__type_traits/is_trivially_relocatable.h
@@ -34,10 +34,13 @@ template <class _Tp, class = void>
struct __libcpp_is_trivially_relocatable : is_trivially_copyable<_Tp> {};
#endif
+// __trivially_relocatable on libc++'s builtin types does not currently return the right answer with PFP.
+#if !__has_feature(pointer_field_protection)
template <class _Tp>
struct __libcpp_is_trivially_relocatable<_Tp,
__enable_if_t<is_same<_Tp, typename _Tp::__trivially_relocatable>::value> >
: true_type {};
+#endif
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/typeinfo b/libcxx/include/typeinfo
index 24aaabf0a87df..a1ee4155bcb3d 100644
--- a/libcxx/include/typeinfo
+++ b/libcxx/include/typeinfo
@@ -300,7 +300,7 @@ class _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_TYPE_INFO_VTABLE_POINTER_AUTH type_info
protected:
typedef __type_info_implementations::__impl __impl;
- __impl::__type_name_t __type_name;
+ _LIBCPP_NO_PFP __impl::__type_name_t __type_name;
_LIBCPP_HIDE_FROM_ABI explicit type_info(const char* __n) : __type_name(__impl::__string_to_type_name(__n)) {}
diff --git a/libcxx/test/libcxx/gdb/gdb_pretty_printer_test.sh.cpp b/libcxx/test/libcxx/gdb/gdb_pretty_printer_test.sh.cpp
index f125cc9adc491..d84914999b8fa 100644
--- a/libcxx/test/libcxx/gdb/gdb_pretty_printer_test.sh.cpp
+++ b/libcxx/test/libcxx/gdb/gdb_pretty_printer_test.sh.cpp
@@ -256,9 +256,12 @@ void unique_ptr_test() {
ComparePrettyPrintToRegex(std::move(forty_two),
R"(std::unique_ptr<int> containing = {__ptr_ = 0x[a-f0-9]+})");
+#if !__has_feature(pointer_field_protection)
+ // GDB doesn't know how to read PFP fields correctly yet.
std::unique_ptr<int> this_is_null;
ComparePrettyPrintToChars(std::move(this_is_null),
R"(std::unique_ptr is nullptr)");
+#endif
}
void bitset_test() {
@@ -476,10 +479,13 @@ void vector_test() {
"std::vector of length "
"3, capacity 3 = {5, 6, 7}");
+#if !__has_feature(pointer_field_protection)
+ // GDB doesn't know how to read PFP fields correctly yet.
std::vector<int, UncompressibleAllocator<int>> test3({7, 8});
ComparePrettyPrintToChars(std::move(test3),
"std::vector of length "
"2, capacity 2 = {7, 8}");
+#endif
}
void set_iterator_test() {
@@ -650,8 +656,11 @@ void shared_ptr_test() {
test0,
R"(std::shared_ptr<int> count [3\?], weak [0\?]( \(libc\+\+ missing debug info\))? containing = {__ptr_ = 0x[a-f0-9]+})");
+#if !__has_feature(pointer_field_protection)
+ // GDB doesn't know how to read PFP fields correctly yet.
std::shared_ptr<const int> test3;
ComparePrettyPrintToChars(test3, "std::shared_ptr is nullptr");
+#endif
}
void streampos_test() {
diff --git a/libcxx/test/libcxx/type_traits/is_trivially_relocatable.compile.pass.cpp b/libcxx/test/libcxx/type_traits/is_trivially_relocatable.compile.pass.cpp
index 8066925f2900a..3b9d7e1776a73 100644
--- a/libcxx/test/libcxx/type_traits/is_trivially_relocatable.compile.pass.cpp
+++ b/libcxx/test/libcxx/type_traits/is_trivially_relocatable.compile.pass.cpp
@@ -28,6 +28,12 @@
# include <locale>
#endif
+#if __has_feature(pointer_field_protection)
+constexpr bool pfp_disabled = false;
+#else
+constexpr bool pfp_disabled = true;
+#endif
+
static_assert(std::__libcpp_is_trivially_relocatable<char>::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<int>::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<double>::value, "");
@@ -70,8 +76,8 @@ static_assert(!std::__libcpp_is_trivially_relocatable<NonTrivialDestructor>::val
// ----------------------
// __split_buffer
-static_assert(std::__libcpp_is_trivially_relocatable<std::__split_buffer<int> >::value, "");
-static_assert(std::__libcpp_is_trivially_relocatable<std::__split_buffer<NotTriviallyCopyable> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::__split_buffer<int> >::value == pfp_disabled, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::__split_buffer<NotTriviallyCopyable> >::value == pfp_disabled, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::__split_buffer<int, test_allocator<int> > >::value, "");
// standard library types
@@ -84,7 +90,7 @@ static_assert(std::__libcpp_is_trivially_relocatable<std::array<std::unique_ptr<
static_assert(std::__libcpp_is_trivially_relocatable<std::array<int, 1> >::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::array<NotTriviallyCopyable, 1> >::value, "");
-static_assert(std::__libcpp_is_trivially_relocatable<std::array<std::unique_ptr<int>, 1> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::array<std::unique_ptr<int>, 1> >::value == pfp_disabled, "");
// basic_string
#if !__has_feature(address_sanitizer) || !_LIBCPP_INSTRUMENTED_WITH_ASAN
@@ -99,17 +105,17 @@ struct NotTriviallyRelocatableCharTraits : constexpr_char_traits<T> {
};
static_assert(std::__libcpp_is_trivially_relocatable<
- std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::value,
+ std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::value == pfp_disabled,
"");
static_assert(std::__libcpp_is_trivially_relocatable<
- std::basic_string<char, NotTriviallyRelocatableCharTraits<char>, std::allocator<char> > >::value,
+ std::basic_string<char, NotTriviallyRelocatableCharTraits<char>, std::allocator<char> > >::value == pfp_disabled,
"");
static_assert(std::__libcpp_is_trivially_relocatable<
- std::basic_string<MyChar, constexpr_char_traits<MyChar>, std::allocator<MyChar> > >::value,
+ std::basic_string<MyChar, constexpr_char_traits<MyChar>, std::allocator<MyChar> > >::value == pfp_disabled,
"");
static_assert(
std::__libcpp_is_trivially_relocatable<
- std::basic_string<MyChar, NotTriviallyRelocatableCharTraits<MyChar>, std::allocator<MyChar> > >::value,
+ std::basic_string<MyChar, NotTriviallyRelocatableCharTraits<MyChar>, std::allocator<MyChar> > >::value == pfp_disabled,
"");
static_assert(!std::__libcpp_is_trivially_relocatable<
std::basic_string<char, std::char_traits<char>, test_allocator<char> > >::value,
@@ -121,21 +127,21 @@ static_assert(
#endif
// deque
-static_assert(std::__libcpp_is_trivially_relocatable<std::deque<int> >::value, "");
-static_assert(std::__libcpp_is_trivially_relocatable<std::deque<NotTriviallyCopyable> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::deque<int> >::value == pfp_disabled, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::deque<NotTriviallyCopyable> >::value == pfp_disabled, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::deque<int, test_allocator<int> > >::value, "");
// exception_ptr
#ifndef _LIBCPP_ABI_MICROSOFT // FIXME: Is this also the case on windows?
-static_assert(std::__libcpp_is_trivially_relocatable<std::exception_ptr>::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::exception_ptr>::value == pfp_disabled, "");
#endif
// expected
#if TEST_STD_VER >= 23
-static_assert(std::__libcpp_is_trivially_relocatable<std::expected<int, int> >::value);
-static_assert(std::__libcpp_is_trivially_relocatable<std::expected<std::unique_ptr<int>, int>>::value);
-static_assert(std::__libcpp_is_trivially_relocatable<std::expected<int, std::unique_ptr<int>>>::value);
-static_assert(std::__libcpp_is_trivially_relocatable<std::expected<std::unique_ptr<int>, std::unique_ptr<int>>>::value);
+static_assert(std::__libcpp_is_trivially_relocatable<std::expected<int, int> >::value == pfp_disabled);
+static_assert(std::__libcpp_is_trivially_relocatable<std::expected<std::unique_ptr<int>, int>>::value == pfp_disabled);
+static_assert(std::__libcpp_is_trivially_relocatable<std::expected<int, std::unique_ptr<int>>>::value == pfp_disabled);
+static_assert(std::__libcpp_is_trivially_relocatable<std::expected<std::unique_ptr<int>, std::unique_ptr<int>>>::value == pfp_disabled);
static_assert(!std::__libcpp_is_trivially_relocatable<std::expected<int, NotTriviallyCopyable>>::value);
static_assert(!std::__libcpp_is_trivially_relocatable<std::expected<NotTriviallyCopyable, int>>::value);
@@ -145,42 +151,42 @@ static_assert(
// locale
#ifndef TEST_HAS_NO_LOCALIZATION
-static_assert(std::__libcpp_is_trivially_relocatable<std::locale>::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::locale>::value == pfp_disabled, "");
#endif
// optional
#if TEST_STD_VER >= 17
static_assert(std::__libcpp_is_trivially_relocatable<std::optional<int>>::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::optional<NotTriviallyCopyable>>::value, "");
-static_assert(std::__libcpp_is_trivially_relocatable<std::optional<std::unique_ptr<int>>>::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::optional<std::unique_ptr<int>>>::value == pfp_disabled, "");
#endif // TEST_STD_VER >= 17
// pair
-static_assert(std::__libcpp_is_trivially_relocatable<std::pair<int, int> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::pair<int, int> >::value == pfp_disabled, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::pair<NotTriviallyCopyable, int> >::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::pair<int, NotTriviallyCopyable> >::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::pair<NotTriviallyCopyable, NotTriviallyCopyable> >::value,
"");
-static_assert(std::__libcpp_is_trivially_relocatable<std::pair<std::unique_ptr<int>, std::unique_ptr<int> > >::value,
+static_assert(std::__libcpp_is_trivially_relocatable<std::pair<std::unique_ptr<int>, std::unique_ptr<int> > >::value == pfp_disabled,
"");
// shared_ptr
-static_assert(std::__libcpp_is_trivially_relocatable<std::shared_ptr<NotTriviallyCopyable> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::shared_ptr<NotTriviallyCopyable> >::value == pfp_disabled, "");
// tuple
#if TEST_STD_VER >= 11
static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<> >::value, "");
-static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<int> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<int> >::value == pfp_disabled, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::tuple<NotTriviallyCopyable> >::value, "");
-static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<std::unique_ptr<int> > >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<std::unique_ptr<int> > >::value == pfp_disabled, "");
-static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<int, int> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<int, int> >::value == pfp_disabled, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::tuple<NotTriviallyCopyable, int> >::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::tuple<int, NotTriviallyCopyable> >::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::tuple<NotTriviallyCopyable, NotTriviallyCopyable> >::value,
"");
-static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<std::unique_ptr<int>, std::unique_ptr<int> > >::value,
+static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<std::unique_ptr<int>, std::unique_ptr<int> > >::value == pfp_disabled,
"");
#endif // TEST_STD_VER >= 11
@@ -205,9 +211,9 @@ struct NotTriviallyRelocatablePointer {
void operator()(T*);
};
-static_assert(std::__libcpp_is_trivially_relocatable<std::unique_ptr<int> >::value, "");
-static_assert(std::__libcpp_is_trivially_relocatable<std::unique_ptr<NotTriviallyCopyable> >::value, "");
-static_assert(std::__libcpp_is_trivially_relocatable<std::unique_ptr<int[]> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::unique_ptr<int> >::value == pfp_disabled, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::unique_ptr<NotTriviallyCopyable> >::value == pfp_disabled, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::unique_ptr<int[]> >::value == pfp_disabled, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::unique_ptr<int, NotTriviallyRelocatableDeleter> >::value,
"");
static_assert(!std::__libcpp_is_trivially_relocatable<std::unique_ptr<int[], NotTriviallyRelocatableDeleter> >::value,
@@ -221,23 +227,23 @@ static_assert(!std::__libcpp_is_trivially_relocatable<std::unique_ptr<int[], Not
#if TEST_STD_VER >= 17
static_assert(std::__libcpp_is_trivially_relocatable<std::variant<int> >::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::variant<NotTriviallyCopyable> >::value, "");
-static_assert(std::__libcpp_is_trivially_relocatable<std::variant<std::unique_ptr<int> > >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::variant<std::unique_ptr<int> > >::value == pfp_disabled, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::variant<int, int> >::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::variant<NotTriviallyCopyable, int> >::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::variant<int, NotTriviallyCopyable> >::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::variant<NotTriviallyCopyable, NotTriviallyCopyable> >::value,
"");
-static_assert(std::__libcpp_is_trivially_relocatable<std::variant<std::unique_ptr<int>, std::unique_ptr<int> > >::value,
+static_assert(std::__libcpp_is_trivially_relocatable<std::variant<std::unique_ptr<int>, std::unique_ptr<int> > >::value == pfp_disabled,
"");
#endif // TEST_STD_VER >= 17
// vector
-static_assert(std::__libcpp_is_trivially_relocatable<std::vector<int> >::value, "");
-static_assert(std::__libcpp_is_trivially_relocatable<std::vector<NotTriviallyCopyable> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::vector<int> >::value == pfp_disabled, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::vector<NotTriviallyCopyable> >::value == pfp_disabled, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::vector<int, test_allocator<int> > >::value, "");
// weak_ptr
-static_assert(std::__libcpp_is_trivially_relocatable<std::weak_ptr<NotTriviallyCopyable> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::weak_ptr<NotTriviallyCopyable> >::value == pfp_disabled, "");
// TODO: Mark all the trivially relocatable STL types as such
diff --git a/libcxxabi/include/__cxxabi_config.h b/libcxxabi/include/__cxxabi_config.h
index 759445dac91f9..00e1357cdab85 100644
--- a/libcxxabi/include/__cxxabi_config.h
+++ b/libcxxabi/include/__cxxabi_config.h
@@ -109,4 +109,14 @@
# define _LIBCXXABI_NOEXCEPT noexcept
#endif
+#if defined(_LIBCXXABI_COMPILER_CLANG)
+# if __has_feature(pointer_field_protection)
+# define _LIBCXXABI_NO_PFP [[clang::no_field_protection]]
+# else
+# define _LIBCXXABI_NO_PFP
+# endif
+#else
+# define _LIBCXXABI_NO_PFP
+#endif
+
#endif // ____CXXABI_CONFIG_H
diff --git a/libcxxabi/src/private_typeinfo.h b/libcxxabi/src/private_typeinfo.h
index 328a02edef5c1..a3bc0bffd41bc 100644
--- a/libcxxabi/src/private_typeinfo.h
+++ b/libcxxabi/src/private_typeinfo.h
@@ -145,7 +145,7 @@ class _LIBCXXABI_TYPE_VIS __class_type_info : public __shim_type_info {
// Has one non-virtual public base class at offset zero
class _LIBCXXABI_TYPE_VIS __si_class_type_info : public __class_type_info {
public:
- const __class_type_info *__base_type;
+ _LIBCXXABI_NO_PFP const __class_type_info *__base_type;
_LIBCXXABI_HIDDEN virtual ~__si_class_type_info();
@@ -204,7 +204,7 @@ class _LIBCXXABI_TYPE_VIS __vmi_class_type_info : public __class_type_info {
class _LIBCXXABI_TYPE_VIS __pbase_type_info : public __shim_type_info {
public:
unsigned int __flags;
- const __shim_type_info *__pointee;
+ _LIBCXXABI_NO_PFP const __shim_type_info *__pointee;
enum __masks {
__const_mask = 0x1,
@@ -245,7 +245,7 @@ class _LIBCXXABI_TYPE_VIS __pointer_type_info : public __pbase_type_info {
class _LIBCXXABI_TYPE_VIS __pointer_to_member_type_info
: public __pbase_type_info {
public:
- const __class_type_info *__context;
+ _LIBCXXABI_NO_PFP const __class_type_info *__context;
_LIBCXXABI_HIDDEN virtual ~__pointer_to_member_type_info();
_LIBCXXABI_HIDDEN virtual bool can_catch(const __shim_type_info *,
|
You can test this locally with the following command:git-clang-format --diff origin/main HEAD --extensions ,h -- libcxx/include/__config libcxx/include/__type_traits/is_trivially_relocatable.h libcxx/include/typeinfo libcxxabi/include/__cxxabi_config.h libcxxabi/src/private_typeinfo.h
View the diff from clang-format here.diff --git a/libcxxabi/src/private_typeinfo.h b/libcxxabi/src/private_typeinfo.h
index a3bc0bffd..c472bf076 100644
--- a/libcxxabi/src/private_typeinfo.h
+++ b/libcxxabi/src/private_typeinfo.h
@@ -145,7 +145,7 @@ public:
// Has one non-virtual public base class at offset zero
class _LIBCXXABI_TYPE_VIS __si_class_type_info : public __class_type_info {
public:
- _LIBCXXABI_NO_PFP const __class_type_info *__base_type;
+ _LIBCXXABI_NO_PFP const __class_type_info* __base_type;
_LIBCXXABI_HIDDEN virtual ~__si_class_type_info();
@@ -204,7 +204,7 @@ public:
class _LIBCXXABI_TYPE_VIS __pbase_type_info : public __shim_type_info {
public:
unsigned int __flags;
- _LIBCXXABI_NO_PFP const __shim_type_info *__pointee;
+ _LIBCXXABI_NO_PFP const __shim_type_info* __pointee;
enum __masks {
__const_mask = 0x1,
@@ -245,7 +245,7 @@ public:
class _LIBCXXABI_TYPE_VIS __pointer_to_member_type_info
: public __pbase_type_info {
public:
- _LIBCXXABI_NO_PFP const __class_type_info *__context;
+ _LIBCXXABI_NO_PFP const __class_type_info* __context;
_LIBCXXABI_HIDDEN virtual ~__pointer_to_member_type_info();
_LIBCXXABI_HIDDEN virtual bool can_catch(const __shim_type_info *,
|
Pointer field protection has the following characteristics: - Pointer fields in RTTI types are unsigned. Signing these fields is unnecessary because PFP is a mechanism for protecting the heap, and the RTTI objects typically live in global variables. Therefore, mark the fields with the no_field_protection to inhibit PFP for these fields. - libcxx's interim trivial relocatability implementation, which memcpy's some non-trivially-copyable objects according to the value of a trait, conflicts with PFP by causing such copies to result in a crash in some cases where the object being copied includes a pointer field. These copies are undefined behavior and in C++26 they must be made using std::trivially_relocate which is not yet implemented, together with using a different predicate for the trait. Therefore, make libcxx's trivially relocatable predicate simply return the value of the trivially copyable trait when PFP is enabled, which results in standards compliant behavior that is accommodated by PFP. - Pointer field protection is an ABI break, so adjust the ABI tag when it is enabled. Pull Request: llvm#151651
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This requires and RFC, and, more importantly, CI coverage.
Created using spr 1.3.6-beta.1
libcxx/include/__config
Outdated
# if defined(__POINTER_FIELD_PROTECTION__) | ||
# define _LIBCPP_PFP_SIG p | ||
# else | ||
# define _LIBCPP_PFP_SIG | ||
# endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that pfp changes the layout of certain types? Why should there be an ABI tag for it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's only for ceses where we want different TUs to link together. Since PFP changes the layout of types that's not something we want users to do, which in turn means that we don't want to make it visible in the ABI tag. We should instead change the whole inline namespace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I removed this logic and added CMake logic to modify the namespace name if PFP is enabled.
libcxx/include/__config
Outdated
# define _LIBCPP_PFP [[clang::pointer_field_protection]] | ||
# define _LIBCPP_NO_PFP [[clang::no_field_protection]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should be _Uglified. Do these attributes do anything with pfp disabled? If no, why not simply check for their availability like with other attributes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[[clang::pointer_field_protection]]
will enable PFP on a type even if the command line flag is not passed, so we should avoid it when PFP is not enabled on the command line to avoid ABI breaks.
Checking attribute availability for [[clang::no_field_protection]]
sounds fine to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
#if !defined(__POINTER_FIELD_PROTECTION__) | ||
// GDB doesn't know how to read PFP fields correctly yet. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does GCC have pfp in general? If not, IMO we should just disable the pretty printer test with pfp enabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The support for this feature in GCC is independent of support in GDB. We could imagine debug info extensions being developed in the future to make it possible for this to pass in GDB even without GCC support.
That being said, disabling the test is also fine with me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
#if defined(__POINTER_FIELD_PROTECTION__) | ||
constexpr bool pfp_disabled = false; | ||
#else | ||
constexpr bool pfp_disabled = true; | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, can we just disable the test instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine with me I suppose. The correct result for __libcpp_is_trivially_relocatable
is implicitly tested by the other tests (which would crash if it was wrong).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
libcxxabi/include/__cxxabi_config.h
Outdated
#if defined(_LIBCXXABI_COMPILER_CLANG) | ||
# if defined(__POINTER_FIELD_PROTECTION__) | ||
# define _LIBCXXABI_NO_PFP [[clang::no_field_protection]] | ||
# else | ||
# define _LIBCXXABI_NO_PFP | ||
# endif | ||
#else | ||
# define _LIBCXXABI_NO_PFP | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this different from the libc++ version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes sense to change it to be the same. I'll do that. This was a leftover from when __POINTER_FIELD_PROTECTION__
was under __has_extension
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More generally, I feel like PFP needs its own __config_site
instead of the ad-hoc annotations all around. @ldionne any thoughts?
Created using spr 1.3.6-beta.1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More generally, I feel like PFP needs its own __config_site instead of the ad-hoc annotations all around. @ldionne any thoughts?
Not sure exactly what you had in mind. Would this be another macro like _LIBCPP_HARDENING_MODE_DEFAULT? I guess __config_site is necessary for default hardening mode because it's only specified via config options, but with PFP the compiler "knows" whether it is enabled via the builtin macro so we can just test for the macro in __config.
libcxx/include/__config
Outdated
# if defined(__POINTER_FIELD_PROTECTION__) | ||
# define _LIBCPP_PFP_SIG p | ||
# else | ||
# define _LIBCPP_PFP_SIG | ||
# endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I removed this logic and added CMake logic to modify the namespace name if PFP is enabled.
Pointer field protection has the following characteristics:
Pointer fields in RTTI types are unsigned. Signing these fields
is unnecessary because PFP is a mechanism for protecting the heap,
and the RTTI objects typically live in global variables. Therefore,
mark the fields with the no_field_protection to inhibit PFP for these
fields.
libcxx's interim trivial relocatability implementation, which memcpy's
some non-trivially-copyable objects according to the value of a
trait, conflicts with PFP by causing such copies to result in a
crash in some cases where the object being copied includes a pointer
field. These copies are undefined behavior and in C++26 they must
be made using std::trivially_relocate which is not yet implemented,
together with using a different predicate for the trait. Therefore,
make libcxx's trivially relocatable predicate simply return the value
of the trivially copyable trait when PFP is enabled, which results in
standards compliant behavior that is accommodated by PFP.
Pointer field protection is an ABI break, so adjust the default
namespace name when it is enabled.