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 : /*
9 : * NaCl inter-module communication primitives.
10 : *
11 : * This file implements common parts of IMC for "UNIX like systems" (i.e. not
12 : * used on Windows).
13 : */
14 :
15 : #include <assert.h>
16 : #include <ctype.h>
17 : #include <errno.h>
18 : #include <fcntl.h>
19 : #include <limits.h>
20 : #include <stdio.h>
21 : #include <string.h>
22 : #include <unistd.h>
23 : #include <sys/mman.h>
24 : #include <sys/types.h>
25 : #if NACL_ANDROID
26 : #include <linux/ashmem.h>
27 : #endif
28 :
29 : #include <algorithm>
30 :
31 : #include "native_client/src/include/atomic_ops.h"
32 :
33 : #include "native_client/src/shared/imc/nacl_imc_c.h"
34 : #include "native_client/src/shared/platform/nacl_check.h"
35 :
36 :
37 : #if NACL_LINUX && defined(NACL_ENABLE_TMPFS_REDIRECT_VAR)
38 : static const char kNaClTempPrefixVar[] = "NACL_TMPFS_PREFIX";
39 : #endif
40 :
41 : /*
42 : * The pathname or SHM-namespace prefixes for memory objects created
43 : * by CreateMemoryObject().
44 : */
45 : static const char kShmTempPrefix[] = "/tmp/google-nacl-shm-";
46 : static const char kShmOpenPrefix[] = "/google-nacl-shm-";
47 :
48 : static NaClCreateMemoryObjectFunc g_create_memory_object_func = NULL;
49 :
50 :
51 : /* Duplicate a file descriptor. */
52 0 : NaClHandle NaClDuplicateNaClHandle(NaClHandle handle) {
53 0 : return dup(handle);
54 : }
55 :
56 0 : void NaClSetCreateMemoryObjectFunc(NaClCreateMemoryObjectFunc func) {
57 0 : g_create_memory_object_func = func;
58 0 : }
59 :
60 : #if NACL_ANDROID
61 : #define ASHMEM_DEVICE "/dev/ashmem"
62 :
63 : static int AshmemCreateRegion(size_t size) {
64 : int fd;
65 :
66 : fd = open(ASHMEM_DEVICE, O_RDWR);
67 : if (fd < 0)
68 : return -1;
69 :
70 : if (ioctl(fd, ASHMEM_SET_SIZE, size) < 0) {
71 : close(fd);
72 : return -1;
73 : }
74 :
75 : return fd;
76 : }
77 : #endif
78 :
79 : int NaClWouldBlock(void) {
80 0 : return errno == EAGAIN;
81 : }
82 :
83 0 : int NaClGetLastErrorString(char* buffer, size_t length) {
84 : #if NACL_LINUX && !NACL_ANDROID
85 : char* message;
86 : /*
87 : * Note some Linux distributions provide only GNU version of strerror_r().
88 : */
89 : if (buffer == NULL || length == 0) {
90 : errno = ERANGE;
91 : return -1;
92 : }
93 : message = strerror_r(errno, buffer, length);
94 : if (message != buffer) {
95 : size_t message_bytes = strlen(message) + 1;
96 : length = std::min(message_bytes, length);
97 : memmove(buffer, message, length);
98 : buffer[length - 1] = '\0';
99 : }
100 : return 0;
101 : #else
102 0 : return strerror_r(errno, buffer, length);
103 : #endif
104 : }
105 :
106 : #if !NACL_ANDROID
107 : static Atomic32 memory_object_count = 0;
108 :
109 286 : static int TryShmOrTempOpen(size_t length, const char* prefix, bool use_temp) {
110 286 : char name[PATH_MAX];
111 286 : if (0 == length) {
112 0 : return -1;
113 : }
114 :
115 286 : for (;;) {
116 286 : int m;
117 572 : snprintf(name, sizeof name, "%s-%u.%u", prefix,
118 286 : getpid(),
119 286 : (int) AtomicIncrement(&memory_object_count, 1));
120 286 : if (use_temp) {
121 260 : m = open(name, O_RDWR | O_CREAT | O_EXCL, 0);
122 260 : } else {
123 : /*
124 : * Using 0 for the mode causes shm_unlink to fail with EACCES on Mac
125 : * OS X 10.8. As of 10.8, the kernel requires the user to have write
126 : * permission to successfully shm_unlink.
127 : */
128 26 : m = shm_open(name, O_RDWR | O_CREAT | O_EXCL, S_IWUSR);
129 : }
130 286 : if (0 <= m) {
131 286 : if (use_temp) {
132 260 : int rc = unlink(name);
133 1040 : DCHECK(rc == 0);
134 260 : } else {
135 26 : int rc = shm_unlink(name);
136 104 : DCHECK(rc == 0);
137 : }
138 286 : if (ftruncate(m, length) == -1) {
139 0 : close(m);
140 0 : m = -1;
141 0 : }
142 286 : return m;
143 : }
144 0 : if (errno != EEXIST) {
145 0 : return -1;
146 : }
147 : /* Retry only if we got EEXIST. */
148 0 : }
149 286 : }
150 : #endif
151 :
152 286 : NaClHandle NaClCreateMemoryObject(size_t length, int executable) {
153 286 : int fd;
154 :
155 286 : if (0 == length) {
156 0 : return -1;
157 : }
158 :
159 286 : if (g_create_memory_object_func != NULL) {
160 0 : fd = g_create_memory_object_func(length, executable);
161 0 : if (fd >= 0)
162 0 : return fd;
163 0 : }
164 :
165 : #if NACL_ANDROID
166 : return AshmemCreateRegion(length);
167 : #else
168 : /*
169 : * /dev/shm is not always available on Linux.
170 : * Sometimes it's mounted as noexec.
171 : * To handle this case, sel_ldr can take a path
172 : * to tmpfs from the environment.
173 : */
174 : #if NACL_LINUX && defined(NACL_ENABLE_TMPFS_REDIRECT_VAR)
175 : if (NACL_ENABLE_TMPFS_REDIRECT_VAR) {
176 : const char* prefix = getenv(kNaClTempPrefixVar);
177 : if (prefix != NULL) {
178 : fd = TryShmOrTempOpen(length, prefix, true);
179 : if (fd >= 0) {
180 : return fd;
181 : }
182 : }
183 : }
184 : #endif
185 :
186 286 : if (NACL_OSX && executable) {
187 : /*
188 : * On Mac OS X, shm_open() gives us file descriptors that the OS
189 : * won't mmap() with PROT_EXEC, which is no good for the dynamic
190 : * code region, so we must use /tmp instead.
191 : */
192 260 : return TryShmOrTempOpen(length, kShmTempPrefix, true);
193 : }
194 :
195 : /* Try shm_open(). */
196 26 : return TryShmOrTempOpen(length, kShmOpenPrefix, false);
197 : #endif /* !NACL_ANDROID */
198 286 : }
199 :
200 377 : void* NaClMap(struct NaClDescEffector* effp,
201 377 : void* start, size_t length, int prot, int flags,
202 377 : NaClHandle memory, off_t offset) {
203 : static const int kPosixProt[] = {
204 : PROT_NONE,
205 : PROT_READ,
206 : PROT_WRITE,
207 : PROT_READ | PROT_WRITE,
208 : PROT_EXEC,
209 : PROT_READ | PROT_EXEC,
210 : PROT_WRITE | PROT_EXEC,
211 : PROT_READ | PROT_WRITE | PROT_EXEC
212 : };
213 377 : int adjusted = 0;
214 754 : UNREFERENCED_PARAMETER(effp);
215 :
216 377 : if (flags & NACL_MAP_SHARED) {
217 377 : adjusted |= MAP_SHARED;
218 377 : }
219 377 : if (flags & NACL_MAP_PRIVATE) {
220 0 : adjusted |= MAP_PRIVATE;
221 0 : }
222 377 : if (flags & NACL_MAP_FIXED) {
223 377 : adjusted |= MAP_FIXED;
224 377 : }
225 377 : return mmap(start, length, kPosixProt[prot & 7], adjusted, memory, offset);
226 : }
227 :
228 0 : int NaClUnmap(void* start, size_t length) {
229 0 : return munmap(start, length);
230 : }
|