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