1 // Copyright 2023 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_POINTERS_RAW_PTR_HOOKABLE_IMPL_H_ 6 #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_POINTERS_RAW_PTR_HOOKABLE_IMPL_H_ 7 8 #include <stddef.h> 9 10 #include <type_traits> 11 12 #include "partition_alloc/partition_alloc_base/compiler_specific.h" 13 #include "partition_alloc/partition_alloc_base/component_export.h" 14 #include "partition_alloc/partition_alloc_base/cxx20_is_constant_evaluated.h" 15 #include "partition_alloc/partition_alloc_buildflags.h" 16 #include "partition_alloc/partition_alloc_forward.h" 17 18 #if !BUILDFLAG(USE_HOOKABLE_RAW_PTR) 19 #error "Included under wrong build option" 20 #endif 21 22 namespace base::internal { 23 24 struct RawPtrHooks { 25 using WrapPtr = void(uintptr_t address); 26 using ReleaseWrappedPtr = void(uintptr_t address); 27 using SafelyUnwrapForDereference = void(uintptr_t address); 28 using SafelyUnwrapForExtraction = void(uintptr_t address); 29 using UnsafelyUnwrapForComparison = void(uintptr_t address); 30 using Advance = void(uintptr_t old_address, uintptr_t new_address); 31 using Duplicate = void(uintptr_t address); 32 using WrapPtrForDuplication = void(uintptr_t address); 33 using UnsafelyUnwrapForDuplication = void(uintptr_t address); 34 35 WrapPtr* wrap_ptr; 36 ReleaseWrappedPtr* release_wrapped_ptr; 37 SafelyUnwrapForDereference* safely_unwrap_for_dereference; 38 SafelyUnwrapForExtraction* safely_unwrap_for_extraction; 39 UnsafelyUnwrapForComparison* unsafely_unwrap_for_comparison; 40 Advance* advance; 41 Duplicate* duplicate; 42 WrapPtrForDuplication* wrap_ptr_for_duplication; 43 UnsafelyUnwrapForDuplication* unsafely_unwrap_for_duplication; 44 }; 45 46 PA_COMPONENT_EXPORT(RAW_PTR) const RawPtrHooks* GetRawPtrHooks(); 47 PA_COMPONENT_EXPORT(RAW_PTR) void InstallRawPtrHooks(const RawPtrHooks*); 48 PA_COMPONENT_EXPORT(RAW_PTR) void ResetRawPtrHooks(); 49 50 template <bool EnableHooks> 51 struct RawPtrHookableImpl { 52 // Since this Impl is used for BRP-ASan, match BRP as closely as possible. 53 static constexpr bool kMustZeroOnConstruct = true; 54 static constexpr bool kMustZeroOnMove = true; 55 static constexpr bool kMustZeroOnDestruct = true; 56 57 // Wraps a pointer. 58 template <typename T> WrapRawPtrRawPtrHookableImpl59 PA_ALWAYS_INLINE static constexpr T* WrapRawPtr(T* ptr) { 60 if (!partition_alloc::internal::base::is_constant_evaluated()) { 61 if (EnableHooks) { 62 GetRawPtrHooks()->wrap_ptr(reinterpret_cast<uintptr_t>(ptr)); 63 } 64 } 65 return ptr; 66 } 67 68 // Notifies the allocator when a wrapped pointer is being removed or replaced. 69 template <typename T> ReleaseWrappedPtrRawPtrHookableImpl70 PA_ALWAYS_INLINE static constexpr void ReleaseWrappedPtr(T* ptr) { 71 if (!partition_alloc::internal::base::is_constant_evaluated()) { 72 if (EnableHooks) { 73 GetRawPtrHooks()->release_wrapped_ptr(reinterpret_cast<uintptr_t>(ptr)); 74 } 75 } 76 } 77 78 // Unwraps the pointer, while asserting that memory hasn't been freed. The 79 // function is allowed to crash on nullptr. 80 template <typename T> SafelyUnwrapPtrForDereferenceRawPtrHookableImpl81 PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForDereference( 82 T* wrapped_ptr) { 83 if (!partition_alloc::internal::base::is_constant_evaluated()) { 84 if (EnableHooks) { 85 GetRawPtrHooks()->safely_unwrap_for_dereference( 86 reinterpret_cast<uintptr_t>(wrapped_ptr)); 87 } 88 } 89 return wrapped_ptr; 90 } 91 92 // Unwraps the pointer, while asserting that memory hasn't been freed. The 93 // function must handle nullptr gracefully. 94 template <typename T> SafelyUnwrapPtrForExtractionRawPtrHookableImpl95 PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForExtraction( 96 T* wrapped_ptr) { 97 if (!partition_alloc::internal::base::is_constant_evaluated()) { 98 if (EnableHooks) { 99 GetRawPtrHooks()->safely_unwrap_for_extraction( 100 reinterpret_cast<uintptr_t>(wrapped_ptr)); 101 } 102 } 103 return wrapped_ptr; 104 } 105 106 // Unwraps the pointer, without making an assertion on whether memory was 107 // freed or not. 108 template <typename T> UnsafelyUnwrapPtrForComparisonRawPtrHookableImpl109 PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForComparison( 110 T* wrapped_ptr) { 111 if (!partition_alloc::internal::base::is_constant_evaluated()) { 112 if (EnableHooks) { 113 GetRawPtrHooks()->unsafely_unwrap_for_comparison( 114 reinterpret_cast<uintptr_t>(wrapped_ptr)); 115 } 116 } 117 return wrapped_ptr; 118 } 119 120 // Upcasts the wrapped pointer. 121 template <typename To, typename From> UpcastRawPtrHookableImpl122 PA_ALWAYS_INLINE static constexpr To* Upcast(From* wrapped_ptr) { 123 static_assert(std::is_convertible_v<From*, To*>, 124 "From must be convertible to To."); 125 // Note, this cast may change the address if upcasting to base that lies in 126 // the middle of the derived object. 127 return wrapped_ptr; 128 } 129 130 // Advance the wrapped pointer by `delta_elems`. 131 template < 132 typename T, 133 typename Z, 134 typename = 135 std::enable_if_t<partition_alloc::internal::is_offset_type<Z>, void>> AdvanceRawPtrHookableImpl136 PA_ALWAYS_INLINE static constexpr T* Advance(T* wrapped_ptr, Z delta_elems) { 137 if (!partition_alloc::internal::base::is_constant_evaluated()) { 138 if (EnableHooks) { 139 GetRawPtrHooks()->advance( 140 reinterpret_cast<uintptr_t>(wrapped_ptr), 141 reinterpret_cast<uintptr_t>(wrapped_ptr + delta_elems)); 142 } 143 } 144 return wrapped_ptr + delta_elems; 145 } 146 147 // Retreat the wrapped pointer by `delta_elems`. 148 template < 149 typename T, 150 typename Z, 151 typename = 152 std::enable_if_t<partition_alloc::internal::is_offset_type<Z>, void>> RetreatRawPtrHookableImpl153 PA_ALWAYS_INLINE static constexpr T* Retreat(T* wrapped_ptr, Z delta_elems) { 154 if (!partition_alloc::internal::base::is_constant_evaluated()) { 155 if (EnableHooks) { 156 GetRawPtrHooks()->advance( 157 reinterpret_cast<uintptr_t>(wrapped_ptr), 158 reinterpret_cast<uintptr_t>(wrapped_ptr - delta_elems)); 159 } 160 } 161 return wrapped_ptr - delta_elems; 162 } 163 164 template <typename T> GetDeltaElemsRawPtrHookableImpl165 PA_ALWAYS_INLINE static constexpr ptrdiff_t GetDeltaElems(T* wrapped_ptr1, 166 T* wrapped_ptr2) { 167 return wrapped_ptr1 - wrapped_ptr2; 168 } 169 170 // Returns a copy of a wrapped pointer, without making an assertion on whether 171 // memory was freed or not. 172 template <typename T> DuplicateRawPtrHookableImpl173 PA_ALWAYS_INLINE static constexpr T* Duplicate(T* wrapped_ptr) { 174 if (!partition_alloc::internal::base::is_constant_evaluated()) { 175 if (EnableHooks) { 176 GetRawPtrHooks()->duplicate(reinterpret_cast<uintptr_t>(wrapped_ptr)); 177 } 178 } 179 return wrapped_ptr; 180 } 181 182 // `WrapRawPtrForDuplication` and `UnsafelyUnwrapPtrForDuplication` are used 183 // to create a new raw_ptr<T> from another raw_ptr<T> of a different flavor. 184 template <typename T> WrapRawPtrForDuplicationRawPtrHookableImpl185 PA_ALWAYS_INLINE static constexpr T* WrapRawPtrForDuplication(T* ptr) { 186 if (!partition_alloc::internal::base::is_constant_evaluated()) { 187 if (EnableHooks) { 188 GetRawPtrHooks()->wrap_ptr_for_duplication( 189 reinterpret_cast<uintptr_t>(ptr)); 190 } 191 } 192 return ptr; 193 } 194 195 template <typename T> UnsafelyUnwrapPtrForDuplicationRawPtrHookableImpl196 PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForDuplication( 197 T* wrapped_ptr) { 198 if (!partition_alloc::internal::base::is_constant_evaluated()) { 199 if (EnableHooks) { 200 GetRawPtrHooks()->unsafely_unwrap_for_duplication( 201 reinterpret_cast<uintptr_t>(wrapped_ptr)); 202 } 203 } 204 return wrapped_ptr; 205 } 206 207 // This is for accounting only, used by unit tests. IncrementSwapCountForTestRawPtrHookableImpl208 PA_ALWAYS_INLINE static constexpr void IncrementSwapCountForTest() {} IncrementLessCountForTestRawPtrHookableImpl209 PA_ALWAYS_INLINE static constexpr void IncrementLessCountForTest() {} 210 }; 211 212 } // namespace base::internal 213 214 #endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_POINTERS_RAW_PTR_HOOKABLE_IMPL_H_ 215