• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 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_DISPATCHER_INTERNAL_DISPATCHER_INTERNAL_H_
6 #define BASE_ALLOCATOR_DISPATCHER_INTERNAL_DISPATCHER_INTERNAL_H_
7 
8 #include "base/allocator/dispatcher/configuration.h"
9 #include "base/allocator/dispatcher/internal/dispatch_data.h"
10 #include "base/allocator/dispatcher/internal/tools.h"
11 #include "base/allocator/dispatcher/memory_tagging.h"
12 #include "base/allocator/dispatcher/notification_data.h"
13 #include "base/allocator/dispatcher/subsystem.h"
14 #include "base/check.h"
15 #include "base/compiler_specific.h"
16 #include "partition_alloc/buildflags.h"
17 
18 #if PA_BUILDFLAG(USE_PARTITION_ALLOC)
19 #include "partition_alloc/partition_alloc_allocation_data.h"  // nogncheck
20 #endif
21 
22 #if PA_BUILDFLAG(USE_ALLOCATOR_SHIM)
23 #include "partition_alloc/shim/allocator_shim.h"
24 #endif
25 
26 #include <tuple>
27 
28 namespace base::allocator::dispatcher::internal {
29 
30 #if PA_BUILDFLAG(USE_ALLOCATOR_SHIM)
31 using allocator_shim::AllocatorDispatch;
32 #endif
33 
34 template <typename CheckObserverPredicate,
35           typename... ObserverTypes,
36           size_t... Indices>
PerformObserverCheck(const std::tuple<ObserverTypes...> & observers,std::index_sequence<Indices...>,CheckObserverPredicate check_observer)37 void inline PerformObserverCheck(const std::tuple<ObserverTypes...>& observers,
38                                  std::index_sequence<Indices...>,
39                                  CheckObserverPredicate check_observer) {
40   ([](bool b) { DCHECK(b); }(check_observer(std::get<Indices>(observers))),
41    ...);
42 }
43 
44 template <typename... ObserverTypes, size_t... Indices>
PerformAllocationNotification(const std::tuple<ObserverTypes...> & observers,std::index_sequence<Indices...>,const AllocationNotificationData & notification_data)45 ALWAYS_INLINE void PerformAllocationNotification(
46     const std::tuple<ObserverTypes...>& observers,
47     std::index_sequence<Indices...>,
48     const AllocationNotificationData& notification_data) {
49   ((std::get<Indices>(observers)->OnAllocation(notification_data)), ...);
50 }
51 
52 template <typename... ObserverTypes, size_t... Indices>
PerformFreeNotification(const std::tuple<ObserverTypes...> & observers,std::index_sequence<Indices...>,const FreeNotificationData & notification_data)53 ALWAYS_INLINE void PerformFreeNotification(
54     const std::tuple<ObserverTypes...>& observers,
55     std::index_sequence<Indices...>,
56     const FreeNotificationData& notification_data) {
57   ((std::get<Indices>(observers)->OnFree(notification_data)), ...);
58 }
59 
60 // DispatcherImpl provides hooks into the various memory subsystems. These hooks
61 // are responsible for dispatching any notification to the observers.
62 // In order to provide as many information on the exact type of the observer and
63 // prevent any conditional jumps in the hot allocation path, observers are
64 // stored in a std::tuple. DispatcherImpl performs a CHECK at initialization
65 // time to ensure they are valid.
66 template <typename... ObserverTypes>
67 struct DispatcherImpl {
68   using AllObservers = std::index_sequence_for<ObserverTypes...>;
69 
70   template <std::enable_if_t<
71                 internal::LessEqual(sizeof...(ObserverTypes),
72                                     configuration::kMaximumNumberOfObservers),
73                 bool> = true>
GetNotificationHooksDispatcherImpl74   static DispatchData GetNotificationHooks(
75       std::tuple<ObserverTypes*...> observers) {
76     s_observers = std::move(observers);
77 
78     PerformObserverCheck(s_observers, AllObservers{}, IsValidObserver{});
79 
80     return CreateDispatchData();
81   }
82 
83  private:
CreateDispatchDataDispatcherImpl84   static DispatchData CreateDispatchData() {
85     return DispatchData()
86 #if PA_BUILDFLAG(USE_PARTITION_ALLOC)
87         .SetAllocationObserverHooks(&PartitionAllocatorAllocationHook,
88                                     &PartitionAllocatorFreeHook)
89 #endif
90 #if PA_BUILDFLAG(USE_ALLOCATOR_SHIM)
91         .SetAllocatorDispatch(&allocator_dispatch_)
92 #endif
93         ;
94   }
95 
96 #if PA_BUILDFLAG(USE_PARTITION_ALLOC)
PartitionAllocatorAllocationHookDispatcherImpl97   static void PartitionAllocatorAllocationHook(
98       const partition_alloc::AllocationNotificationData& pa_notification_data) {
99     AllocationNotificationData dispatcher_notification_data(
100         pa_notification_data.address(), pa_notification_data.size(),
101         pa_notification_data.type_name(),
102         AllocationSubsystem::kPartitionAllocator);
103 
104 #if PA_BUILDFLAG(HAS_MEMORY_TAGGING)
105     dispatcher_notification_data.SetMteReportingMode(
106         ConvertToMTEMode(pa_notification_data.mte_reporting_mode()));
107 #endif
108 
109     DoNotifyAllocation(dispatcher_notification_data);
110   }
111 
PartitionAllocatorFreeHookDispatcherImpl112   static void PartitionAllocatorFreeHook(
113       const partition_alloc::FreeNotificationData& pa_notification_data) {
114     FreeNotificationData dispatcher_notification_data(
115         pa_notification_data.address(),
116         AllocationSubsystem::kPartitionAllocator);
117 
118 #if PA_BUILDFLAG(HAS_MEMORY_TAGGING)
119     dispatcher_notification_data.SetMteReportingMode(
120         ConvertToMTEMode(pa_notification_data.mte_reporting_mode()));
121 #endif
122 
123     DoNotifyFree(dispatcher_notification_data);
124   }
125 #endif  // PA_BUILDFLAG(USE_PARTITION_ALLOC)
126 
127 #if PA_BUILDFLAG(USE_ALLOCATOR_SHIM)
AllocFnDispatcherImpl128   static void* AllocFn(size_t size, void* context) {
129     void* const address =
130         allocator_dispatch_.next->alloc_function(size, context);
131 
132     DoNotifyAllocationForShim(address, size);
133 
134     return address;
135   }
136 
AllocUncheckedFnDispatcherImpl137   static void* AllocUncheckedFn(size_t size, void* context) {
138     void* const address =
139         allocator_dispatch_.next->alloc_unchecked_function(size, context);
140 
141     DoNotifyAllocationForShim(address, size);
142 
143     return address;
144   }
145 
AllocZeroInitializedFnDispatcherImpl146   static void* AllocZeroInitializedFn(size_t n, size_t size, void* context) {
147     void* const address =
148         allocator_dispatch_.next->alloc_zero_initialized_function(n, size,
149                                                                   context);
150 
151     DoNotifyAllocationForShim(address, n * size);
152 
153     return address;
154   }
155 
AllocAlignedFnDispatcherImpl156   static void* AllocAlignedFn(size_t alignment, size_t size, void* context) {
157     void* const address = allocator_dispatch_.next->alloc_aligned_function(
158         alignment, size, context);
159 
160     DoNotifyAllocationForShim(address, size);
161 
162     return address;
163   }
164 
ReallocFnDispatcherImpl165   static void* ReallocFn(void* address, size_t size, void* context) {
166     // Note: size == 0 actually performs free.
167     DoNotifyFreeForShim(address);
168     void* const reallocated_address =
169         allocator_dispatch_.next->realloc_function(address, size, context);
170 
171     DoNotifyAllocationForShim(reallocated_address, size);
172 
173     return reallocated_address;
174   }
175 
ReallocUncheckedFnDispatcherImpl176   static void* ReallocUncheckedFn(void* address, size_t size, void* context) {
177     // Note: size == 0 actually performs free.
178     DoNotifyFreeForShim(address);
179     void* const reallocated_address =
180         allocator_dispatch_.next->realloc_unchecked_function(address, size,
181                                                              context);
182 
183     DoNotifyAllocationForShim(reallocated_address, size);
184 
185     return reallocated_address;
186   }
187 
FreeFnDispatcherImpl188   static void FreeFn(void* address, void* context) {
189     // Note: DoNotifyFree should be called before free_function (here and in
190     // other places). That is because observers need to handle the allocation
191     // being freed before calling free_function, as once the latter is executed
192     // the address becomes available and can be allocated by another thread.
193     // That would be racy otherwise.
194     DoNotifyFreeForShim(address);
195     MUSTTAIL return allocator_dispatch_.next->free_function(address, context);
196   }
197 
BatchMallocFnDispatcherImpl198   static unsigned BatchMallocFn(size_t size,
199                                 void** results,
200                                 unsigned num_requested,
201                                 void* context) {
202     unsigned const num_allocated =
203         allocator_dispatch_.next->batch_malloc_function(size, results,
204                                                         num_requested, context);
205     for (unsigned i = 0; i < num_allocated; ++i) {
206       DoNotifyAllocationForShim(results[i], size);
207     }
208     return num_allocated;
209   }
210 
BatchFreeFnDispatcherImpl211   static void BatchFreeFn(void** to_be_freed,
212                           unsigned num_to_be_freed,
213                           void* context) {
214     for (unsigned i = 0; i < num_to_be_freed; ++i) {
215       DoNotifyFreeForShim(to_be_freed[i]);
216     }
217 
218     MUSTTAIL return allocator_dispatch_.next->batch_free_function(
219         to_be_freed, num_to_be_freed, context);
220   }
221 
FreeDefiniteSizeFnDispatcherImpl222   static void FreeDefiniteSizeFn(void* address, size_t size, void* context) {
223     DoNotifyFreeForShim(address);
224     MUSTTAIL return allocator_dispatch_.next->free_definite_size_function(
225         address, size, context);
226   }
227 
TryFreeDefaultFnDispatcherImpl228   static void TryFreeDefaultFn(void* address, void* context) {
229     DoNotifyFreeForShim(address);
230     MUSTTAIL return allocator_dispatch_.next->try_free_default_function(
231         address, context);
232   }
233 
AlignedMallocFnDispatcherImpl234   static void* AlignedMallocFn(size_t size, size_t alignment, void* context) {
235     void* const address = allocator_dispatch_.next->aligned_malloc_function(
236         size, alignment, context);
237 
238     DoNotifyAllocationForShim(address, size);
239 
240     return address;
241   }
242 
AlignedMallocUncheckedFnDispatcherImpl243   static void* AlignedMallocUncheckedFn(size_t size,
244                                         size_t alignment,
245                                         void* context) {
246     void* const address =
247         allocator_dispatch_.next->aligned_malloc_unchecked_function(
248             size, alignment, context);
249 
250     DoNotifyAllocationForShim(address, size);
251 
252     return address;
253   }
254 
AlignedReallocFnDispatcherImpl255   static void* AlignedReallocFn(void* address,
256                                 size_t size,
257                                 size_t alignment,
258                                 void* context) {
259     // Note: size == 0 actually performs free.
260     DoNotifyFreeForShim(address);
261     address = allocator_dispatch_.next->aligned_realloc_function(
262         address, size, alignment, context);
263 
264     DoNotifyAllocationForShim(address, size);
265 
266     return address;
267   }
268 
AlignedReallocUncheckedFnDispatcherImpl269   static void* AlignedReallocUncheckedFn(void* address,
270                                          size_t size,
271                                          size_t alignment,
272                                          void* context) {
273     // Note: size == 0 actually performs free.
274     DoNotifyFreeForShim(address);
275     address = allocator_dispatch_.next->aligned_realloc_unchecked_function(
276         address, size, alignment, context);
277 
278     DoNotifyAllocationForShim(address, size);
279 
280     return address;
281   }
282 
AlignedFreeFnDispatcherImpl283   static void AlignedFreeFn(void* address, void* context) {
284     DoNotifyFreeForShim(address);
285     MUSTTAIL return allocator_dispatch_.next->aligned_free_function(address,
286                                                                     context);
287   }
288 
DoNotifyAllocationForShimDispatcherImpl289   ALWAYS_INLINE static void DoNotifyAllocationForShim(void* address,
290                                                       size_t size) {
291     AllocationNotificationData notification_data(
292         address, size, nullptr, AllocationSubsystem::kAllocatorShim);
293 
294     DoNotifyAllocation(notification_data);
295   }
296 
DoNotifyFreeForShimDispatcherImpl297   ALWAYS_INLINE static void DoNotifyFreeForShim(void* address) {
298     FreeNotificationData notification_data(address,
299                                            AllocationSubsystem::kAllocatorShim);
300 
301     DoNotifyFree(notification_data);
302   }
303 
304   static AllocatorDispatch allocator_dispatch_;
305 #endif  // PA_BUILDFLAG(USE_ALLOCATOR_SHIM)
306 
DoNotifyAllocationDispatcherImpl307   ALWAYS_INLINE static void DoNotifyAllocation(
308       const AllocationNotificationData& notification_data) {
309     PerformAllocationNotification(s_observers, AllObservers{},
310                                   notification_data);
311   }
312 
DoNotifyFreeDispatcherImpl313   ALWAYS_INLINE static void DoNotifyFree(
314       const FreeNotificationData& notification_data) {
315     PerformFreeNotification(s_observers, AllObservers{}, notification_data);
316   }
317 
318   static std::tuple<ObserverTypes*...> s_observers;
319 };
320 
321 template <typename... ObserverTypes>
322 std::tuple<ObserverTypes*...> DispatcherImpl<ObserverTypes...>::s_observers;
323 
324 #if PA_BUILDFLAG(USE_ALLOCATOR_SHIM)
325 template <typename... ObserverTypes>
326 AllocatorDispatch DispatcherImpl<ObserverTypes...>::allocator_dispatch_ = {
327     AllocFn,                    // alloc_function
328     AllocUncheckedFn,           // alloc_unchecked_function
329     AllocZeroInitializedFn,     // alloc_zero_initialized_function
330     AllocAlignedFn,             // alloc_aligned_function
331     ReallocFn,                  // realloc_function
332     ReallocUncheckedFn,         // realloc_unchecked_function
333     FreeFn,                     // free_function
334     nullptr,                    // get_size_estimate_function
335     nullptr,                    // good_size_function
336     nullptr,                    // claimed_address_function
337     BatchMallocFn,              // batch_malloc_function
338     BatchFreeFn,                // batch_free_function
339     FreeDefiniteSizeFn,         // free_definite_size_function
340     TryFreeDefaultFn,           // try_free_default_function
341     AlignedMallocFn,            // aligned_malloc_function
342     AlignedMallocUncheckedFn,   // aligned_malloc_unchecked_function
343     AlignedReallocFn,           // aligned_realloc_function
344     AlignedReallocUncheckedFn,  // aligned_realloc_unchecked_function
345     AlignedFreeFn,              // aligned_free_function
346     nullptr                     // next
347 };
348 #endif  // PA_BUILDFLAG(USE_ALLOCATOR_SHIM)
349 
350 // Specialization of DispatcherImpl in case we have no observers to notify. In
351 // this special case we return a set of null pointers as the Dispatcher must not
352 // install any hooks at all.
353 template <>
354 struct DispatcherImpl<> {
355   static DispatchData GetNotificationHooks(std::tuple<> /*observers*/) {
356     return DispatchData()
357 #if PA_BUILDFLAG(USE_PARTITION_ALLOC)
358         .SetAllocationObserverHooks(nullptr, nullptr)
359 #endif
360 #if PA_BUILDFLAG(USE_ALLOCATOR_SHIM)
361         .SetAllocatorDispatch(nullptr)
362 #endif
363         ;
364   }
365 };
366 
367 // A little utility function that helps using DispatcherImpl by providing
368 // automated type deduction for templates.
369 template <typename... ObserverTypes>
370 inline DispatchData GetNotificationHooks(
371     std::tuple<ObserverTypes*...> observers) {
372   return DispatcherImpl<ObserverTypes...>::GetNotificationHooks(
373       std::move(observers));
374 }
375 
376 }  // namespace base::allocator::dispatcher::internal
377 
378 #endif  // BASE_ALLOCATOR_DISPATCHER_INTERNAL_DISPATCHER_INTERNAL_H_
379