1 : /*
2 : * Copyright (c) 2012 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 : /*
8 : * NaCl Service Runtime memory allocation code
9 : */
10 :
11 : #include <sys/mman.h>
12 : #include <sys/stat.h>
13 : #include <sys/types.h>
14 :
15 : #include <errno.h>
16 : #include <fcntl.h>
17 : #include <stdint.h>
18 : #include <stdio.h>
19 : #include <string.h>
20 : #include <unistd.h>
21 :
22 : #include "native_client/src/include/nacl_platform.h"
23 : #include "native_client/src/include/portability.h"
24 : #include "native_client/src/shared/platform/nacl_exit.h"
25 : #include "native_client/src/shared/platform/nacl_global_secure_random.h"
26 : #include "native_client/src/shared/platform/nacl_log.h"
27 : #include "native_client/src/trusted/service_runtime/sel_memory.h"
28 : #include "native_client/src/trusted/service_runtime/nacl_config.h"
29 : #include "native_client/src/trusted/service_runtime/include/machine/_types.h"
30 :
31 : /*
32 : * When we're built into Chromium's "nacl_helper", its main will set this.
33 : */
34 : void *g_nacl_prereserved_sandbox_addr = NULL;
35 :
36 : /*
37 : * Find sandbox memory pre-reserved by the nacl_helper in chrome. The
38 : * nacl_helper, if present, reserves the bottom 1G of the address space
39 : * for use by Native Client.
40 : *
41 : * NOTE: num_bytes is currently ignored. It should be 1GB on Linux and
42 : * 1GB plus a few pages on ARM. TODO(bradchen): deal with num_bytes.
43 : *
44 : * Out parameter p should be either:
45 : * 0: reserved memory was not found
46 : * less than 128K: indicates the bottom 1G was reserved.
47 : */
48 : int NaCl_find_prereserved_sandbox_memory(void **p,
49 0 : size_t num_bytes) {
50 : UNREFERENCED_PARAMETER(num_bytes);
51 :
52 0 : NaClLog(2,
53 : "NaCl_find_prereserved_sandbox_memory(, %#.8"NACL_PRIxPTR") => %p\n",
54 : num_bytes, g_nacl_prereserved_sandbox_addr);
55 :
56 0 : *p = g_nacl_prereserved_sandbox_addr;
57 0 : return g_nacl_prereserved_sandbox_addr != NULL;
58 : }
59 :
60 : void NaCl_page_free(void *p,
61 1006 : size_t size) {
62 1006 : if (p == 0 || size == 0)
63 0 : return;
64 1006 : if (munmap(p, size) != 0) {
65 0 : NaClLog(LOG_FATAL, "NaCl_page_free: munmap() failed");
66 : }
67 : }
68 :
69 : /*
70 : * NaCl_page_alloc_intern_flags
71 : */
72 : static
73 : int NaCl_page_alloc_intern_flags(void **p,
74 : size_t size,
75 1010 : int map_flags) {
76 : void *addr;
77 :
78 1010 : map_flags |= MAP_PRIVATE | MAP_ANONYMOUS;
79 :
80 1010 : NaClLog(4,
81 : "sel_memory: NaCl_page_alloc_intern:"
82 : " mmap(%p, %"NACL_PRIxS", %#x, %#x, %d, %"NACL_PRIdNACL_OFF64")\n",
83 : *p, size, PROT_NONE, map_flags, -1,
84 : (nacl_abi_off64_t) 0);
85 1010 : addr = mmap(*p, size, PROT_NONE, map_flags, -1, (off_t) 0);
86 1010 : if (MAP_FAILED == addr) {
87 0 : addr = NULL;
88 : }
89 1010 : if (NULL != addr) {
90 1010 : *p = addr;
91 : }
92 1010 : return (NULL == addr) ? -ENOMEM : 0;
93 : }
94 :
95 : /*
96 : * Note that NaCl_page_alloc does not allocate pages that satisify
97 : * NaClIsAllocPageMultiple. On linux/osx, the system does not impose
98 : * any such restrictions, and we only need to enforce the restriction
99 : * on NaCl app code to ensure that the app code is portable across all
100 : * host OSes.
101 : */
102 : static
103 : int NaCl_page_alloc_intern(void **p,
104 1007 : size_t size) {
105 1007 : int map_flags = 0;
106 :
107 1007 : if (NULL != *p) {
108 0 : map_flags |= MAP_FIXED;
109 : }
110 : #if NACL_LINUX
111 : /*
112 : * Indicate to the kernel that we just want these pages allocated, not
113 : * committed. This is important for systems with relatively little RAM+swap.
114 : * See bug 251.
115 : */
116 : map_flags |= MAP_NORESERVE;
117 : #elif NACL_OSX
118 : /*
119 : * TODO(cbiffle): Contrary to its name, this file is used by Mac OS X as well
120 : * as Linux. An equivalent fix may require this to stop, since we might have
121 : * to drop to the xnu layer and use vm_allocate.
122 : *
123 : * Currently this code is not guaranteed to work for non-x86-32 Mac OS X.
124 : */
125 : #else
126 : # error This file should be included only by Linux and (surprisingly) OS X.
127 : #endif
128 1007 : return NaCl_page_alloc_intern_flags(p, size, map_flags);
129 : }
130 :
131 : /*
132 : * Pick a "hint" address that is random.
133 : */
134 : int NaCl_page_alloc_randomized(void **p,
135 3 : size_t size) {
136 : uintptr_t addr;
137 3 : int neg_errno = -ENOMEM; /* in case we change kNumTries to 0 */
138 : int tries;
139 3 : const int kNumTries = 4;
140 : /*
141 : * linux permits 128 TB of user address space.
142 : */
143 :
144 3 : for (tries = 0; tries < kNumTries; ++tries) {
145 : #if NACL_HOST_WORDSIZE == 32
146 3 : addr = NaClGlobalSecureRngUint32();
147 3 : NaClLog(2, "NaCl_page_alloc_randomized: 0x%"NACL_PRIxPTR"\n", addr);
148 : /* linux permits 3-4 GB of user address space */
149 3 : *p = (void *) (addr & ~((uintptr_t) NACL_MAP_PAGESIZE - 1)
150 : & ((~(uintptr_t) 0) >> 1));
151 : #elif NACL_HOST_WORDSIZE == 64
152 : addr = NaClGlobalSecureRngUint32();
153 : NaClLog(2, "NaCl_page_alloc_randomized: 0x%"NACL_PRIxPTR"\n", addr);
154 : /*
155 : * linux permits 128 TB of user address space, and we keep the low
156 : * 16 bits free (64K alignment to match Windows), so we have
157 : * 47-16=31 bits of entropy.
158 : */
159 : *p = (void *) ((addr << NACL_MAP_PAGESHIFT) /* bits [47:16] are random */
160 : & ((((uintptr_t) 1) << 47) - 1)); /* now bits [46:16] */
161 : #else
162 : # error "where am i?"
163 : #endif
164 :
165 3 : NaClLog(2, "NaCl_page_alloc_randomized: hint 0x%"NACL_PRIxPTR"\n",
166 : (uintptr_t) *p);
167 3 : neg_errno = NaCl_page_alloc_intern_flags(p, size, 0);
168 3 : if (0 == neg_errno) {
169 3 : break;
170 : }
171 : }
172 3 : if (0 != neg_errno) {
173 0 : NaClLog(LOG_INFO,
174 : "NaCl_page_alloc_randomized: failed (%d), dropping hints\n",
175 : -neg_errno);
176 0 : *p = 0;
177 0 : neg_errno = NaCl_page_alloc_intern_flags(p, size, 0);
178 : }
179 3 : return neg_errno;
180 : }
181 :
182 : int NaCl_page_alloc(void **p,
183 1007 : size_t size) {
184 1007 : void *addr = NULL;
185 : int rv;
186 :
187 1007 : if (0 == (rv = NaCl_page_alloc_intern(&addr, size))) {
188 1007 : *p = addr;
189 : }
190 :
191 1007 : return rv;
192 : }
193 :
194 : int NaCl_page_alloc_at_addr(void **p,
195 0 : size_t size) {
196 0 : return NaCl_page_alloc_intern(p, size);
197 : }
198 :
199 : /*
200 : * This is critical to make the text region non-writable, and the data
201 : * region read/write but no exec. Of course, some kernels do not
202 : * respect the lack of PROT_EXEC.
203 : */
204 : int NaCl_mprotect(void *addr,
205 : size_t len,
206 31 : int prot) {
207 31 : int ret = mprotect(addr, len, prot);
208 :
209 31 : return ret == -1 ? -errno : ret;
210 : }
211 :
212 :
213 : int NaCl_madvise(void *start,
214 : size_t length,
215 3 : int advice) {
216 3 : int ret = madvise(start, length, advice);
217 :
218 : /*
219 : * MADV_DONTNEED and MADV_NORMAL are needed
220 : */
221 3 : return ret == -1 ? -errno : ret;
222 : }
|