1 // Copyright 2014 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_ADDRESS_SPACE_RANDOMIZATION_H_
6 #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION_H_
7
8 #include <cstdint>
9
10 #include "base/allocator/partition_allocator/page_allocator_constants.h"
11 #include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
12 #include "base/allocator/partition_allocator/partition_alloc_base/component_export.h"
13 #include "build/build_config.h"
14
15 namespace partition_alloc {
16
17 // Calculates a random preferred mapping address. In calculating an address, we
18 // balance good ASLR against not fragmenting the address space too badly.
19 PA_COMPONENT_EXPORT(PARTITION_ALLOC) uintptr_t GetRandomPageBase();
20
21 namespace internal {
22
23 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
AslrAddress(uintptr_t mask)24 AslrAddress(uintptr_t mask) {
25 return mask & PageAllocationGranularityBaseMask();
26 }
27 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
AslrMask(uintptr_t bits)28 AslrMask(uintptr_t bits) {
29 return AslrAddress((1ULL << bits) - 1ULL);
30 }
31
32 // Turn off formatting, because the thicket of nested ifdefs below is
33 // incomprehensible without indentation. It is also incomprehensible with
34 // indentation, but the only other option is a combinatorial explosion of
35 // *_{win,linux,mac,foo}_{32,64}.h files.
36 //
37 // clang-format off
38
39 #if defined(ARCH_CPU_64_BITS)
40
41 #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
42
43 // We shouldn't allocate system pages at all for sanitizer builds. However,
44 // we do, and if random hint addresses interfere with address ranges
45 // hard-coded in those tools, bad things happen. This address range is
46 // copied from TSAN source but works with all tools. See
47 // https://crbug.com/539863.
48 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
ASLRMask()49 ASLRMask() {
50 return AslrAddress(0x007fffffffffULL);
51 }
52 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
ASLROffset()53 ASLROffset() {
54 return AslrAddress(0x7e8000000000ULL);
55 }
56
57 #elif BUILDFLAG(IS_WIN)
58
59 // Windows 8.10 and newer support the full 48 bit address range. Since
60 // ASLROffset() is non-zero and may cause a carry, use 47 bit masks. See
61 // http://www.alex-ionescu.com/?p=246
ASLRMask()62 PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() {
63 return AslrMask(47);
64 }
65 // Try not to map pages into the range where Windows loads DLLs by default.
ASLROffset()66 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
67 return 0x80000000ULL;
68 }
69
70 #elif BUILDFLAG(IS_APPLE)
71
72 // macOS as of 10.12.5 does not clean up entries in page map levels 3/4
73 // [PDP/PML4] created from mmap or mach_vm_allocate, even after the region
74 // is destroyed. Using a virtual address space that is too large causes a
75 // leak of about 1 wired [can never be paged out] page per call to mmap. The
76 // page is only reclaimed when the process is killed. Confine the hint to a
77 // 39-bit section of the virtual address space.
78 //
79 // This implementation adapted from
80 // https://chromium-review.googlesource.com/c/v8/v8/+/557958. The difference
81 // is that here we clamp to 39 bits, not 32.
82 //
83 // TODO(crbug.com/738925): Remove this limitation if/when the macOS behavior
84 // changes.
85 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
ASLRMask()86 ASLRMask() {
87 return AslrMask(38);
88 }
89 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
ASLROffset()90 ASLROffset() {
91 // Be careful, there is a zone where macOS will not map memory, at least
92 // on ARM64. From an ARM64 machine running 12.3, the range seems to be
93 // [0x1000000000, 0x7000000000). Make sure that the range we use is
94 // outside these bounds. In 12.3, there is a reserved area between
95 // MACH_VM_MIN_GPU_CARVEOUT_ADDRESS and MACH_VM_MAX_GPU_CARVEOUT_ADDRESS,
96 // which is reserved on ARM64. See these constants in XNU's source code
97 // for details (xnu-8019.80.24/osfmk/mach/arm/vm_param.h).
98 return AslrAddress(0x10000000000ULL);
99 }
100
101 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
102
103 #if defined(ARCH_CPU_X86_64)
104
105 // Linux (and macOS) support the full 47-bit user space of x64 processors.
106 // Use only 46 to allow the kernel a chance to fulfill the request.
ASLRMask()107 PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() {
108 return AslrMask(46);
109 }
ASLROffset()110 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
111 return AslrAddress(0);
112 }
113
114 #elif defined(ARCH_CPU_ARM64)
115
116 #if BUILDFLAG(IS_ANDROID)
117
118 // Restrict the address range on Android to avoid a large performance
119 // regression in single-process WebViews. See https://crbug.com/837640.
ASLRMask()120 PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() {
121 return AslrMask(30);
122 }
ASLROffset()123 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
124 return AslrAddress(0x20000000ULL);
125 }
126
127 #elif BUILDFLAG(IS_LINUX)
128
129 // Linux on arm64 can use 39, 42, 48, or 52-bit user space, depending on
130 // page size and number of levels of translation pages used. We use
131 // 39-bit as base as all setups should support this, lowered to 38-bit
132 // as ASLROffset() could cause a carry.
133 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
ASLRMask()134 ASLRMask() {
135 return AslrMask(38);
136 }
137 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
ASLROffset()138 ASLROffset() {
139 return AslrAddress(0x1000000000ULL);
140 }
141
142 #else
143
144 // ARM64 on Linux has 39-bit user space. Use 38 bits since ASLROffset()
145 // could cause a carry.
ASLRMask()146 PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() {
147 return AslrMask(38);
148 }
ASLROffset()149 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
150 return AslrAddress(0x1000000000ULL);
151 }
152
153 #endif
154
155 #elif defined(ARCH_CPU_PPC64)
156
157 #if BUILDFLAG(IS_AIX)
158
159 // AIX has 64 bits of virtual addressing, but we limit the address range
160 // to (a) minimize segment lookaside buffer (SLB) misses; and (b) use
161 // extra address space to isolate the mmap regions.
ASLRMask()162 PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() {
163 return AslrMask(30);
164 }
ASLROffset()165 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
166 return AslrAddress(0x400000000000ULL);
167 }
168
169 #elif defined(ARCH_CPU_BIG_ENDIAN)
170
171 // Big-endian Linux PPC has 44 bits of virtual addressing. Use 42.
ASLRMask()172 PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() {
173 return AslrMask(42);
174 }
ASLROffset()175 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
176 return AslrAddress(0);
177 }
178
179 #else // !BUILDFLAG(IS_AIX) && !defined(ARCH_CPU_BIG_ENDIAN)
180
181 // Little-endian Linux PPC has 48 bits of virtual addressing. Use 46.
ASLRMask()182 PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() {
183 return AslrMask(46);
184 }
ASLROffset()185 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
186 return AslrAddress(0);
187 }
188
189 #endif // !BUILDFLAG(IS_AIX) && !defined(ARCH_CPU_BIG_ENDIAN)
190
191 #elif defined(ARCH_CPU_S390X)
192
193 // Linux on Z uses bits 22 - 32 for Region Indexing, which translates to
194 // 42 bits of virtual addressing. Truncate to 40 bits to allow kernel a
195 // chance to fulfill the request.
ASLRMask()196 PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() {
197 return AslrMask(40);
198 }
ASLROffset()199 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
200 return AslrAddress(0);
201 }
202
203 #elif defined(ARCH_CPU_S390)
204
205 // 31 bits of virtual addressing. Truncate to 29 bits to allow the kernel
206 // a chance to fulfill the request.
ASLRMask()207 PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() {
208 return AslrMask(29);
209 }
ASLROffset()210 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
211 return AslrAddress(0);
212 }
213
214 #else // !defined(ARCH_CPU_X86_64) && !defined(ARCH_CPU_PPC64) &&
215 // !defined(ARCH_CPU_S390X) && !defined(ARCH_CPU_S390)
216
217 // For all other POSIX variants, use 30 bits.
ASLRMask()218 PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() {
219 return AslrMask(30);
220 }
221
222 #if BUILDFLAG(IS_SOLARIS)
223
224 // For our Solaris/illumos mmap hint, we pick a random address in the
225 // bottom half of the top half of the address space (that is, the third
226 // quarter). Because we do not MAP_FIXED, this will be treated only as a
227 // hint -- the system will not fail to mmap because something else
228 // happens to already be mapped at our random address. We deliberately
229 // set the hint high enough to get well above the system's break (that
230 // is, the heap); Solaris and illumos will try the hint and if that
231 // fails allocate as if there were no hint at all. The high hint
232 // prevents the break from getting hemmed in at low values, ceding half
233 // of the address space to the system heap.
ASLROffset()234 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
235 return AslrAddress(0x80000000ULL);
236 }
237
238 #elif BUILDFLAG(IS_AIX)
239
240 // The range 0x30000000 - 0xD0000000 is available on AIX; choose the
241 // upper range.
ASLROffset()242 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
243 return AslrAddress(0x90000000ULL);
244 }
245
246 #else // !BUILDFLAG(IS_SOLARIS) && !BUILDFLAG(IS_AIX)
247
248 // The range 0x20000000 - 0x60000000 is relatively unpopulated across a
249 // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macOS
250 // 10.6 and 10.7.
ASLROffset()251 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
252 return AslrAddress(0x20000000ULL);
253 }
254
255 #endif // !BUILDFLAG(IS_SOLARIS) && !BUILDFLAG(IS_AIX)
256
257 #endif // !defined(ARCH_CPU_X86_64) && !defined(ARCH_CPU_PPC64) &&
258 // !defined(ARCH_CPU_S390X) && !defined(ARCH_CPU_S390)
259
260 #endif // BUILDFLAG(IS_POSIX)
261
262 #elif defined(ARCH_CPU_32_BITS)
263
264 // This is a good range on 32-bit Windows and Android (the only platforms on
265 // which we support 32-bitness). Allocates in the 0.5 - 1.5 GiB region. There
266 // is no issue with carries here.
ASLRMask()267 PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() {
268 return AslrMask(30);
269 }
ASLROffset()270 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
271 return AslrAddress(0x20000000ULL);
272 }
273
274 #else
275
276 #error Please tell us about your exotic hardware! Sounds interesting.
277
278 #endif // defined(ARCH_CPU_32_BITS)
279
280 // clang-format on
281
282 } // namespace internal
283
284 } // namespace partition_alloc
285
286 #endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION_H_
287