• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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_PAGE_ALLOCATOR_H_
6 #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H_
7 
8 #include <cstddef>
9 #include <cstdint>
10 
11 #include "base/allocator/partition_allocator/page_allocator_constants.h"
12 #include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
13 #include "base/allocator/partition_allocator/partition_alloc_base/component_export.h"
14 #include "base/allocator/partition_allocator/partition_alloc_buildflags.h"
15 #include "build/build_config.h"
16 
17 namespace partition_alloc {
18 
19 struct PageAccessibilityConfiguration {
20   enum Permissions {
21     kInaccessible,
22     kRead,
23     kReadWrite,
24     // This flag is mapped to kReadWrite on systems that
25     // don't support MTE.
26     kReadWriteTagged,
27     // This flag is mapped to kReadExecute on systems
28     // that don't support Arm's BTI.
29     kReadExecuteProtected,
30     kReadExecute,
31     // This flag is deprecated and will go away soon.
32     // TODO(bbudge) Remove this as soon as V8 doesn't need RWX pages.
33     kReadWriteExecute,
34   };
35 
36 #if BUILDFLAG(ENABLE_PKEYS)
PageAccessibilityConfigurationPageAccessibilityConfiguration37   constexpr explicit PageAccessibilityConfiguration(Permissions permissions)
38       : permissions(permissions), pkey(0) {}
PageAccessibilityConfigurationPageAccessibilityConfiguration39   constexpr PageAccessibilityConfiguration(Permissions permissions, int pkey)
40       : permissions(permissions), pkey(pkey) {}
41 #else
PageAccessibilityConfigurationPageAccessibilityConfiguration42   constexpr explicit PageAccessibilityConfiguration(Permissions permissions)
43       : permissions(permissions) {}
44 #endif  // BUILDFLAG(ENABLE_PKEYS)
45 
46   Permissions permissions;
47 #if BUILDFLAG(ENABLE_PKEYS)
48   // Tag the page with a Memory Protection Key. Use 0 for none.
49   int pkey;
50 #endif  // BUILDFLAG(ENABLE_PKEYS)
51 };
52 
53 // Use for De/RecommitSystemPages API.
54 enum class PageAccessibilityDisposition {
55   // Enforces permission update (Decommit will set to
56   // PageAccessibilityConfiguration::kInaccessible;
57   // Recommit will set to whatever was requested, other than
58   // PageAccessibilityConfiguration::kInaccessible).
59   kRequireUpdate,
60   // Will not update permissions, if the platform supports that (POSIX & Fuchsia
61   // only).
62   kAllowKeepForPerf,
63 };
64 
65 // macOS supports tagged memory regions, to help in debugging. On Android,
66 // these tags are used to name anonymous mappings.
67 enum class PageTag {
68   kFirst = 240,           // Minimum tag value.
69   kSimulation = 251,      // Memory simulator tool.
70   kBlinkGC = 252,         // Blink GC pages.
71   kPartitionAlloc = 253,  // PartitionAlloc, no matter the partition.
72   kChromium = 254,        // Chromium page.
73   kV8 = 255,              // V8 heap pages.
74   kLast = kV8             // Maximum tag value.
75 };
76 
77 // See
78 // https://github.com/apple-oss-distributions/xnu/blob/5c2921b07a2480ab43ec66f5b9e41cb872bc554f/osfmk/mach/vm_statistics.h#L687
79 static_assert(
80     static_cast<int>(PageTag::kLast) < 256,
81     "Tags are only 1 byte long on macOS, see vm_statistics.h in XNU.");
82 
83 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
84 uintptr_t NextAlignedWithOffset(uintptr_t ptr,
85                                 uintptr_t alignment,
86                                 uintptr_t requested_offset);
87 
88 // Allocates one or more pages.
89 //
90 // The requested |address| is just a hint; the actual address returned may
91 // differ. The returned address will be aligned to |align_offset| modulo |align|
92 // bytes.
93 //
94 // |length|, |align| and |align_offset| are in bytes, and must be a multiple of
95 // |PageAllocationGranularity()|. |length| and |align| must be non-zero.
96 // |align_offset| must be less than |align|. |align| must be a power of two.
97 //
98 // If |address| is 0/nullptr, then a suitable and randomized address will be
99 // chosen automatically.
100 //
101 // |accessibility| controls the permission of the allocated pages.
102 // PageAccessibilityConfiguration::kInaccessible means uncommitted.
103 //
104 // |page_tag| is used on some platforms to identify the source of the
105 // allocation. Use PageTag::kChromium as a catch-all category.
106 //
107 // |file_descriptor_for_shared_alloc| is only used in mapping the shadow
108 // pools to the same physical address as the real one in
109 // PartitionAddressSpace::Init(). It should be ignored in other cases.
110 //
111 // This call will return 0/nullptr if the allocation cannot be satisfied.
112 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
113 uintptr_t AllocPages(size_t length,
114                      size_t align,
115                      PageAccessibilityConfiguration accessibility,
116                      PageTag page_tag,
117                      int file_descriptor_for_shared_alloc = -1);
118 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
119 uintptr_t AllocPages(uintptr_t address,
120                      size_t length,
121                      size_t align,
122                      PageAccessibilityConfiguration accessibility,
123                      PageTag page_tag);
124 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
125 void* AllocPages(void* address,
126                  size_t length,
127                  size_t align,
128                  PageAccessibilityConfiguration accessibility,
129                  PageTag page_tag);
130 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
131 uintptr_t AllocPagesWithAlignOffset(
132     uintptr_t address,
133     size_t length,
134     size_t align,
135     size_t align_offset,
136     PageAccessibilityConfiguration page_accessibility,
137     PageTag page_tag,
138     int file_descriptor_for_shared_alloc = -1);
139 
140 // Frees one or more pages starting at |address| and continuing for |length|
141 // bytes.
142 //
143 // |address| and |length| must match a previous call to |AllocPages|. Therefore,
144 // |address| must be aligned to |PageAllocationGranularity()| bytes, and
145 // |length| must be a multiple of |PageAllocationGranularity()|.
146 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
147 void FreePages(uintptr_t address, size_t length);
148 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
149 void FreePages(void* address, size_t length);
150 
151 // Marks one or more system pages, starting at |address| with the given
152 // |page_accessibility|. |length| must be a multiple of |SystemPageSize()|
153 // bytes.
154 //
155 // Returns true if the permission change succeeded. In most cases you must
156 // |CHECK| the result.
157 [[nodiscard]] PA_COMPONENT_EXPORT(PARTITION_ALLOC) bool TrySetSystemPagesAccess(
158     uintptr_t address,
159     size_t length,
160     PageAccessibilityConfiguration page_accessibility);
161 [[nodiscard]] PA_COMPONENT_EXPORT(PARTITION_ALLOC) bool TrySetSystemPagesAccess(
162     void* address,
163     size_t length,
164     PageAccessibilityConfiguration page_accessibility);
165 
166 // Marks one or more system pages, starting at |address| with the given
167 // |page_accessibility|. |length| must be a multiple of |SystemPageSize()|
168 // bytes.
169 //
170 // Performs a CHECK that the operation succeeds.
171 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
172 void SetSystemPagesAccess(uintptr_t address,
173                           size_t length,
174                           PageAccessibilityConfiguration page_accessibility);
175 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
176 void SetSystemPagesAccess(void* address,
177                           size_t length,
178                           PageAccessibilityConfiguration page_accessibility);
179 
180 // Decommits one or more system pages starting at |address| and continuing for
181 // |length| bytes. |address| and |length| must be aligned to a system page
182 // boundary.
183 //
184 // This API will crash if the operation cannot be performed!
185 //
186 // If disposition is PageAccessibilityDisposition::kRequireUpdate (recommended),
187 // the decommitted pages will be made inaccessible before the call returns.
188 // While it is always a programming error to access decommitted pages without
189 // first recommitting them, callers may use
190 // PageAccessibilityDisposition::kAllowKeepForPerf to allow the implementation
191 // to skip changing permissions (use with care), for performance reasons (see
192 // crrev.com/c/2567282 and crrev.com/c/2563038 for perf regressions encountered
193 // in the past). Implementations may choose to always modify permissions, hence
194 // accessing those pages may or may not trigger a fault.
195 //
196 // Decommitting means that physical resources (RAM or swap/pagefile) backing the
197 // allocated virtual address range may be released back to the system, but the
198 // address space is still allocated to the process (possibly using up page table
199 // entries or other accounting resources). There is no guarantee that the pages
200 // are zeroed, unless |DecommittedMemoryIsAlwaysZeroed()| is true.
201 //
202 // This operation may not be atomic on some platforms.
203 //
204 // Note: "Committed memory" is a Windows Memory Subsystem concept that ensures
205 // processes will not fault when touching a committed memory region. There is
206 // no analogue in the POSIX & Fuchsia memory API where virtual memory pages are
207 // best-effort allocated resources on the first touch. If
208 // PageAccessibilityDisposition::kRequireUpdate disposition is used, this API
209 // behaves in a platform-agnostic way by simulating the Windows "decommit" state
210 // by both discarding the region (allowing the OS to avoid swap operations)
211 // *and* changing the page protections so accesses fault.
212 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
213 void DecommitSystemPages(
214     uintptr_t address,
215     size_t length,
216     PageAccessibilityDisposition accessibility_disposition);
217 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
218 void DecommitSystemPages(
219     void* address,
220     size_t length,
221     PageAccessibilityDisposition accessibility_disposition);
222 
223 // Decommits one or more system pages starting at |address| and continuing for
224 // |length| bytes. |address| and |length| must be aligned to a system page
225 // boundary.
226 //
227 // In contrast to |DecommitSystemPages|, this API guarantees that the pages are
228 // zeroed and will always mark the region as inaccessible (the equivalent of
229 // setting them to PageAccessibilityConfiguration::kInaccessible).
230 //
231 // This API will crash if the operation cannot be performed.
232 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
233 void DecommitAndZeroSystemPages(uintptr_t address, size_t length);
234 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
235 void DecommitAndZeroSystemPages(void* address, size_t length);
236 
237 // Whether decommitted memory is guaranteed to be zeroed when it is
238 // recommitted. Do not assume that this will not change over time.
PA_COMPONENT_EXPORT(PARTITION_ALLOC)239 constexpr PA_COMPONENT_EXPORT(
240     PARTITION_ALLOC) bool DecommittedMemoryIsAlwaysZeroed() {
241 #if BUILDFLAG(IS_APPLE)
242   return false;
243 #else
244   return true;
245 #endif
246 }
247 
248 // (Re)Commits one or more system pages, starting at |address| and continuing
249 // for |length| bytes with the given |page_accessibility| (must not be
250 // PageAccessibilityConfiguration::kInaccessible). |address| and |length|
251 // must be aligned to a system page boundary.
252 //
253 // This API will crash if the operation cannot be performed!
254 //
255 // If disposition is PageAccessibilityConfiguration::kRequireUpdate, the calls
256 // updates the pages to |page_accessibility|. This can be used regardless of
257 // what disposition was used to decommit the pages.
258 // PageAccessibilityConfiguration::kAllowKeepForPerf allows the implementation
259 // to leave the page permissions, if that improves performance. This option can
260 // only be used if the pages were previously accessible and decommitted with
261 // that same option.
262 //
263 // The memory will be zeroed when it is committed for the first time. However,
264 // there is no such guarantee when memory is recommitted, unless
265 // |DecommittedMemoryIsAlwaysZeroed()| is true.
266 //
267 // This operation may not be atomic on some platforms.
268 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
269 void RecommitSystemPages(
270     uintptr_t address,
271     size_t length,
272     PageAccessibilityConfiguration page_accessibility,
273     PageAccessibilityDisposition accessibility_disposition);
274 
275 // Like RecommitSystemPages(), but returns false instead of crashing.
276 [[nodiscard]] PA_COMPONENT_EXPORT(PARTITION_ALLOC) bool TryRecommitSystemPages(
277     uintptr_t address,
278     size_t length,
279     PageAccessibilityConfiguration page_accessibility,
280     PageAccessibilityDisposition accessibility_disposition);
281 
282 // Discard one or more system pages starting at |address| and continuing for
283 // |length| bytes. |length| must be a multiple of |SystemPageSize()|.
284 //
285 // Discarding is a hint to the system that the page is no longer required. The
286 // hint may:
287 //   - Do nothing.
288 //   - Discard the page immediately, freeing up physical pages.
289 //   - Discard the page at some time in the future in response to memory
290 //   pressure.
291 //
292 // Only committed pages should be discarded. Discarding a page does not decommit
293 // it, and it is valid to discard an already-discarded page. A read or write to
294 // a discarded page will not fault.
295 //
296 // Reading from a discarded page may return the original page content, or a page
297 // full of zeroes.
298 //
299 // Writing to a discarded page is the only guaranteed way to tell the system
300 // that the page is required again. Once written to, the content of the page is
301 // guaranteed stable once more. After being written to, the page content may be
302 // based on the original page content, or a page of zeroes.
303 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
304 void DiscardSystemPages(uintptr_t address, size_t length);
305 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
306 void DiscardSystemPages(void* address, size_t length);
307 
308 // Rounds up |address| to the next multiple of |SystemPageSize()|. Returns
309 // 0 for an |address| of 0.
310 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
RoundUpToSystemPage(uintptr_t address)311 RoundUpToSystemPage(uintptr_t address) {
312   return (address + internal::SystemPageOffsetMask()) &
313          internal::SystemPageBaseMask();
314 }
315 
316 // Rounds down |address| to the previous multiple of |SystemPageSize()|. Returns
317 // 0 for an |address| of 0.
318 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
RoundDownToSystemPage(uintptr_t address)319 RoundDownToSystemPage(uintptr_t address) {
320   return address & internal::SystemPageBaseMask();
321 }
322 
323 // Rounds up |address| to the next multiple of |PageAllocationGranularity()|.
324 // Returns 0 for an |address| of 0.
325 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
RoundUpToPageAllocationGranularity(uintptr_t address)326 RoundUpToPageAllocationGranularity(uintptr_t address) {
327   return (address + internal::PageAllocationGranularityOffsetMask()) &
328          internal::PageAllocationGranularityBaseMask();
329 }
330 
331 // Rounds down |address| to the previous multiple of
332 // |PageAllocationGranularity()|. Returns 0 for an |address| of 0.
333 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
RoundDownToPageAllocationGranularity(uintptr_t address)334 RoundDownToPageAllocationGranularity(uintptr_t address) {
335   return address & internal::PageAllocationGranularityBaseMask();
336 }
337 
338 // Reserves (at least) |size| bytes of address space, aligned to
339 // |PageAllocationGranularity()|. This can be called early on to make it more
340 // likely that large allocations will succeed. Returns true if the reservation
341 // succeeded, false if the reservation failed or a reservation was already made.
342 PA_COMPONENT_EXPORT(PARTITION_ALLOC) bool ReserveAddressSpace(size_t size);
343 
344 // Releases any reserved address space. |AllocPages| calls this automatically on
345 // an allocation failure. External allocators may also call this on failure.
346 //
347 // Returns true when an existing reservation was released.
348 PA_COMPONENT_EXPORT(PARTITION_ALLOC) bool ReleaseReservation();
349 
350 // Returns true if there is currently an address space reservation.
351 PA_COMPONENT_EXPORT(PARTITION_ALLOC) bool HasReservationForTesting();
352 
353 // Returns |errno| (POSIX) or the result of |GetLastError| (Windows) when |mmap|
354 // (POSIX) or |VirtualAlloc| (Windows) fails.
355 PA_COMPONENT_EXPORT(PARTITION_ALLOC) uint32_t GetAllocPageErrorCode();
356 
357 // Returns the total amount of mapped pages from all clients of
358 // PageAllocator. These pages may or may not be committed. This is mostly useful
359 // to assess address space pressure.
360 PA_COMPONENT_EXPORT(PARTITION_ALLOC) size_t GetTotalMappedSize();
361 
362 #if BUILDFLAG(IS_WIN)
363 // Sets whether to retry the allocation of pages when a commit failure
364 // happens. This doesn't cover cases where the system is out of address space,
365 // or reaches another limit.
366 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
367 void SetRetryOnCommitFailure(bool retry_on_commit_failure);
368 bool GetRetryOnCommitFailure();
369 #endif  // BUILDFLAG(IS_WIN)
370 
371 }  // namespace partition_alloc
372 
373 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H_
374