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 PARTITION_ALLOC_ADDRESS_SPACE_RANDOMIZATION_H_
6 #define PARTITION_ALLOC_ADDRESS_SPACE_RANDOMIZATION_H_
7
8 #include <cstdint>
9
10 #include "build/build_config.h"
11 #include "partition_alloc/page_allocator_constants.h"
12 #include "partition_alloc/partition_alloc_base/compiler_specific.h"
13 #include "partition_alloc/partition_alloc_base/component_export.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.
107 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
ASLRMask()108 ASLRMask() {
109 return AslrMask(46);
110 }
111 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
ASLROffset()112 ASLROffset() {
113 return AslrAddress(0);
114 }
115
116 #elif BUILDFLAG(IS_ANDROID) && (defined(ARCH_CPU_ARM64) || defined(ARCH_CPU_RISCV64))
117 // Restrict the address range on Android to avoid a large performance
118 // regression in single-process WebViews. See https://crbug.com/837640.
119 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
ASLRMask()120 ASLRMask() {
121 return AslrMask(30);
122 }
123 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
ASLROffset()124 ASLROffset() {
125 return AslrAddress(0x20000000ULL);
126 }
127 #elif defined(ARCH_CPU_ARM64)
128 #if BUILDFLAG(IS_LINUX)
129
130 // Linux on arm64 can use 39, 42, 48, or 52-bit user space, depending on
131 // page size and number of levels of translation pages used. We use
132 // 39-bit as base as all setups should support this, lowered to 38-bit
133 // as ASLROffset() could cause a carry.
134 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
ASLRMask()135 ASLRMask() {
136 return AslrMask(38);
137 }
138 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
ASLROffset()139 ASLROffset() {
140 return AslrAddress(0x1000000000ULL);
141 }
142
143 #else
144
145 // ARM64 on Linux has 39-bit user space. Use 38 bits since ASLROffset()
146 // could cause a carry.
ASLRMask()147 PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() {
148 return AslrMask(38);
149 }
ASLROffset()150 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
151 return AslrAddress(0x1000000000ULL);
152 }
153
154 #endif
155
156 #elif defined(ARCH_CPU_PPC64)
157
158 #if BUILDFLAG(IS_AIX)
159
160 // AIX has 64 bits of virtual addressing, but we limit the address range
161 // to (a) minimize segment lookaside buffer (SLB) misses; and (b) use
162 // extra address space to isolate the mmap regions.
ASLRMask()163 PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() {
164 return AslrMask(30);
165 }
ASLROffset()166 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
167 return AslrAddress(0x400000000000ULL);
168 }
169
170 #elif defined(ARCH_CPU_BIG_ENDIAN)
171
172 // Big-endian Linux PPC has 44 bits of virtual addressing. Use 42.
ASLRMask()173 PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() {
174 return AslrMask(42);
175 }
ASLROffset()176 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
177 return AslrAddress(0);
178 }
179
180 #else // !BUILDFLAG(IS_AIX) && !defined(ARCH_CPU_BIG_ENDIAN)
181
182 // Little-endian Linux PPC has 48 bits of virtual addressing. Use 46.
ASLRMask()183 PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() {
184 return AslrMask(46);
185 }
ASLROffset()186 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
187 return AslrAddress(0);
188 }
189
190 #endif // !BUILDFLAG(IS_AIX) && !defined(ARCH_CPU_BIG_ENDIAN)
191
192 #elif defined(ARCH_CPU_S390X)
193
194 // Linux on Z uses bits 22 - 32 for Region Indexing, which translates to
195 // 42 bits of virtual addressing. Truncate to 40 bits to allow kernel a
196 // chance to fulfill the request.
ASLRMask()197 PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() {
198 return AslrMask(40);
199 }
ASLROffset()200 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
201 return AslrAddress(0);
202 }
203
204 #elif defined(ARCH_CPU_S390)
205
206 // 31 bits of virtual addressing. Truncate to 29 bits to allow the kernel
207 // a chance to fulfill the request.
ASLRMask()208 PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() {
209 return AslrMask(29);
210 }
ASLROffset()211 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
212 return AslrAddress(0);
213 }
214
215 #else // !defined(ARCH_CPU_X86_64) && !defined(ARCH_CPU_PPC64) &&
216 // !defined(ARCH_CPU_S390X) && !defined(ARCH_CPU_S390)
217
218 // For all other POSIX variants, use 30 bits.
219 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
ASLRMask()220 ASLRMask() {
221 return AslrMask(30);
222 }
223
224 #if BUILDFLAG(IS_SOLARIS)
225
226 // For our Solaris/illumos mmap hint, we pick a random address in the
227 // bottom half of the top half of the address space (that is, the third
228 // quarter). Because we do not MAP_FIXED, this will be treated only as a
229 // hint -- the system will not fail to mmap because something else
230 // happens to already be mapped at our random address. We deliberately
231 // set the hint high enough to get well above the system's break (that
232 // is, the heap); Solaris and illumos will try the hint and if that
233 // fails allocate as if there were no hint at all. The high hint
234 // prevents the break from getting hemmed in at low values, ceding half
235 // of the address space to the system heap.
ASLROffset()236 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
237 return AslrAddress(0x80000000ULL);
238 }
239
240 #elif BUILDFLAG(IS_AIX)
241
242 // The range 0x30000000 - 0xD0000000 is available on AIX; choose the
243 // upper range.
ASLROffset()244 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
245 return AslrAddress(0x90000000ULL);
246 }
247
248 #else // !BUILDFLAG(IS_SOLARIS) && !BUILDFLAG(IS_AIX)
249
250 // The range 0x20000000 - 0x60000000 is relatively unpopulated across a
251 // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macOS
252 // 10.6 and 10.7.
253 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t
ASLROffset()254 ASLROffset() {
255 return AslrAddress(0x20000000ULL);
256 }
257
258 #endif // !BUILDFLAG(IS_SOLARIS) && !BUILDFLAG(IS_AIX)
259
260 #endif // !defined(ARCH_CPU_X86_64) && !defined(ARCH_CPU_PPC64) &&
261 // !defined(ARCH_CPU_S390X) && !defined(ARCH_CPU_S390)
262
263 #endif // BUILDFLAG(IS_POSIX)
264
265 #elif defined(ARCH_CPU_32_BITS)
266
267 // This is a good range on 32-bit Windows and Android (the only platforms on
268 // which we support 32-bitness). Allocates in the 0.5 - 1.5 GiB region. There
269 // is no issue with carries here.
ASLRMask()270 PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() {
271 return AslrMask(30);
272 }
ASLROffset()273 PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() {
274 return AslrAddress(0x20000000ULL);
275 }
276
277 #else
278
279 #error Please tell us about your exotic hardware! Sounds interesting.
280
281 #endif // defined(ARCH_CPU_32_BITS)
282
283 // clang-format on
284
285 } // namespace internal
286
287 } // namespace partition_alloc
288
289 #endif // PARTITION_ALLOC_ADDRESS_SPACE_RANDOMIZATION_H_
290