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 1264 : void NaClPageFree(void *p, size_t size) {
32 2528 : if (p == 0 || size == 0)
33 0 : return;
34 1264 : if (munmap(p, size) != 0) {
35 0 : NaClLog(LOG_FATAL, "NaClPageFree: munmap() failed");
36 0 : }
37 1264 : }
38 :
39 1270 : static int NaClPageAllocInternalFlags(void **p, size_t size, int map_flags) {
40 1270 : void *addr;
41 :
42 1270 : map_flags |= MAP_PRIVATE | MAP_ANONYMOUS;
43 :
44 1270 : NaClLog(4,
45 : "NaClPageAllocInternalFlags:"
46 : " mmap(%p, %"NACL_PRIxS", %#x, %#x, %d, %"NACL_PRIdNACL_OFF64")\n",
47 : *p, size, PROT_NONE, map_flags, -1,
48 : (nacl_abi_off64_t) 0);
49 1270 : addr = mmap(*p, size, PROT_NONE, map_flags, -1, (off_t) 0);
50 1270 : if (MAP_FAILED == addr) {
51 0 : addr = NULL;
52 0 : }
53 1270 : if (NULL != addr) {
54 1270 : *p = addr;
55 1270 : }
56 1270 : return (NULL == addr) ? -ENOMEM : 0;
57 : }
58 :
59 : /*
60 : * Note that NaClPageAlloc does not allocate pages that satisify
61 : * NaClIsAllocPageMultiple. On linux/osx, the system does not impose
62 : * any such restrictions, and we only need to enforce the restriction
63 : * on NaCl app code to ensure that the app code is portable across all
64 : * host OSes.
65 : */
66 1005 : static int NaClPageAllocInternal(void **p, size_t size) {
67 1005 : int map_flags = 0;
68 :
69 1005 : if (NULL != *p) {
70 0 : map_flags |= MAP_FIXED;
71 0 : }
72 : #if NACL_LINUX
73 : /*
74 : * Indicate to the kernel that we just want these pages allocated, not
75 : * committed. This is important for systems with relatively little RAM+swap.
76 : * See bug 251.
77 : */
78 : map_flags |= MAP_NORESERVE;
79 : #elif NACL_OSX
80 : /*
81 : * TODO(cbiffle): This file is used by Mac OS X as well as Linux.
82 : * An equivalent fix may require this to stop, since we might have
83 : * to drop to the xnu layer and use vm_allocate.
84 : *
85 : * Currently this code is not guaranteed to work for non-x86-32 Mac OS X.
86 : */
87 : #else
88 : # error This file should be included only by Linux and (surprisingly) OS X.
89 : #endif
90 1005 : return NaClPageAllocInternalFlags(p, size, map_flags);
91 : }
92 :
93 : /*
94 : * Pick a "hint" address that is random.
95 : */
96 265 : int NaClPageAllocRandomized(void **p, size_t size) {
97 265 : uintptr_t addr;
98 265 : int neg_errno = -ENOMEM; /* in case we change kNumTries to 0 */
99 265 : int tries;
100 265 : const int kNumTries = 4;
101 : /*
102 : * linux permits 128 TB of user address space.
103 : */
104 :
105 530 : for (tries = 0; tries < kNumTries; ++tries) {
106 : #if NACL_HOST_WORDSIZE == 32
107 : addr = NaClGlobalSecureRngUint32();
108 : NaClLog(2, "NaClPageAllocRandomized: 0x%"NACL_PRIxPTR"\n", addr);
109 : /* linux permits 3-4 GB of user address space */
110 : *p = (void *) (addr & ~((uintptr_t) NACL_MAP_PAGESIZE - 1)
111 : & ((~(uintptr_t) 0) >> 1));
112 : #elif NACL_HOST_WORDSIZE == 64
113 265 : addr = NaClGlobalSecureRngUint32();
114 265 : NaClLog(2, "NaClPageAllocRandomized: 0x%"NACL_PRIxPTR"\n", addr);
115 : /*
116 : * linux permits 128 TB of user address space, and we keep the low
117 : * 16 bits free (64K alignment to match Windows), so we have
118 : * 47-16=31 bits of entropy.
119 : */
120 265 : *p = (void *) ((addr << NACL_MAP_PAGESHIFT) /* bits [47:16] are random */
121 : & ((((uintptr_t) 1) << 47) - 1)); /* now bits [46:16] */
122 : #else
123 : # error "where am i?"
124 : #endif
125 :
126 265 : NaClLog(2, "NaClPageAllocRandomized: hint 0x%"NACL_PRIxPTR"\n",
127 : (uintptr_t) *p);
128 265 : neg_errno = NaClPageAllocInternalFlags(p, size, 0);
129 265 : if (0 == neg_errno) {
130 265 : break;
131 : }
132 0 : }
133 265 : if (0 != neg_errno) {
134 0 : NaClLog(LOG_INFO,
135 : "NaClPageAllocRandomized: failed (%d), dropping hints\n",
136 : -neg_errno);
137 0 : *p = 0;
138 0 : neg_errno = NaClPageAllocInternalFlags(p, size, 0);
139 0 : }
140 265 : return neg_errno;
141 : }
142 :
143 1005 : int NaClPageAlloc(void **p, size_t size) {
144 1005 : void *addr = NULL;
145 1005 : int rv;
146 :
147 1005 : if (0 == (rv = NaClPageAllocInternal(&addr, size))) {
148 1005 : *p = addr;
149 1005 : }
150 :
151 1005 : return rv;
152 : }
153 :
154 0 : int NaClPageAllocAtAddr(void **p, size_t size) {
155 0 : return NaClPageAllocInternal(p, size);
156 : }
157 :
158 : /*
159 : * This is critical to make the text region non-writable, and the data
160 : * region read/write but no exec. Of course, some kernels do not
161 : * respect the lack of PROT_EXEC.
162 : */
163 2214 : int NaClMprotect(void *addr, size_t len, int prot) {
164 2214 : int ret = mprotect(addr, len, prot);
165 :
166 6642 : return ret == -1 ? -errno : ret;
167 : }
168 :
169 :
170 279 : int NaClMadvise(void *start, size_t length, int advice) {
171 279 : int ret = madvise(start, length, advice);
172 :
173 : /*
174 : * MADV_DONTNEED and MADV_NORMAL are needed
175 : */
176 837 : return ret == -1 ? -errno : ret;
177 : }
|