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