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