1 : /*
2 : * Copyright (c) 2010 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 : #include <stdio.h>
8 : #include <stdint.h>
9 : #include <sys/mman.h>
10 : #include <sys/types.h>
11 :
12 : #include "native_client/src/shared/platform/nacl_find_addrsp.h"
13 :
14 : #include "native_client/src/include/nacl_base.h"
15 : #include "native_client/src/include/nacl_platform.h"
16 : #include "native_client/src/include/portability.h"
17 : #include "native_client/src/shared/platform/nacl_check.h"
18 : #include "native_client/src/shared/platform/nacl_global_secure_random.h"
19 : #include "native_client/src/shared/platform/nacl_log.h"
20 :
21 :
22 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64
23 : /*
24 : * The x86-64 (currently) permits only a 48 bit virtual address
25 : * space (and requires that the upper 64-48=16 bits is sign extended
26 : * from bit 47). Additionally, the linux kernel disallows negative
27 : * addresses for user-space code. So instead of 48, we get a
28 : * maximum of 47 usable bits of virtual address.
29 : */
30 : # define NUM_USER_ADDRESS_BITS 47
31 : /*
32 : * Don't assume __LP64__ is defined, even though we're POSIX and thus
33 : * we are compiling with gcc or gcc-compatible compilers.
34 : */
35 : # define NACL_POINTER_SIZE 64
36 :
37 : #elif (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32) || \
38 : NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm
39 : /*
40 : * The x86-32 / ARM architectures use a 32-bit address space, but it is common
41 : * for the kernel to reserve the high 1 or 2 GB. In order to allow the start
42 : * address to be in the 2GB-3GB range, we choose from all possible 32-bit
43 : * addresses.
44 : */
45 : # define NUM_USER_ADDRESS_BITS 32
46 : # define NACL_POINTER_SIZE 32
47 :
48 : #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
49 : /*
50 : * The MIPS architecture uses a 32-bit address space, but it is common for the
51 : * kernel to reserve the high 1 or 2 GB. The current MIPS platforms we target
52 : * will not be making use of the address space in the 2GB-4GB range.
53 : */
54 : # define NUM_USER_ADDRESS_BITS 31
55 : # define NACL_POINTER_SIZE 32
56 :
57 : #else
58 : # error "Is this a 32-bit or 64-bit architecture?"
59 : #endif
60 :
61 : #define NACL_VALGRIND_MAP_FAILED_COUNT 10000
62 :
63 : /* bool */
64 380 : int NaClFindAddressSpaceRandomized(uintptr_t *addr, size_t memory_size,
65 380 : int max_tries) {
66 380 : void *map_addr;
67 380 : int tries_remaining;
68 : /*
69 : * Mask for keeping the low order NUM_USER_ADDRESS_BITS of a randomly
70 : * generated address.
71 : */
72 380 : uintptr_t addr_mask;
73 380 : uintptr_t suggested_addr;
74 380 : int mmap_type;
75 :
76 380 : size_t map_failed_count_left = NACL_VALGRIND_MAP_FAILED_COUNT;
77 :
78 1140 : CHECK(max_tries >= 0);
79 380 : NaClLog(4,
80 : "NaClFindAddressSpaceRandomized: looking for %"NACL_PRIxS" bytes\n",
81 : memory_size);
82 380 : NaClLog(4, "NaClFindAddressSpaceRandomized: max %d tries\n", max_tries);
83 :
84 : if (NACL_ARCH(NACL_BUILD_ARCH) != NACL_mips) {
85 : /* 4kB pages */
86 380 : addr_mask = ~(((uintptr_t) 1 << 12) - 1);
87 380 : mmap_type = MAP_PRIVATE;
88 : } else {
89 : /*
90 : * 8kB pages in userspace memory region.
91 : * Apart from system page size, MIPS shared memory mask in kernel can depend
92 : * on dcache attributes. Ideally, we could use kernel constant SHMLBA, but
93 : * it is too large. Common case is that 8kB is sufficient.
94 : * As shared memory mask alignment is more rigid on MIPS, we need to pass
95 : * MAP_SHARED type to mmap, so it can return value applicable both for
96 : * private and shared mapping.
97 : */
98 : addr_mask = ~(((uintptr_t) 1 << 13) - 1);
99 : mmap_type = MAP_SHARED;
100 : }
101 : /*
102 : * We cannot just do
103 : *
104 : * if (NUM_USER_ADDRESS_BITS < sizeof(uintptr_t) * 8) {
105 : * addr_mask &= (((uintptr_t) 1 << NUM_USER_ADDRESS_BITS) - 1);
106 : * }
107 : *
108 : * since when NUM_USER_ADDRESS_BITS is defined to be 32, for
109 : * example, and sizeof(uintptr_t) is 4, then even though the
110 : * constant expression NUM_USER_ADDRESS_BITS < sizeof(uintptr_t) * 8
111 : * is false, the compiler still parses the block of code controlled
112 : * by the conditional. And the warning for shifting by too many
113 : * bits would be produced because we'd venture into
114 : * undefined-behavior territory:
115 : *
116 : * 6.5.7.3: "The integer promotions are performed on each of the
117 : * operands. The type of the result is that of the promoted left
118 : * operand. If the value of the right operand is negative or is
119 : * greater than or equal to the width of the promoted left
120 : * operand, the behavior is undefined."
121 : *
122 : * Since we compile with -Wall and -Werror, this would lead to a
123 : * build failure.
124 : */
125 : #if NACL_POINTER_SIZE > NUM_USER_ADDRESS_BITS
126 380 : addr_mask &= (((uintptr_t) 1 << NUM_USER_ADDRESS_BITS) - 1);
127 : #endif
128 :
129 380 : tries_remaining = max_tries;
130 380 : for (;;) {
131 : #if NACL_POINTER_SIZE > 32
132 380 : suggested_addr = (((uintptr_t) NaClGlobalSecureRngUint32() << 32) |
133 380 : (uintptr_t) NaClGlobalSecureRngUint32());
134 : #else
135 : suggested_addr = ((uintptr_t) NaClGlobalSecureRngUint32());
136 : #endif
137 380 : suggested_addr &= addr_mask;
138 :
139 380 : NaClLog(4,
140 : ("NaClFindAddressSpaceRandomized: non-MAP_FAILED tries"
141 : " remaining %d, hint addr %"NACL_PRIxPTR"\n"),
142 : tries_remaining,
143 : suggested_addr);
144 380 : map_addr = mmap((void *) suggested_addr, memory_size,
145 : PROT_NONE,
146 : MAP_ANONYMOUS | MAP_NORESERVE | mmap_type,
147 : -1,
148 : 0);
149 : /*
150 : * On most POSIX systems, the mmap syscall, without MAP_FIXED,
151 : * will use the first parameter (actual argument suggested_addr)
152 : * as a hint for where to find a hole in the address space for the
153 : * new memory -- often as a starting point for a search.
154 : *
155 : * However, when Valgrind is used (3.7.0 to 3.8.1 at least), the
156 : * mmap syscall is replaced with wrappers which do not behave
157 : * correctly: the Valgrind-provided mmap replacement will return
158 : * MAP_FAILED instead of ignoring the hint, until the "Warning:
159 : * set address range perms: large range [0x...., 0x....]
160 : * (noaccess)" message to warn about large allocations shows up.
161 : * So in order for this code to not fail when run under Valgrind,
162 : * we have to ignore MAP_FAILED and not count these attempts
163 : * against randomization failures.
164 : */
165 380 : if (MAP_FAILED == map_addr) {
166 0 : if (--map_failed_count_left != 0) {
167 0 : NaClLog(LOG_INFO, "NaClFindAddressSpaceRandomized: MAP_FAILED\n");
168 0 : } else {
169 0 : NaClLog(LOG_ERROR,
170 : "NaClFindAddressSpaceRandomized: too many MAP_FAILED\n");
171 0 : return 0;
172 : }
173 0 : } else {
174 : /*
175 : * mmap will use a system-dependent algorithm to find a starting
176 : * address if the hint location cannot work, e.g., if there is
177 : * already memory mapped there. We do not trust that algorithm
178 : * to provide any randomness in the high-order bits, so we only
179 : * accept allocations that match the requested high-order bits
180 : * exactly. If the algorithm is to scan the address space
181 : * starting near the hint address, then it would be acceptable;
182 : * if it is to use a default algorithm that is independent of
183 : * the supplied hint, then it would not be.
184 : */
185 380 : if ((addr_mask & ((uintptr_t) map_addr)) == suggested_addr) {
186 380 : NaClLog(5,
187 : "NaClFindAddressSpaceRandomized: high order bits matched.\n");
188 : /* success */
189 380 : break;
190 : }
191 :
192 0 : if (0 == tries_remaining--) {
193 : /* give up on retrying, and just use what was returned */
194 0 : NaClLog(5, "NaClFindAddressSpaceRandomized: last try, taking as is.\n");
195 0 : break;
196 : }
197 : /*
198 : * Remove undesirable mapping location before trying again.
199 : */
200 0 : if (-1 == munmap(map_addr, memory_size)) {
201 0 : NaClLog(LOG_FATAL,
202 : "NaClFindAddressSpaceRandomized: could not unmap non-random"
203 : " memory\n");
204 0 : }
205 : }
206 0 : }
207 380 : NaClLog(4,
208 : "NaClFindAddressSpaceRandomized: got addr %"NACL_PRIxPTR"\n",
209 : (uintptr_t) map_addr);
210 380 : *addr = (uintptr_t) map_addr;
211 380 : return 1;
212 380 : }
213 :
214 103 : int NaClFindAddressSpace(uintptr_t *addr, size_t memory_size) {
215 103 : return NaClFindAddressSpaceRandomized(addr, memory_size, 0);
216 : }
|