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_POINTERS_RAW_PTR_HOOKABLE_IMPL_H_ 6 #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_HOOKABLE_IMPL_H_ 7 8 #include <stddef.h> 9 10 #include <type_traits> 11 12 #include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" 13 #include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" 14 #include "base/allocator/partition_allocator/partition_alloc_base/cxx20_is_constant_evaluated.h" 15 #include "base/allocator/partition_allocator/partition_alloc_buildflags.h" 16 #include "base/allocator/partition_allocator/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 33 WrapPtr* wrap_ptr; 34 ReleaseWrappedPtr* release_wrapped_ptr; 35 SafelyUnwrapForDereference* safely_unwrap_for_dereference; 36 SafelyUnwrapForExtraction* safely_unwrap_for_extraction; 37 UnsafelyUnwrapForComparison* unsafely_unwrap_for_comparison; 38 Advance* advance; 39 Duplicate* duplicate; 40 }; 41 42 PA_COMPONENT_EXPORT(RAW_PTR) const RawPtrHooks* GetRawPtrHooks(); 43 PA_COMPONENT_EXPORT(RAW_PTR) void InstallRawPtrHooks(const RawPtrHooks*); 44 PA_COMPONENT_EXPORT(RAW_PTR) void ResetRawPtrHooks(); 45 46 struct RawPtrHookableImpl { 47 // Since this Impl is used for BRP-ASan, match BRP as closely as possible. 48 static constexpr bool kMustZeroOnInit = true; 49 static constexpr bool kMustZeroOnMove = true; 50 static constexpr bool kMustZeroOnDestruct = true; 51 52 // Wraps a pointer. 53 template <typename T> WrapRawPtrRawPtrHookableImpl54 PA_ALWAYS_INLINE static constexpr T* WrapRawPtr(T* ptr) { 55 if (!partition_alloc::internal::base::is_constant_evaluated()) { 56 GetRawPtrHooks()->wrap_ptr(reinterpret_cast<uintptr_t>(ptr)); 57 } 58 return ptr; 59 } 60 61 // Notifies the allocator when a wrapped pointer is being removed or replaced. 62 template <typename T> ReleaseWrappedPtrRawPtrHookableImpl63 PA_ALWAYS_INLINE static constexpr void ReleaseWrappedPtr(T* ptr) { 64 if (!partition_alloc::internal::base::is_constant_evaluated()) { 65 GetRawPtrHooks()->release_wrapped_ptr(reinterpret_cast<uintptr_t>(ptr)); 66 } 67 } 68 69 // Unwraps the pointer, while asserting that memory hasn't been freed. The 70 // function is allowed to crash on nullptr. 71 template <typename T> SafelyUnwrapPtrForDereferenceRawPtrHookableImpl72 PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForDereference( 73 T* wrapped_ptr) { 74 if (!partition_alloc::internal::base::is_constant_evaluated()) { 75 GetRawPtrHooks()->safely_unwrap_for_dereference( 76 reinterpret_cast<uintptr_t>(wrapped_ptr)); 77 } 78 return wrapped_ptr; 79 } 80 81 // Unwraps the pointer, while asserting that memory hasn't been freed. The 82 // function must handle nullptr gracefully. 83 template <typename T> SafelyUnwrapPtrForExtractionRawPtrHookableImpl84 PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForExtraction( 85 T* wrapped_ptr) { 86 if (!partition_alloc::internal::base::is_constant_evaluated()) { 87 GetRawPtrHooks()->safely_unwrap_for_extraction( 88 reinterpret_cast<uintptr_t>(wrapped_ptr)); 89 } 90 return wrapped_ptr; 91 } 92 93 // Unwraps the pointer, without making an assertion on whether memory was 94 // freed or not. 95 template <typename T> UnsafelyUnwrapPtrForComparisonRawPtrHookableImpl96 PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForComparison( 97 T* wrapped_ptr) { 98 if (!partition_alloc::internal::base::is_constant_evaluated()) { 99 GetRawPtrHooks()->unsafely_unwrap_for_comparison( 100 reinterpret_cast<uintptr_t>(wrapped_ptr)); 101 } 102 return wrapped_ptr; 103 } 104 105 // Upcasts the wrapped pointer. 106 template <typename To, typename From> UpcastRawPtrHookableImpl107 PA_ALWAYS_INLINE static constexpr To* Upcast(From* wrapped_ptr) { 108 static_assert(std::is_convertible<From*, To*>::value, 109 "From must be convertible to To."); 110 // Note, this cast may change the address if upcasting to base that lies in 111 // the middle of the derived object. 112 return wrapped_ptr; 113 } 114 115 // Advance the wrapped pointer by `delta_elems`. 116 template < 117 typename T, 118 typename Z, 119 typename = 120 std::enable_if_t<partition_alloc::internal::is_offset_type<Z>, void>> AdvanceRawPtrHookableImpl121 PA_ALWAYS_INLINE static constexpr T* Advance(T* wrapped_ptr, Z delta_elems) { 122 if (!partition_alloc::internal::base::is_constant_evaluated()) { 123 GetRawPtrHooks()->advance( 124 reinterpret_cast<uintptr_t>(wrapped_ptr), 125 reinterpret_cast<uintptr_t>(wrapped_ptr + delta_elems)); 126 } 127 return wrapped_ptr + delta_elems; 128 } 129 130 // Retreat 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>> RetreatRawPtrHookableImpl136 PA_ALWAYS_INLINE static constexpr T* Retreat(T* wrapped_ptr, Z delta_elems) { 137 if (!partition_alloc::internal::base::is_constant_evaluated()) { 138 GetRawPtrHooks()->advance( 139 reinterpret_cast<uintptr_t>(wrapped_ptr), 140 reinterpret_cast<uintptr_t>(wrapped_ptr - delta_elems)); 141 } 142 return wrapped_ptr - delta_elems; 143 } 144 145 template <typename T> GetDeltaElemsRawPtrHookableImpl146 PA_ALWAYS_INLINE static constexpr ptrdiff_t GetDeltaElems(T* wrapped_ptr1, 147 T* wrapped_ptr2) { 148 return wrapped_ptr1 - wrapped_ptr2; 149 } 150 151 // Returns a copy of a wrapped pointer, without making an assertion on whether 152 // memory was freed or not. 153 template <typename T> DuplicateRawPtrHookableImpl154 PA_ALWAYS_INLINE static constexpr T* Duplicate(T* wrapped_ptr) { 155 if (!partition_alloc::internal::base::is_constant_evaluated()) { 156 GetRawPtrHooks()->duplicate(reinterpret_cast<uintptr_t>(wrapped_ptr)); 157 } 158 return wrapped_ptr; 159 } 160 161 // `WrapRawPtrForDuplication` and `UnsafelyUnwrapPtrForDuplication` are used 162 // to create a new raw_ptr<T> from another raw_ptr<T> of a different flavor. 163 template <typename T> WrapRawPtrForDuplicationRawPtrHookableImpl164 PA_ALWAYS_INLINE static constexpr T* WrapRawPtrForDuplication(T* ptr) { 165 return ptr; 166 } 167 168 template <typename T> UnsafelyUnwrapPtrForDuplicationRawPtrHookableImpl169 PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForDuplication( 170 T* wrapped_ptr) { 171 return wrapped_ptr; 172 } 173 174 // This is for accounting only, used by unit tests. IncrementSwapCountForTestRawPtrHookableImpl175 PA_ALWAYS_INLINE static constexpr void IncrementSwapCountForTest() {} IncrementLessCountForTestRawPtrHookableImpl176 PA_ALWAYS_INLINE static constexpr void IncrementLessCountForTest() {} 177 PA_ALWAYS_INLINE static constexpr void IncrementPointerToMemberOperatorCountForTestRawPtrHookableImpl178 IncrementPointerToMemberOperatorCountForTest() {} 179 }; 180 181 } // namespace base::internal 182 183 #endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_HOOKABLE_IMPL_H_ 184