• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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