Skip to content

Commit b9c5a18

Browse files
ldionnekovdan01
authored andcommitted
[libc++] Upstream ptrauth support in libc++ and libc++abi (llvm#84573)
This is an exact upstreaming of the downstream diff. Minor simplifications can be made in the future but upstreaming as-is will make it easier for us to deal with downstream merge conflicts. Partially fixes llvm#83805
1 parent 7372433 commit b9c5a18

File tree

3 files changed

+163
-2
lines changed

3 files changed

+163
-2
lines changed

libcxx/include/typeinfo

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,19 @@ struct __type_info_implementations {
299299
__impl;
300300
};
301301

302-
class _LIBCPP_EXPORTED_FROM_ABI type_info
303-
{
302+
# if defined(__arm64__) && __has_cpp_attribute(clang::ptrauth_vtable_pointer)
303+
# if __has_feature(ptrauth_type_info_discriminated_vtable_pointer)
304+
# define _LIBCPP_TYPE_INFO_VTABLE_POINTER_AUTH \
305+
[[clang::ptrauth_vtable_pointer(process_independent, address_discrimination, type_discrimination)]]
306+
# else
307+
# define _LIBCPP_TYPE_INFO_VTABLE_POINTER_AUTH \
308+
[[clang::ptrauth_vtable_pointer(process_independent, no_address_discrimination, no_extra_discrimination)]]
309+
# endif
310+
# else
311+
# define _LIBCPP_TYPE_INFO_VTABLE_POINTER_AUTH
312+
# endif
313+
314+
class _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_TYPE_INFO_VTABLE_POINTER_AUTH type_info {
304315
type_info& operator=(const type_info&);
305316
type_info(const type_info&);
306317

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// -*- C++ -*-
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#ifndef _LIBCPP_SRC_INCLUDE_OVERRIDABLE_FUNCTION_H
11+
#define _LIBCPP_SRC_INCLUDE_OVERRIDABLE_FUNCTION_H
12+
13+
#include <__config>
14+
#include <cstdint>
15+
16+
#if defined(__arm64e__) && __has_feature(ptrauth_calls)
17+
# include <ptrauth.h>
18+
#endif
19+
20+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
21+
# pragma GCC system_header
22+
#endif
23+
24+
//
25+
// This file provides the std::__is_function_overridden utility, which allows checking
26+
// whether an overridable function (typically a weak symbol) like `operator new`
27+
// has been overridden by a user or not.
28+
//
29+
// This is a low-level utility which does not work on all platforms, since it needs
30+
// to make assumptions about the object file format in use. Furthermore, it requires
31+
// the "base definition" of the function (the one we want to check whether it has been
32+
// overridden) to be annotated with the _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE macro.
33+
//
34+
// This currently works with Mach-O files (used on Darwin) and with ELF files (used on Linux
35+
// and others). On platforms where we know how to implement this detection, the macro
36+
// _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION is defined to 1, and it is defined to 0 on
37+
// other platforms. The _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE macro is defined to
38+
// nothing on unsupported platforms so that it can be used to decorate functions regardless
39+
// of whether detection is actually supported.
40+
//
41+
// How does this work?
42+
// -------------------
43+
//
44+
// Let's say we want to check whether a weak function `f` has been overridden by the user.
45+
// The general mechanism works by placing `f`'s definition (in the libc++ built library)
46+
// inside a special section, which we do using the `__section__` attribute via the
47+
// _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE macro.
48+
//
49+
// Then, when comes the time to check whether the function has been overridden, we take
50+
// the address of the function and we check whether it falls inside the special function
51+
// we created. This can be done by finding pointers to the start and the end of the section
52+
// (which is done differently for ELF and Mach-O), and then checking whether `f` falls
53+
// within those bounds. If it falls within those bounds, then `f` is still inside the
54+
// special section and so it is the version we defined in the libc++ built library, i.e.
55+
// it was not overridden. Otherwise, it was overridden by the user because it falls
56+
// outside of the section.
57+
//
58+
// Important note
59+
// --------------
60+
//
61+
// This mechanism should never be used outside of the libc++ built library. In particular,
62+
// attempting to use this within the libc++ headers will not work at all because we don't
63+
// want to be defining special sections inside user's executables which use our headers.
64+
// This is provided inside libc++'s include tree solely to make it easier to share with
65+
// libc++abi, which needs the same mechanism.
66+
//
67+
68+
#if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
69+
70+
# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
71+
# define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE \
72+
__attribute__((__section__("__TEXT,__lcxx_override,regular,pure_instructions")))
73+
74+
_LIBCPP_BEGIN_NAMESPACE_STD
75+
template <class _Ret, class... _Args>
76+
_LIBCPP_HIDE_FROM_ABI bool __is_function_overridden(_Ret (*__fptr)(_Args...)) noexcept {
77+
// Declare two dummy bytes and give them these special `__asm` values. These values are
78+
// defined by the linker, which means that referring to `&__lcxx_override_start` will
79+
// effectively refer to the address where the section starts (and same for the end).
80+
extern char __lcxx_override_start __asm("section$start$__TEXT$__lcxx_override");
81+
extern char __lcxx_override_end __asm("section$end$__TEXT$__lcxx_override");
82+
83+
// Now get a uintptr_t out of these locations, and out of the function pointer.
84+
uintptr_t __start = reinterpret_cast<uintptr_t>(&__lcxx_override_start);
85+
uintptr_t __end = reinterpret_cast<uintptr_t>(&__lcxx_override_end);
86+
uintptr_t __ptr = reinterpret_cast<uintptr_t>(__fptr);
87+
88+
#if defined(__arm64e__) && __has_feature(ptrauth_calls)
89+
// We must pass a void* to ptrauth_strip since it only accepts a pointer type. Also, in particular,
90+
// we must NOT pass a function pointer, otherwise we will strip the function pointer, and then attempt
91+
// to authenticate and re-sign it when casting it to a uintptr_t again, which will fail because we just
92+
// stripped the function pointer. See rdar://122927845.
93+
__ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
94+
#endif
95+
96+
// Finally, the function was overridden if it falls outside of the section's bounds.
97+
return __ptr < __start || __ptr > __end;
98+
}
99+
_LIBCPP_END_NAMESPACE_STD
100+
101+
#elif defined(_LIBCPP_OBJECT_FORMAT_ELF)
102+
103+
# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
104+
# define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE __attribute__((__section__("__lcxx_override")))
105+
106+
// This is very similar to what we do for Mach-O above. The ELF linker will implicitly define
107+
// variables with those names corresponding to the start and the end of the section.
108+
//
109+
// See https://stackoverflow.com/questions/16552710/how-do-you-get-the-start-and-end-addresses-of-a-custom-elf-section
110+
extern char __start___lcxx_override;
111+
extern char __stop___lcxx_override;
112+
113+
_LIBCPP_BEGIN_NAMESPACE_STD
114+
template <class _Ret, class... _Args>
115+
_LIBCPP_HIDE_FROM_ABI bool __is_function_overridden(_Ret (*__fptr)(_Args...)) noexcept {
116+
uintptr_t __start = reinterpret_cast<uintptr_t>(&__start___lcxx_override);
117+
uintptr_t __end = reinterpret_cast<uintptr_t>(&__stop___lcxx_override);
118+
uintptr_t __ptr = reinterpret_cast<uintptr_t>(__fptr);
119+
120+
return __ptr < __start || __ptr > __end;
121+
}
122+
_LIBCPP_END_NAMESPACE_STD
123+
124+
#else
125+
126+
# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 0
127+
# define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE /* nothing */
128+
129+
#endif
130+
131+
#endif // _LIBCPP_SRC_INCLUDE_OVERRIDABLE_FUNCTION_H

libcxxabi/src/private_typeinfo.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,21 @@
5151
#include <atomic>
5252
#endif
5353

54+
#if __has_feature(ptrauth_calls)
55+
#include <ptrauth.h>
56+
#endif
57+
58+
59+
template<typename T>
60+
static inline
61+
T *
62+
get_vtable(T *vtable) {
63+
#if __has_feature(ptrauth_calls)
64+
vtable = ptrauth_strip(vtable, ptrauth_key_cxx_vtable_pointer);
65+
#endif
66+
return vtable;
67+
}
68+
5469
static inline
5570
bool
5671
is_equal(const std::type_info* x, const std::type_info* y, bool use_strcmp)
@@ -103,6 +118,7 @@ void dyn_cast_get_derived_info(derived_object_info* info, const void* static_ptr
103118
info->dynamic_type = *(reinterpret_cast<const __class_type_info* const*>(ptr_to_ti_proxy));
104119
#else
105120
void **vtable = *static_cast<void ** const *>(static_ptr);
121+
vtable = get_vtable(vtable);
106122
info->offset_to_derived = reinterpret_cast<ptrdiff_t>(vtable[-2]);
107123
info->dynamic_ptr = static_cast<const char*>(static_ptr) + info->offset_to_derived;
108124
info->dynamic_type = static_cast<const __class_type_info*>(vtable[-1]);
@@ -561,6 +577,7 @@ __base_class_type_info::has_unambiguous_public_base(__dynamic_cast_info* info,
561577
offset_to_base = __offset_flags >> __offset_shift;
562578
if (is_virtual) {
563579
const char* vtable = *static_cast<const char* const*>(adjustedPtr);
580+
vtable = get_vtable(vtable);
564581
offset_to_base = update_offset_to_base(vtable, offset_to_base);
565582
}
566583
} else if (!is_virtual) {
@@ -1501,6 +1518,7 @@ __base_class_type_info::search_above_dst(__dynamic_cast_info* info,
15011518
if (__offset_flags & __virtual_mask)
15021519
{
15031520
const char* vtable = *static_cast<const char*const*>(current_ptr);
1521+
vtable = get_vtable(vtable);
15041522
offset_to_base = update_offset_to_base(vtable, offset_to_base);
15051523
}
15061524
__base_type->search_above_dst(info, dst_ptr,
@@ -1521,6 +1539,7 @@ __base_class_type_info::search_below_dst(__dynamic_cast_info* info,
15211539
if (__offset_flags & __virtual_mask)
15221540
{
15231541
const char* vtable = *static_cast<const char*const*>(current_ptr);
1542+
vtable = get_vtable(vtable);
15241543
offset_to_base = update_offset_to_base(vtable, offset_to_base);
15251544
}
15261545
__base_type->search_below_dst(info,

0 commit comments

Comments
 (0)