1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "berberis/base/mmap.h"
18
19 #include <sys/mman.h>
20
21 #include <atomic>
22 #include <cstdint>
23 #include <cstdlib>
24 #include <random> // for old versions of GLIBC only (see below)
25
26 #include "berberis/base/checks.h"
27
28 namespace berberis {
29
30 #if defined(__LP64__) && !defined(__x86_64__)
31 namespace {
32
33 // arc4random was introduced in GLIBC 2.36
34 #if defined(__GLIBC__) && ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 36)))
arc4random_uniform(uint32_t upper_bound)35 uint32_t arc4random_uniform(uint32_t upper_bound) {
36 // Fall back to implementation-defined stl random
37 static std::random_device random_device("/dev/urandom");
38 static std::mt19937 generator(random_device());
39 std::uniform_int_distribution<uint32_t> distrib(0, upper_bound);
40 return distrib(generator);
41 }
42 #endif
43
TryMmap32Bit(MmapImplArgs args)44 void* TryMmap32Bit(MmapImplArgs args) {
45 // Outside of x86_64 mapping in the lower 32bit address space
46 // is achieved by trying to map at the random 32bit address with
47 // hint and then verifying that the resulted map indeed falls in
48 // lower 32bit address space. Note that if another mapping already
49 // exists "the kernel picks a new address that may or may not
50 // depend on the hint." which makes it more difficult.
51
52 constexpr uintptr_t kMinAddress = 0x10000;
53
54 // This is always positive hence no sign-extend.
55 constexpr uintptr_t kMaxAddress = std::numeric_limits<int32_t>::max();
56
57 // This number is somewhat arbitrary. We want it to be big enough so that it
58 // doesn't fail prematurely when 2G space has lower availability, but not too
59 // big so it doesn't take forever.
60 constexpr size_t kMaxMapAttempts = 512;
61
62 static std::atomic<uintptr_t> saved_hint = 0;
63 uintptr_t hint = saved_hint.load();
64
65 uintptr_t arc4_random_upper_bound = kMaxAddress - kMinAddress;
66
67 if (args.size == 0) {
68 return MAP_FAILED;
69 }
70
71 if (__builtin_usubl_overflow(arc4_random_upper_bound, args.size, &arc4_random_upper_bound)) {
72 return MAP_FAILED;
73 }
74 CHECK_LE(arc4_random_upper_bound, kMaxAddress - kMinAddress);
75
76 if (hint == 0 || hint > (arc4_random_upper_bound + kMinAddress)) {
77 hint = arc4random_uniform(static_cast<uint32_t>(arc4_random_upper_bound)) + kMinAddress;
78 }
79
80 for (size_t i = 0; i < kMaxMapAttempts; i++) {
81 // PROT_NONE, MAP_NORESERVE to make it faster since this may take several attempts.
82 // We'll do another mmap() with proper flags on top of this one below.
83 void* addr = mmap(reinterpret_cast<void*>(hint),
84 args.size,
85 PROT_NONE,
86 MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE,
87 0,
88 0);
89 if (addr == MAP_FAILED) {
90 return MAP_FAILED;
91 }
92
93 uintptr_t start = reinterpret_cast<uintptr_t>(addr);
94 uintptr_t end = start + args.size;
95
96 if (end <= kMaxAddress) {
97 saved_hint.store(AlignUpPageSize(end)); // next hint
98 return mmap(addr, args.size, args.prot, MAP_FIXED | args.flags, args.fd, args.offset);
99 }
100
101 hint = arc4random_uniform(static_cast<uint32_t>(arc4_random_upper_bound)) + kMinAddress;
102 }
103
104 saved_hint.store(0);
105 return MAP_FAILED;
106 }
107
108 } // namespace
109
110 #endif // defined(__LP64__) && !defined(__x86_64__)
111
MmapImpl(MmapImplArgs args)112 void* MmapImpl(MmapImplArgs args) {
113 if ((args.berberis_flags & kMmapBerberis32Bit) != 0) {
114 // This doesn't make sense for MAP_FIXED
115 CHECK_EQ(args.flags & MAP_FIXED, 0);
116 #if defined(__x86_64__)
117 args.flags |= MAP_32BIT;
118 #elif defined(__LP64__)
119 return TryMmap32Bit(args);
120 #endif
121 }
122 return mmap(args.addr, args.size, args.prot, args.flags, args.fd, args.offset);
123 }
124
MmapImplOrDie(MmapImplArgs args)125 void* MmapImplOrDie(MmapImplArgs args) {
126 void* ptr = MmapImpl(args);
127 CHECK_NE(ptr, MAP_FAILED);
128 return ptr;
129 }
130
MunmapOrDie(void * ptr,size_t size)131 void MunmapOrDie(void* ptr, size_t size) {
132 int res = munmap(ptr, size);
133 CHECK_EQ(res, 0);
134 }
135
MprotectOrDie(void * ptr,size_t size,int prot)136 void MprotectOrDie(void* ptr, size_t size, int prot) {
137 int res = mprotect(ptr, size, prot);
138 CHECK_EQ(res, 0);
139 }
140
141 } // namespace berberis
142