1 // Copyright 2020 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 #include "base/allocator/partition_allocator/partition_alloc_hooks.h"
6
7 #include <ostream>
8
9 #include "base/allocator/partition_allocator/partition_alloc_check.h"
10 #include "base/allocator/partition_allocator/partition_lock.h"
11
12 namespace partition_alloc {
13
14 namespace {
15
16 internal::Lock g_hook_lock;
17
GetHooksLock()18 internal::Lock& GetHooksLock() {
19 return g_hook_lock;
20 }
21
22 } // namespace
23
24 std::atomic<bool> PartitionAllocHooks::hooks_enabled_(false);
25 std::atomic<PartitionAllocHooks::AllocationObserverHook*>
26 PartitionAllocHooks::allocation_observer_hook_(nullptr);
27 std::atomic<PartitionAllocHooks::FreeObserverHook*>
28 PartitionAllocHooks::free_observer_hook_(nullptr);
29 std::atomic<PartitionAllocHooks::AllocationOverrideHook*>
30 PartitionAllocHooks::allocation_override_hook_(nullptr);
31 std::atomic<PartitionAllocHooks::FreeOverrideHook*>
32 PartitionAllocHooks::free_override_hook_(nullptr);
33 std::atomic<PartitionAllocHooks::ReallocOverrideHook*>
34 PartitionAllocHooks::realloc_override_hook_(nullptr);
35 std::atomic<PartitionAllocHooks::QuarantineOverrideHook*>
36 PartitionAllocHooks::quarantine_override_hook_(nullptr);
37
SetObserverHooks(AllocationObserverHook * alloc_hook,FreeObserverHook * free_hook)38 void PartitionAllocHooks::SetObserverHooks(AllocationObserverHook* alloc_hook,
39 FreeObserverHook* free_hook) {
40 internal::ScopedGuard guard(GetHooksLock());
41
42 // Chained hooks are not supported. Registering a non-null hook when a
43 // non-null hook is already registered indicates somebody is trying to
44 // overwrite a hook.
45 PA_CHECK((!allocation_observer_hook_ && !free_observer_hook_) ||
46 (!alloc_hook && !free_hook))
47 << "Overwriting already set observer hooks";
48 allocation_observer_hook_ = alloc_hook;
49 free_observer_hook_ = free_hook;
50
51 hooks_enabled_ = allocation_observer_hook_ || allocation_override_hook_;
52 }
53
SetOverrideHooks(AllocationOverrideHook * alloc_hook,FreeOverrideHook * free_hook,ReallocOverrideHook realloc_hook)54 void PartitionAllocHooks::SetOverrideHooks(AllocationOverrideHook* alloc_hook,
55 FreeOverrideHook* free_hook,
56 ReallocOverrideHook realloc_hook) {
57 internal::ScopedGuard guard(GetHooksLock());
58
59 PA_CHECK((!allocation_override_hook_ && !free_override_hook_ &&
60 !realloc_override_hook_) ||
61 (!alloc_hook && !free_hook && !realloc_hook))
62 << "Overwriting already set override hooks";
63 allocation_override_hook_ = alloc_hook;
64 free_override_hook_ = free_hook;
65 realloc_override_hook_ = realloc_hook;
66
67 hooks_enabled_ = allocation_observer_hook_ || allocation_override_hook_;
68 }
69
AllocationObserverHookIfEnabled(void * address,size_t size,const char * type_name)70 void PartitionAllocHooks::AllocationObserverHookIfEnabled(
71 void* address,
72 size_t size,
73 const char* type_name) {
74 if (auto* hook = allocation_observer_hook_.load(std::memory_order_relaxed)) {
75 hook(address, size, type_name);
76 }
77 }
78
AllocationOverrideHookIfEnabled(void ** out,unsigned int flags,size_t size,const char * type_name)79 bool PartitionAllocHooks::AllocationOverrideHookIfEnabled(
80 void** out,
81 unsigned int flags,
82 size_t size,
83 const char* type_name) {
84 if (auto* hook = allocation_override_hook_.load(std::memory_order_relaxed)) {
85 return hook(out, flags, size, type_name);
86 }
87 return false;
88 }
89
FreeObserverHookIfEnabled(void * address)90 void PartitionAllocHooks::FreeObserverHookIfEnabled(void* address) {
91 if (auto* hook = free_observer_hook_.load(std::memory_order_relaxed)) {
92 hook(address);
93 }
94 }
95
FreeOverrideHookIfEnabled(void * address)96 bool PartitionAllocHooks::FreeOverrideHookIfEnabled(void* address) {
97 if (auto* hook = free_override_hook_.load(std::memory_order_relaxed)) {
98 return hook(address);
99 }
100 return false;
101 }
102
ReallocObserverHookIfEnabled(void * old_address,void * new_address,size_t size,const char * type_name)103 void PartitionAllocHooks::ReallocObserverHookIfEnabled(void* old_address,
104 void* new_address,
105 size_t size,
106 const char* type_name) {
107 // Report a reallocation as a free followed by an allocation.
108 AllocationObserverHook* allocation_hook =
109 allocation_observer_hook_.load(std::memory_order_relaxed);
110 FreeObserverHook* free_hook =
111 free_observer_hook_.load(std::memory_order_relaxed);
112 if (allocation_hook && free_hook) {
113 free_hook(old_address);
114 allocation_hook(new_address, size, type_name);
115 }
116 }
117
ReallocOverrideHookIfEnabled(size_t * out,void * address)118 bool PartitionAllocHooks::ReallocOverrideHookIfEnabled(size_t* out,
119 void* address) {
120 if (ReallocOverrideHook* hook =
121 realloc_override_hook_.load(std::memory_order_relaxed)) {
122 return hook(out, address);
123 }
124 return false;
125 }
126
127 // Do not unset the hook if there are remaining quarantined slots
128 // not to break checks on unquarantining.
SetQuarantineOverrideHook(QuarantineOverrideHook * hook)129 void PartitionAllocHooks::SetQuarantineOverrideHook(
130 QuarantineOverrideHook* hook) {
131 quarantine_override_hook_.store(hook, std::memory_order_release);
132 }
133
134 } // namespace partition_alloc
135