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