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