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