llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-libcxx Author: llvmbot <details> <summary>Changes</summary> Backport db7356dde8312a86e79ec0d7b7f844878a1a743c Requested by: @<!-- -->petrhosek --- Full diff: https://github.com/llvm/llvm-project/pull/210364.diff 3 Files Affected: - (modified) libcxx/src/include/overridable_function.h (+67-8) - (modified) libcxx/src/new.cpp (+4-4) - (modified) libcxxabi/src/stdlib_new_delete.cpp (+4-4) ``````````diff diff --git a/libcxx/src/include/overridable_function.h b/libcxx/src/include/overridable_function.h index 83439ad363208..e430ccda29f65 100644 --- a/libcxx/src/include/overridable_function.h +++ b/libcxx/src/include/overridable_function.h @@ -21,12 +21,17 @@ // whether an overridable function (typically a weak symbol) like `operator new` // has been overridden by a user or not. // -// This is a low-level utility which does not work on all platforms, since it needs to -// make assumptions about the object file format in use. This currently works with Mach-O -// files (used on Darwin) and with ELF files (used on Linux and others). On platforms -// where we know how to implement this detection, the macro -// _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION is defined to 1, and it is defined to 0 on -// other platforms. +// This is a low-level utility which does not work on all platforms, since it needs +// to make assumptions about the object file format in use. Furthermore, it requires +// the "base definition" of the function (the one we want to check whether it has been +// overridden) to be defined using the OVERRIDABLE_FUNCTION macro. +// +// This currently works with Mach-O files (used on Darwin) and with ELF files (used on Linux +// and others). On platforms where we know how to implement this detection, the macro +// _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION is defined to 1, and it is defined to 0 on +// other platforms. The OVERRIDABLE_FUNCTION macro is defined to perform a normal +// function definition on unsupported platforms so that it can be used to define functions +// regardless of whether detection is actually supported. // // How does this work? // ------------------- @@ -38,6 +43,14 @@ // compares the address of `__impl_ref<f>::__impl_` with the address of `f` loaded from // GOT: if `f` was overridden by the user in another TU, the addresses will be different. // +// When pointer authentication is used, the above mechanism doesn't work (yet) so we use +// a different strategy placing `f`'s definition (in the libc++ built library) inside +// a special section, which we do using the `__section__` attribute via the +// OVERRIDABLE_FUNCTION macro. Then, when comes the time to check whether the function has +// been overridden, we take the address of the function and we check whether it falls inside +// the special section we created. This can be done by finding pointers to the start and +// the end of the section, and then checking whether `f` falls within those bounds. +// // Important note // -------------- // @@ -50,6 +63,10 @@ # define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1 +# if !__has_feature(ptrauth_calls) + +# define OVERRIDABLE_FUNCTION [[gnu::weak]] + _LIBCPP_BEGIN_NAMESPACE_STD namespace { @@ -95,10 +112,52 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept { _LIBCPP_END_NAMESPACE_STD -#else +# else // __has_feature(ptrauth_calls) + +# include <ptrauth.h> + +# if defined(_LIBCPP_OBJECT_FORMAT_MACHO) +# define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__TEXT,__lcxx_override,regular,pure_instructions")]] +// Declare two dummy bytes and give them these special `__asm` values. These values are +// defined by the linker, which means that referring to `&__lcxx_override_start` will +// effectively refer to the address where the section starts (and same for the end). +extern char __start___lcxx_override __asm("section$start$__TEXT$__lcxx_override"); +extern char __stop___lcxx_override __asm("section$end$__TEXT$__lcxx_override"); +# elif defined(_LIBCPP_OBJECT_FORMAT_ELF) +// This is very similar to what we do for Mach-O above. The ELF linker will implicitly define +// variables with those names corresponding to the start and the end of the section. +// +// See https://stackoverflow.com/questions/16552710/how-do-you-get-the-start-and-end-addresses-of-a-custom-elf-section +# define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__lcxx_override")]] +extern char __start___lcxx_override; +extern char __stop___lcxx_override; +# endif + +_LIBCPP_BEGIN_NAMESPACE_STD +template <typename T, T* _Func> +_LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept { + uintptr_t __start = reinterpret_cast<uintptr_t>(&__start___lcxx_override); + uintptr_t __end = reinterpret_cast<uintptr_t>(&__stop___lcxx_override); + uintptr_t __ptr = reinterpret_cast<uintptr_t>(_Func); + + // We must pass a void* to ptrauth_strip since it only accepts a pointer type. Also, in particular, + // we must NOT pass a function pointer, otherwise we will strip the function pointer, and then attempt + // to authenticate and re-sign it when casting it to a uintptr_t again, which will fail because we just + // stripped the function pointer. See rdar://122927845. + __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer)); + + // Finally, the function was overridden if it falls outside of the section's bounds. + return __ptr < __start || __ptr > __end; +} +_LIBCPP_END_NAMESPACE_STD + +# endif // __has_feature(ptrauth_calls) + +#else // defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__)) # define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 0 +# define OVERRIDABLE_FUNCTION [[gnu::weak]] -#endif +#endif // defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__)) #endif // _LIBCPP_SRC_INCLUDE_OVERRIDABLE_FUNCTION_H diff --git a/libcxx/src/new.cpp b/libcxx/src/new.cpp index 0ccc3acad06aa..25bdaea32f57a 100644 --- a/libcxx/src/new.cpp +++ b/libcxx/src/new.cpp @@ -43,7 +43,7 @@ static void* operator_new_impl(std::size_t size) { return p; } -[[gnu::weak]] void* operator new(std::size_t size) _THROW_BAD_ALLOC { +OVERRIDABLE_FUNCTION void* operator new(std::size_t size) _THROW_BAD_ALLOC { void* p = operator_new_impl(size); if (p == nullptr) __throw_bad_alloc_shim(); @@ -74,7 +74,7 @@ static void* operator_new_impl(std::size_t size) { # endif } -[[gnu::weak]] void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); } +OVERRIDABLE_FUNCTION void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); } [[gnu::weak]] void* operator new[](size_t size, const std::nothrow_t&) noexcept { # if !_LIBCPP_HAS_EXCEPTIONS @@ -134,7 +134,7 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm return p; } -[[gnu::weak]] void* operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC { +OVERRIDABLE_FUNCTION void* operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC { void* p = operator_new_aligned_impl(size, alignment); if (p == nullptr) __throw_bad_alloc_shim(); @@ -165,7 +165,7 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm # endif } -[[gnu::weak]] void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC { +OVERRIDABLE_FUNCTION void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC { return ::operator new(size, alignment); } diff --git a/libcxxabi/src/stdlib_new_delete.cpp b/libcxxabi/src/stdlib_new_delete.cpp index 9f538948542c3..164a26335eb38 100644 --- a/libcxxabi/src/stdlib_new_delete.cpp +++ b/libcxxabi/src/stdlib_new_delete.cpp @@ -59,7 +59,7 @@ static void* operator_new_impl(std::size_t size) { return p; } -[[gnu::weak]] void* operator new(std::size_t size) _THROW_BAD_ALLOC { +OVERRIDABLE_FUNCTION void* operator new(std::size_t size) _THROW_BAD_ALLOC { void* p = operator_new_impl(size); if (p == nullptr) __throw_bad_alloc_shim(); @@ -90,7 +90,7 @@ static void* operator_new_impl(std::size_t size) { #endif } -[[gnu::weak]] void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); } +OVERRIDABLE_FUNCTION void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); } [[gnu::weak]] void* operator new[](size_t size, const std::nothrow_t&) noexcept { #if !_LIBCPP_HAS_EXCEPTIONS @@ -150,7 +150,7 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm return p; } -[[gnu::weak]] void* operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC { +OVERRIDABLE_FUNCTION void* operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC { void* p = operator_new_aligned_impl(size, alignment); if (p == nullptr) __throw_bad_alloc_shim(); @@ -181,7 +181,7 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm # endif } -[[gnu::weak]] void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC { +OVERRIDABLE_FUNCTION void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC { return ::operator new(size, alignment); } `````````` </details> https://github.com/llvm/llvm-project/pull/210364 _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
