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 PARTITION_ALLOC_POINTERS_RAW_PTR_HOOKABLE_IMPL_H_ 6 #define PARTITION_ALLOC_POINTERS_RAW_PTR_HOOKABLE_IMPL_H_ 7 8 #include <cstddef> 9 #include <type_traits> 10 11 #include "partition_alloc/partition_alloc_base/compiler_specific.h" 12 #include "partition_alloc/partition_alloc_base/component_export.h" 13 #include "partition_alloc/partition_alloc_base/cxx20_is_constant_evaluated.h" 14 #include "partition_alloc/partition_alloc_buildflags.h" 15 #include "partition_alloc/partition_alloc_forward.h" 16 17 #if !BUILDFLAG(USE_HOOKABLE_RAW_PTR) 18 #error "Included under wrong build option" 19 #endif 20 21 namespace base::internal { 22 23 struct RawPtrHooks { 24 using WrapPtr = void(uintptr_t address); 25 using ReleaseWrappedPtr = void(uintptr_t address); 26 using SafelyUnwrapForDereference = void(uintptr_t address); 27 using SafelyUnwrapForExtraction = void(uintptr_t address); 28 using UnsafelyUnwrapForComparison = void(uintptr_t address); 29 using Advance = void(uintptr_t old_address, uintptr_t new_address); 30 using Duplicate = void(uintptr_t address); 31 using WrapPtrForDuplication = void(uintptr_t address); 32 using UnsafelyUnwrapForDuplication = void(uintptr_t address); 33 34 WrapPtr* wrap_ptr; 35 ReleaseWrappedPtr* release_wrapped_ptr; 36 SafelyUnwrapForDereference* safely_unwrap_for_dereference; 37 SafelyUnwrapForExtraction* safely_unwrap_for_extraction; 38 UnsafelyUnwrapForComparison* unsafely_unwrap_for_comparison; 39 Advance* advance; 40 Duplicate* duplicate; 41 WrapPtrForDuplication* wrap_ptr_for_duplication; 42 UnsafelyUnwrapForDuplication* unsafely_unwrap_for_duplication; 43 }; 44 45 PA_COMPONENT_EXPORT(RAW_PTR) const RawPtrHooks* GetRawPtrHooks(); 46 PA_COMPONENT_EXPORT(RAW_PTR) void InstallRawPtrHooks(const RawPtrHooks*); 47 PA_COMPONENT_EXPORT(RAW_PTR) void ResetRawPtrHooks(); 48 49 template <bool EnableHooks> 50 struct RawPtrHookableImpl { 51 // Since this Impl is used for BRP-ASan, match BRP as closely as possible. 52 static constexpr bool kMustZeroOnConstruct = true; 53 static constexpr bool kMustZeroOnMove = true; 54 static constexpr bool kMustZeroOnDestruct = true; 55 56 // Wraps a pointer. 57 template <typename T> WrapRawPtrRawPtrHookableImpl58 PA_ALWAYS_INLINE static constexpr T* WrapRawPtr(T* ptr) { 59 if (!partition_alloc::internal::base::is_constant_evaluated()) { 60 if (EnableHooks) { 61 GetRawPtrHooks()->wrap_ptr(reinterpret_cast<uintptr_t>(ptr)); 62 } 63 } 64 return ptr; 65 } 66 67 // Notifies the allocator when a wrapped pointer is being removed or replaced. 68 template <typename T> ReleaseWrappedPtrRawPtrHookableImpl69 PA_ALWAYS_INLINE static constexpr void ReleaseWrappedPtr(T* ptr) { 70 if (!partition_alloc::internal::base::is_constant_evaluated()) { 71 if (EnableHooks) { 72 GetRawPtrHooks()->release_wrapped_ptr(reinterpret_cast<uintptr_t>(ptr)); 73 } 74 } 75 } 76 77 // Unwraps the pointer, while asserting that memory hasn't been freed. The 78 // function is allowed to crash on nullptr. 79 template <typename T> SafelyUnwrapPtrForDereferenceRawPtrHookableImpl80 PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForDereference( 81 T* wrapped_ptr) { 82 if (!partition_alloc::internal::base::is_constant_evaluated()) { 83 if (EnableHooks) { 84 GetRawPtrHooks()->safely_unwrap_for_dereference( 85 reinterpret_cast<uintptr_t>(wrapped_ptr)); 86 } 87 } 88 return wrapped_ptr; 89 } 90 91 // Unwraps the pointer, while asserting that memory hasn't been freed. The 92 // function must handle nullptr gracefully. 93 template <typename T> SafelyUnwrapPtrForExtractionRawPtrHookableImpl94 PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForExtraction( 95 T* wrapped_ptr) { 96 if (!partition_alloc::internal::base::is_constant_evaluated()) { 97 if (EnableHooks) { 98 GetRawPtrHooks()->safely_unwrap_for_extraction( 99 reinterpret_cast<uintptr_t>(wrapped_ptr)); 100 } 101 } 102 return wrapped_ptr; 103 } 104 105 // Unwraps the pointer, without making an assertion on whether memory was 106 // freed or not. 107 template <typename T> UnsafelyUnwrapPtrForComparisonRawPtrHookableImpl108 PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForComparison( 109 T* wrapped_ptr) { 110 if (!partition_alloc::internal::base::is_constant_evaluated()) { 111 if (EnableHooks) { 112 GetRawPtrHooks()->unsafely_unwrap_for_comparison( 113 reinterpret_cast<uintptr_t>(wrapped_ptr)); 114 } 115 } 116 return wrapped_ptr; 117 } 118 119 // Upcasts the wrapped pointer. 120 template <typename To, typename From> UpcastRawPtrHookableImpl121 PA_ALWAYS_INLINE static constexpr To* Upcast(From* wrapped_ptr) { 122 static_assert(std::is_convertible_v<From*, To*>, 123 "From must be convertible to To."); 124 // Note, this cast may change the address if upcasting to base that lies in 125 // the middle of the derived object. 126 return wrapped_ptr; 127 } 128 129 // Advance the wrapped pointer by `delta_elems`. 130 template < 131 typename T, 132 typename Z, 133 typename = 134 std::enable_if_t<partition_alloc::internal::is_offset_type<Z>, void>> 135 PA_ALWAYS_INLINE static constexpr T* AdvanceRawPtrHookableImpl136 Advance(T* wrapped_ptr, Z delta_elems, bool /*is_in_pointer_modification*/) { 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>> 153 PA_ALWAYS_INLINE static constexpr T* RetreatRawPtrHookableImpl154 Retreat(T* wrapped_ptr, Z delta_elems, bool /*is_in_pointer_modification*/) { 155 if (!partition_alloc::internal::base::is_constant_evaluated()) { 156 if (EnableHooks) { 157 GetRawPtrHooks()->advance( 158 reinterpret_cast<uintptr_t>(wrapped_ptr), 159 reinterpret_cast<uintptr_t>(wrapped_ptr - delta_elems)); 160 } 161 } 162 return wrapped_ptr - delta_elems; 163 } 164 165 template <typename T> GetDeltaElemsRawPtrHookableImpl166 PA_ALWAYS_INLINE static constexpr ptrdiff_t GetDeltaElems(T* wrapped_ptr1, 167 T* wrapped_ptr2) { 168 return wrapped_ptr1 - wrapped_ptr2; 169 } 170 171 // Returns a copy of a wrapped pointer, without making an assertion on whether 172 // memory was freed or not. 173 template <typename T> DuplicateRawPtrHookableImpl174 PA_ALWAYS_INLINE static constexpr T* Duplicate(T* wrapped_ptr) { 175 if (!partition_alloc::internal::base::is_constant_evaluated()) { 176 if (EnableHooks) { 177 GetRawPtrHooks()->duplicate(reinterpret_cast<uintptr_t>(wrapped_ptr)); 178 } 179 } 180 return wrapped_ptr; 181 } 182 183 // `WrapRawPtrForDuplication` and `UnsafelyUnwrapPtrForDuplication` are used 184 // to create a new raw_ptr<T> from another raw_ptr<T> of a different flavor. 185 template <typename T> WrapRawPtrForDuplicationRawPtrHookableImpl186 PA_ALWAYS_INLINE static constexpr T* WrapRawPtrForDuplication(T* ptr) { 187 if (!partition_alloc::internal::base::is_constant_evaluated()) { 188 if (EnableHooks) { 189 GetRawPtrHooks()->wrap_ptr_for_duplication( 190 reinterpret_cast<uintptr_t>(ptr)); 191 } 192 } 193 return ptr; 194 } 195 196 template <typename T> UnsafelyUnwrapPtrForDuplicationRawPtrHookableImpl197 PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForDuplication( 198 T* wrapped_ptr) { 199 if (!partition_alloc::internal::base::is_constant_evaluated()) { 200 if (EnableHooks) { 201 GetRawPtrHooks()->unsafely_unwrap_for_duplication( 202 reinterpret_cast<uintptr_t>(wrapped_ptr)); 203 } 204 } 205 return wrapped_ptr; 206 } 207 208 template <typename T> TraceRawPtrHookableImpl209 static constexpr void Trace(uint64_t owner_id, T* wrapped_ptr) {} UntraceRawPtrHookableImpl210 static constexpr void Untrace(uint64_t owner_id) {} 211 212 // This is for accounting only, used by unit tests. IncrementSwapCountForTestRawPtrHookableImpl213 PA_ALWAYS_INLINE static constexpr void IncrementSwapCountForTest() {} IncrementLessCountForTestRawPtrHookableImpl214 PA_ALWAYS_INLINE static constexpr void IncrementLessCountForTest() {} 215 }; 216 217 } // namespace base::internal 218 219 #endif // PARTITION_ALLOC_POINTERS_RAW_PTR_HOOKABLE_IMPL_H_ 220