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 #ifndef PARTITION_ALLOC_ADDRESS_POOL_MANAGER_H_
6 #define PARTITION_ALLOC_ADDRESS_POOL_MANAGER_H_
7
8 #include <bitset>
9 #include <limits>
10
11 #include "build/build_config.h"
12 #include "partition_alloc/address_pool_manager_types.h"
13 #include "partition_alloc/partition_address_space.h"
14 #include "partition_alloc/partition_alloc_base/compiler_specific.h"
15 #include "partition_alloc/partition_alloc_base/component_export.h"
16 #include "partition_alloc/partition_alloc_base/debug/debugging_buildflags.h"
17 #include "partition_alloc/partition_alloc_base/thread_annotations.h"
18 #include "partition_alloc/partition_alloc_buildflags.h"
19 #include "partition_alloc/partition_alloc_check.h"
20 #include "partition_alloc/partition_alloc_constants.h"
21 #include "partition_alloc/partition_lock.h"
22 #include "partition_alloc/thread_isolation/alignment.h"
23 #include "partition_alloc/thread_isolation/thread_isolation.h"
24
25 #if !BUILDFLAG(HAS_64_BIT_POINTERS)
26 #include "partition_alloc/address_pool_manager_bitmap.h"
27 #endif
28
29 namespace partition_alloc {
30
31 class AddressSpaceStatsDumper;
32 struct AddressSpaceStats;
33 struct PoolStats;
34
35 } // namespace partition_alloc
36
37 namespace partition_alloc::internal {
38
39 // (64bit version)
40 // AddressPoolManager takes a reserved virtual address space and manages address
41 // space allocation.
42 //
43 // AddressPoolManager (currently) supports up to 4 pools. Each pool manages a
44 // contiguous reserved address space. Alloc() takes a pool_handle and returns
45 // address regions from the specified pool. Free() also takes a pool_handle and
46 // returns the address region back to the manager.
47 //
48 // (32bit version)
49 // AddressPoolManager wraps AllocPages and FreePages and remembers allocated
50 // address regions using bitmaps. IsManagedByPartitionAlloc*Pool use the bitmaps
51 // to judge whether a given address is in a pool that supports BackupRefPtr or
52 // in a pool that doesn't. All PartitionAlloc allocations must be in either of
53 // the pools.
PA_COMPONENT_EXPORT(PARTITION_ALLOC)54 class PA_COMPONENT_EXPORT(PARTITION_ALLOC)
55 PA_THREAD_ISOLATED_ALIGN AddressPoolManager {
56 public:
57 static AddressPoolManager& GetInstance();
58
59 AddressPoolManager(const AddressPoolManager&) = delete;
60 AddressPoolManager& operator=(const AddressPoolManager&) = delete;
61
62 #if BUILDFLAG(HAS_64_BIT_POINTERS)
63 void Add(pool_handle handle, uintptr_t address, size_t length);
64 void Remove(pool_handle handle);
65
66 // Populate a |used| bitset of superpages currently in use.
67 void GetPoolUsedSuperPages(pool_handle handle,
68 std::bitset<kMaxSuperPagesInPool>& used);
69
70 // Return the base address of a pool.
71 uintptr_t GetPoolBaseAddress(pool_handle handle);
72 #endif // BUILDFLAG(HAS_64_BIT_POINTERS)
73
74 // Reserves address space from the pool.
75 uintptr_t Reserve(pool_handle handle,
76 uintptr_t requested_address,
77 size_t length);
78
79 // Frees address space back to the pool and decommits underlying system pages.
80 void UnreserveAndDecommit(pool_handle handle,
81 uintptr_t address,
82 size_t length);
83 void ResetForTesting();
84
85 #if !BUILDFLAG(HAS_64_BIT_POINTERS)
86 void MarkUsed(pool_handle handle, uintptr_t address, size_t size);
87 void MarkUnused(pool_handle handle, uintptr_t address, size_t size);
88
89 static bool IsManagedByRegularPool(uintptr_t address) {
90 return AddressPoolManagerBitmap::IsManagedByRegularPool(address);
91 }
92
93 static bool IsManagedByBRPPool(uintptr_t address) {
94 return AddressPoolManagerBitmap::IsManagedByBRPPool(address);
95 }
96 #endif // !BUILDFLAG(HAS_64_BIT_POINTERS)
97
98 void DumpStats(AddressSpaceStatsDumper* dumper);
99
100 private:
101 friend class AddressPoolManagerForTesting;
102 #if BUILDFLAG(ENABLE_THREAD_ISOLATION)
103 // If we use a thread isolated pool, we need to write-protect its metadata.
104 // Allow the function to get access to the pool pointer.
105 friend void WriteProtectThreadIsolatedGlobals(ThreadIsolationOption);
106 #endif
107
108 constexpr AddressPoolManager() = default;
109 ~AddressPoolManager() = default;
110
111 // Populates `stats` if applicable.
112 // Returns whether `stats` was populated. (They might not be, e.g.
113 // if PartitionAlloc is wholly unused in this process.)
114 bool GetStats(AddressSpaceStats* stats);
115
116 #if BUILDFLAG(ENABLE_THREAD_ISOLATION)
117 static void AssertThreadIsolatedLayout();
118 #endif // BUILDFLAG(ENABLE_THREAD_ISOLATION)
119
120 #if BUILDFLAG(HAS_64_BIT_POINTERS)
121
122 class Pool {
123 public:
124 constexpr Pool() = default;
125 ~Pool() = default;
126
127 Pool(const Pool&) = delete;
128 Pool& operator=(const Pool&) = delete;
129
130 void Initialize(uintptr_t ptr, size_t length);
131 bool IsInitialized();
132 void Reset();
133
134 uintptr_t FindChunk(size_t size);
135 void FreeChunk(uintptr_t address, size_t size);
136
137 bool TryReserveChunk(uintptr_t address, size_t size);
138
139 void GetUsedSuperPages(std::bitset<kMaxSuperPagesInPool>& used);
140 uintptr_t GetBaseAddress();
141
142 void GetStats(PoolStats* stats);
143
144 private:
145 // The lock needs to be the first field in this class.
146 // We write-protect the pool in the ThreadIsolated case, except that the
147 // lock can be used without acquiring write-permission first (via
148 // DumpStats()). So instead of protecting the whole variable, we only
149 // protect the memory after the lock.
150 // See the alignment of ` below.
151 Lock lock_;
152
153 // The bitset stores the allocation state of the address pool. 1 bit per
154 // super-page: 1 = allocated, 0 = free.
155 std::bitset<kMaxSuperPagesInPool> alloc_bitset_ PA_GUARDED_BY(lock_);
156
157 // An index of a bit in the bitset before which we know for sure there all
158 // 1s. This is a best-effort hint in the sense that there still may be lots
159 // of 1s after this index, but at least we know there is no point in
160 // starting the search before it.
161 size_t bit_hint_ PA_GUARDED_BY(lock_) = 0;
162
163 size_t total_bits_ = 0;
164 uintptr_t address_begin_ = 0;
165 #if BUILDFLAG(PA_DCHECK_IS_ON)
166 uintptr_t address_end_ = 0;
167 #endif
168
169 #if BUILDFLAG(ENABLE_THREAD_ISOLATION)
170 friend class AddressPoolManager;
171 friend void WriteProtectThreadIsolatedGlobals(ThreadIsolationOption);
172 #endif // BUILDFLAG(ENABLE_THREAD_ISOLATION)
173 };
174
175 PA_ALWAYS_INLINE Pool* GetPool(pool_handle handle) {
176 PA_DCHECK(kNullPoolHandle < handle && handle <= kNumPools);
177 return &pools_[handle - 1];
178 }
179
180 // Gets the stats for the pool identified by `handle`, if
181 // initialized.
182 void GetPoolStats(pool_handle handle, PoolStats* stats);
183
184 // If thread isolation support is enabled, we need to write-protect the
185 // isolated pool (which needs to be last). For this, we need to add padding in
186 // front of the pools so that the isolated one starts on a page boundary.
187 // We also skip the Lock at the beginning of the pool since it needs to be
188 // used in contexts where we didn't enable write access to the pool memory.
189 #if defined(__clang__)
190 #pragma clang diagnostic push
191 #pragma clang diagnostic ignored "-Wzero-length-array"
192 #endif
193 char pad_[PA_THREAD_ISOLATED_ARRAY_PAD_SZ_WITH_OFFSET(
194 Pool,
195 kNumPools,
196 offsetof(Pool, alloc_bitset_))] = {};
197 #if defined(__clang__)
198 #pragma clang diagnostic pop
199 #endif
200 Pool pools_[kNumPools];
201
202 #endif // BUILDFLAG(HAS_64_BIT_POINTERS)
203
204 static PA_CONSTINIT AddressPoolManager singleton_;
205 };
206
207 } // namespace partition_alloc::internal
208
209 #endif // PARTITION_ALLOC_ADDRESS_POOL_MANAGER_H_
210