1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
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 <stdint.h>
9 
10 #include <cstddef>
11 
12 #include "base/allocator/partition_allocator/page_allocator_constants.h"
13 #include "base/base_export.h"
14 #include "base/compiler_specific.h"
15 #include "build/build_config.h"
16 
17 namespace base {
18 
19 enum PageAccessibilityConfiguration {
20   PageInaccessible,
21   PageRead,
22   PageReadWrite,
23   PageReadExecute,
24   // This flag is deprecated and will go away soon.
25   // TODO(bbudge) Remove this as soon as V8 doesn't need RWX pages.
26   PageReadWriteExecute,
27 };
28 
29 // macOS supports tagged memory regions, to help in debugging. On Android,
30 // these tags are used to name anonymous mappings.
31 enum class PageTag {
32   kFirst = 240,           // Minimum tag value.
33   kBlinkGC = 252,         // Blink GC pages.
34   kPartitionAlloc = 253,  // PartitionAlloc, no matter the partition.
35   kChromium = 254,        // Chromium page.
36   kV8 = 255,              // V8 heap pages.
37   kLast = kV8             // Maximum tag value.
38 };
39 
40 // Allocate one or more pages.
41 //
42 // The requested |address| is just a hint; the actual address returned may
43 // differ. The returned address will be aligned at least to |align| bytes.
44 // |length| is in bytes, and must be a multiple of |kPageAllocationGranularity|.
45 // |align| is in bytes, and must be a power-of-two multiple of
46 // |kPageAllocationGranularity|.
47 //
48 // If |address| is null, then a suitable and randomized address will be chosen
49 // automatically.
50 //
51 // |page_accessibility| controls the permission of the allocated pages.
52 // |page_tag| is used on some platforms to identify the source of the
53 // allocation. Use PageTag::kChromium as a catch-all category.
54 //
55 // This call will return null if the allocation cannot be satisfied.
56 BASE_EXPORT void* AllocPages(void* address,
57                              size_t length,
58                              size_t align,
59                              PageAccessibilityConfiguration page_accessibility,
60                              PageTag tag,
61                              bool commit = true);
62 
63 // Free one or more pages starting at |address| and continuing for |length|
64 // bytes.
65 //
66 // |address| and |length| must match a previous call to |AllocPages|. Therefore,
67 // |address| must be aligned to |kPageAllocationGranularity| bytes, and |length|
68 // must be a multiple of |kPageAllocationGranularity|.
69 BASE_EXPORT void FreePages(void* address, size_t length);
70 
71 // Mark one or more system pages, starting at |address| with the given
72 // |page_accessibility|. |length| must be a multiple of |kSystemPageSize| bytes.
73 //
74 // Returns true if the permission change succeeded. In most cases you must
75 // |CHECK| the result.
76 BASE_EXPORT WARN_UNUSED_RESULT bool TrySetSystemPagesAccess(
77     void* address,
78     size_t length,
79     PageAccessibilityConfiguration page_accessibility);
80 
81 // Mark one or more system pages, starting at |address| with the given
82 // |page_accessibility|. |length| must be a multiple of |kSystemPageSize| bytes.
83 //
84 // Performs a CHECK that the operation succeeds.
85 BASE_EXPORT void SetSystemPagesAccess(
86     void* address,
87     size_t length,
88     PageAccessibilityConfiguration page_accessibility);
89 
90 // Decommit one or more system pages starting at |address| and continuing for
91 // |length| bytes. |length| must be a multiple of |kSystemPageSize|.
92 //
93 // Decommitted means that physical resources (RAM or swap) backing the allocated
94 // virtual address range are released back to the system, but the address space
95 // is still allocated to the process (possibly using up page table entries or
96 // other accounting resources). Any access to a decommitted region of memory
97 // is an error and will generate a fault.
98 //
99 // This operation is not atomic on all platforms.
100 //
101 // Note: "Committed memory" is a Windows Memory Subsystem concept that ensures
102 // processes will not fault when touching a committed memory region. There is
103 // no analogue in the POSIX memory API where virtual memory pages are
104 // best-effort allocated resources on the first touch. To create a
105 // platform-agnostic abstraction, this API simulates the Windows "decommit"
106 // state by both discarding the region (allowing the OS to avoid swap
107 // operations) and changing the page protections so accesses fault.
108 //
109 // TODO(ajwong): This currently does not change page protections on POSIX
110 // systems due to a perf regression. Tracked at http://crbug.com/766882.
111 BASE_EXPORT void DecommitSystemPages(void* address, size_t length);
112 
113 // Recommit one or more system pages, starting at |address| and continuing for
114 // |length| bytes with the given |page_accessibility|. |length| must be a
115 // multiple of |kSystemPageSize|.
116 //
117 // Decommitted system pages must be recommitted with their original permissions
118 // before they are used again.
119 //
120 // Returns true if the recommit change succeeded. In most cases you must |CHECK|
121 // the result.
122 BASE_EXPORT WARN_UNUSED_RESULT bool RecommitSystemPages(
123     void* address,
124     size_t length,
125     PageAccessibilityConfiguration page_accessibility);
126 
127 // Discard one or more system pages starting at |address| and continuing for
128 // |length| bytes. |length| must be a multiple of |kSystemPageSize|.
129 //
130 // Discarding is a hint to the system that the page is no longer required. The
131 // hint may:
132 //   - Do nothing.
133 //   - Discard the page immediately, freeing up physical pages.
134 //   - Discard the page at some time in the future in response to memory
135 //   pressure.
136 //
137 // Only committed pages should be discarded. Discarding a page does not decommit
138 // it, and it is valid to discard an already-discarded page. A read or write to
139 // a discarded page will not fault.
140 //
141 // Reading from a discarded page may return the original page content, or a page
142 // full of zeroes.
143 //
144 // Writing to a discarded page is the only guaranteed way to tell the system
145 // that the page is required again. Once written to, the content of the page is
146 // guaranteed stable once more. After being written to, the page content may be
147 // based on the original page content, or a page of zeroes.
148 BASE_EXPORT void DiscardSystemPages(void* address, size_t length);
149 
150 // Rounds up |address| to the next multiple of |kSystemPageSize|. Returns
151 // 0 for an |address| of 0.
RoundUpToSystemPage(uintptr_t address)152 constexpr ALWAYS_INLINE uintptr_t RoundUpToSystemPage(uintptr_t address) {
153   return (address + kSystemPageOffsetMask) & kSystemPageBaseMask;
154 }
155 
156 // Rounds down |address| to the previous multiple of |kSystemPageSize|. Returns
157 // 0 for an |address| of 0.
RoundDownToSystemPage(uintptr_t address)158 constexpr ALWAYS_INLINE uintptr_t RoundDownToSystemPage(uintptr_t address) {
159   return address & kSystemPageBaseMask;
160 }
161 
162 // Rounds up |address| to the next multiple of |kPageAllocationGranularity|.
163 // Returns 0 for an |address| of 0.
164 constexpr ALWAYS_INLINE uintptr_t
RoundUpToPageAllocationGranularity(uintptr_t address)165 RoundUpToPageAllocationGranularity(uintptr_t address) {
166   return (address + kPageAllocationGranularityOffsetMask) &
167          kPageAllocationGranularityBaseMask;
168 }
169 
170 // Rounds down |address| to the previous multiple of
171 // |kPageAllocationGranularity|. Returns 0 for an |address| of 0.
172 constexpr ALWAYS_INLINE uintptr_t
RoundDownToPageAllocationGranularity(uintptr_t address)173 RoundDownToPageAllocationGranularity(uintptr_t address) {
174   return address & kPageAllocationGranularityBaseMask;
175 }
176 
177 // Reserves (at least) |size| bytes of address space, aligned to
178 // |kPageAllocationGranularity|. This can be called early on to make it more
179 // likely that large allocations will succeed. Returns true if the reservation
180 // succeeded, false if the reservation failed or a reservation was already made.
181 BASE_EXPORT bool ReserveAddressSpace(size_t size);
182 
183 // Releases any reserved address space. |AllocPages| calls this automatically on
184 // an allocation failure. External allocators may also call this on failure.
185 //
186 // Returns true when an existing reservation was released.
187 BASE_EXPORT bool ReleaseReservation();
188 
189 // Returns true if there is currently an address space reservation.
190 BASE_EXPORT bool HasReservationForTesting();
191 
192 // Returns |errno| (POSIX) or the result of |GetLastError| (Windows) when |mmap|
193 // (POSIX) or |VirtualAlloc| (Windows) fails.
194 BASE_EXPORT uint32_t GetAllocPageErrorCode();
195 
196 }  // namespace base
197 
198 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H_
199