xref: /xnu-8020.101.4/libkern/os/alloc_util.h (revision e7776783b89a353188416a9a346c6cdb4928faad)
1 /*
2  * Copyright (c) 2021 Apple Inc. All rights reserved.
3  *
4  * @APPLE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. Please obtain a copy of the License at
10  * http://www.opensource.apple.com/apsl/ and read it before using this
11  * file.
12  *
13  * The Original Code and all software distributed under the License are
14  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18  * Please see the License for the specific language governing rights and
19  * limitations under the License.
20  *
21  * @APPLE_LICENSE_HEADER_END@
22  */
23 
24 #ifndef _OS_ALLOC_UTIL_H
25 #define _OS_ALLOC_UTIL_H
26 
27 #include <sys/cdefs.h>
28 
29 #ifdef __cplusplus
30 
31 #if __cplusplus >= 201103L
32 
33 extern "C++" {
34 #include <os/cpp_util.h>
35 
36 namespace os {
37 namespace au_detail {
38 /*
39  * This is implemented using constexpr member functions to work around
40  * a compiler bug related to template deduction for __ptrauth-qualified
41  * types (rdar://83481514).
42  *
43  * We use the constexpr variable __p as a parameter to the constexpr
44  * member function; because function parameters strip the __ptrauth
45  * attributes, this allows us to infer the non-__ptrauth qualified
46  * type in the member function template.
47  */
48 template<class PtrTy, class CheckTy>
49 struct is_compatible_ptr {
50 	template<class T>
51 	static constexpr bool
__compute_resultis_compatible_ptr52 	__compute_result(T *p __unused)
53 	{
54 		using _T = os::remove_extent_t<os::remove_const_t<os::remove_volatile_t<T> > >;
55 		using _U = os::remove_extent_t<CheckTy>;
56 		return os::is_same<void, _T>::value || os::is_same<_T, _U>::value;
57 	}
58 
59 	static constexpr os::remove_reference_t<PtrTy> __p = nullptr;
60 	static const bool value = __compute_result(__p);
61 };
62 } // namespace au_detail
63 } // namespace os
64 
65 /*
66  * Type checking macro implementation (C++)
67  */
68 #define os_is_compatible_ptr_impl(ptr, type) \
69 	os::au_detail::is_compatible_ptr<decltype(ptr), type>::value
70 } // "extern C++"
71 
72 #else  /* __cplusplus >= 201103L */
73 
74 #define os_is_compatible_ptr_impl(ptr, type) 1
75 
76 #endif /* __cplusplus >= 201103L */
77 
78 #else  /* __cplusplus */
79 
80 /*
81  * Type checking macro implementation (C):
82  * wildcard any void pointer type, and check all other types using
83  * __builtin_types_compatible_p.
84  */
85 #define os_is_compatible_ptr_impl(ptr, type) _Generic((ptr),              \
86 	void *: 1,                                                         \
87 	const void *: 1,                                                   \
88 	const volatile void *: 1,                                          \
89 	volatile void * const: 1,                                          \
90 	default: (__builtin_types_compatible_p(__typeof__(*(ptr)), type)))
91 
92 #endif /* __cplusplus */
93 
94 /*!
95  * @macro os_is_compatible_ptr
96  *
97  * @abstract
98  * Check at compile time that a pointer is compatible with a given type.
99  *
100  * @discussion
101  * The goal of this macro is to check that the type pointed to by @c ptr
102  * is compatible with the given type @c type.
103  *
104  * The underlying implementation differs between C and C++. In C, we
105  * rely on the concept of compatible types, and we implement the check
106  * using __builtin_types_compatible_p. In C++, we check type equivalence
107  * using type traits.
108  *
109  * NOTE: This macro is a no-op for C++98 (no type check is performed).
110  *
111  * @param ptr           the pointer whose type needs to be checked.
112  * @param type          the type which the pointer will be checked against.
113  */
114 #define os_is_compatible_ptr(ptr, type)  os_is_compatible_ptr_impl(ptr, type)
115 
116 /*!
117  * @macro os_is_ptr_like
118  *
119  * @abstract
120  * Tell whether the given expression resembles a pointer.
121  *
122  * @discussion
123  * When pointer bounds are enabled, only types that are actually classified
124  * as pointers will be considered pointer-like. Otherwise, any pointer-sized
125  * type will be considered pointer-like.
126  *
127  * @param P             the expression to be checked
128  */
129 #if __has_ptrcheck
130 #define os_is_ptr_like(P) (__builtin_classify_type(P) == 5)
131 #else  /* __has_ptrcheck */
132 #define os_is_ptr_like(P) (sizeof(P) == sizeof(void *))
133 #endif /* __has_ptrcheck */
134 
135 /*!
136  * @macro os_ptr_load_and_erase
137  *
138  * @abstract
139  * Load the value of @c elem into a temporary, set @c elem to NULL, and
140  * return the value.
141  *
142  * @param elem          the pointer whose value will be taken, and which will
143  *                      be set to NULL.
144  */
145 #define os_ptr_load_and_erase(elem) ({                           \
146 	_Static_assert(os_is_ptr_like(elem),                      \
147 	    "elem isn't pointer sized");                          \
148 	__auto_type __eptr = &(elem);                             \
149 	__auto_type __elem = *__eptr;                             \
150 	_Pragma("clang diagnostic push")                          \
151 	_Pragma("clang diagnostic ignored \"-Wold-style-cast\"")  \
152 	*__eptr = (__typeof__(__elem))NULL;                       \
153 	_Pragma("clang diagnostic pop")                           \
154 	__elem;                                                   \
155 })
156 
157 #endif /* _OS_ALLOC_UTIL_H */
158