• 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_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