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