1 : /*
2 : * Copyright (c) 2011 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. Transferrable shared memory objects.
9 : */
10 :
11 : #include "native_client/src/include/portability.h"
12 : #include "native_client/src/include/nacl_platform.h"
13 :
14 : #include <stdlib.h>
15 : #include <string.h>
16 :
17 : #include "native_client/src/shared/imc/nacl_imc_c.h"
18 : #include "native_client/src/trusted/desc/nacl_desc_base.h"
19 : #include "native_client/src/trusted/desc/nacl_desc_effector.h"
20 : #include "native_client/src/trusted/desc/nacl_desc_io.h"
21 : #include "native_client/src/trusted/desc/nacl_desc_imc_shm.h"
22 :
23 : #include "native_client/src/shared/platform/nacl_find_addrsp.h"
24 : #include "native_client/src/shared/platform/nacl_host_desc.h"
25 : #include "native_client/src/shared/platform/nacl_log.h"
26 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
27 :
28 : #include "native_client/src/trusted/service_runtime/include/sys/errno.h"
29 : #include "native_client/src/trusted/service_runtime/include/sys/mman.h"
30 : #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
31 : #include "native_client/src/trusted/service_runtime/internal_errno.h"
32 : #include "native_client/src/trusted/service_runtime/nacl_config.h"
33 :
34 : #ifndef SIZE_T_MAX
35 : # define SIZE_T_MAX (~(size_t) 0)
36 : #endif
37 :
38 : /*
39 : * This file contains the implementation of the NaClDescImcShm
40 : * subclass of NaClDesc.
41 : *
42 : * NaClDescImcShm is the subclass that wraps IMC shm descriptors.
43 : */
44 :
45 : static struct NaClDescVtbl const kNaClDescImcShmVtbl; /* fwd */
46 :
47 : int NaClDescImcShmCtor(struct NaClDescImcShm *self,
48 : NaClHandle h,
49 9 : nacl_off64_t size) {
50 9 : struct NaClDesc *basep = (struct NaClDesc *) self;
51 :
52 : /*
53 : * off_t is signed, but size_t are not; historically size_t is for
54 : * sizeof and similar, and off_t is also used for stat structure
55 : * st_size member. This runtime test detects large object sizes
56 : * that are silently converted to negative values.
57 : */
58 9 : basep->base.vtbl = (struct NaClRefCountVtbl const *) NULL;
59 9 : if (size < 0 || SIZE_T_MAX < (uint64_t) size) {
60 0 : return 0;
61 : }
62 :
63 9 : if (!NaClDescCtor(basep)) {
64 0 : return 0;
65 : }
66 9 : self->h = h;
67 9 : self->size = size;
68 9 : basep->base.vtbl = (struct NaClRefCountVtbl const *) &kNaClDescImcShmVtbl;
69 9 : return 1;
70 : }
71 :
72 : int NaClDescImcShmAllocCtor(struct NaClDescImcShm *self,
73 : nacl_off64_t size,
74 9 : int executable) {
75 : NaClHandle h;
76 : int rv;
77 :
78 9 : if (size < 0 || SIZE_T_MAX < (uint64_t) size) {
79 0 : NaClLog(4,
80 : "NaClDescImcShmAllocCtor: requested size 0x%08"NACL_PRIx64
81 : " (0x%08"NACL_PRId64") too large\n",
82 : size, size);
83 0 : return 0;
84 : }
85 9 : h = NaClCreateMemoryObject((size_t) size, executable);
86 9 : if (NACL_INVALID_HANDLE == h) {
87 0 : return 0;
88 : }
89 9 : if (0 == (rv = NaClDescImcShmCtor(self, h, size))) {
90 0 : (void) NaClClose(h);
91 : }
92 9 : return rv;
93 : }
94 :
95 1 : static void NaClDescImcShmDtor(struct NaClRefCount *vself) {
96 1 : struct NaClDescImcShm *self = (struct NaClDescImcShm *) vself;
97 :
98 1 : (void) NaClClose(self->h);
99 1 : self->h = NACL_INVALID_HANDLE;
100 1 : vself->vtbl = (struct NaClRefCountVtbl const *) &kNaClDescVtbl;
101 1 : (*vself->vtbl->Dtor)(vself);
102 1 : }
103 :
104 : static uintptr_t NaClDescImcShmMap(struct NaClDesc *vself,
105 : struct NaClDescEffector *effp,
106 : void *start_addr,
107 : size_t len,
108 : int prot,
109 : int flags,
110 11 : nacl_off64_t offset) {
111 11 : struct NaClDescImcShm *self = (struct NaClDescImcShm *) vself;
112 :
113 : int rv;
114 : int nacl_imc_prot;
115 : int nacl_imc_flags;
116 : uintptr_t addr;
117 : uintptr_t end_addr;
118 : void *result;
119 : nacl_off64_t tmp_off64;
120 : off_t tmp_off;
121 :
122 11 : NaClLog(4,
123 : "NaClDescImcShmMmap(,,0x%08"NACL_PRIxPTR",0x%"NACL_PRIxS","
124 : "0x%x,0x%x,0x%08"NACL_PRIxNACL_OFF64")\n",
125 : (uintptr_t) start_addr, len, prot, flags, offset);
126 : /*
127 : * shm must have NACL_ABI_MAP_SHARED in flags, and all calls through
128 : * this API must supply a start_addr, so NACL_ABI_MAP_FIXED is
129 : * assumed.
130 : */
131 11 : if (NACL_ABI_MAP_SHARED != (flags & NACL_ABI_MAP_SHARING_MASK)) {
132 0 : NaClLog(LOG_INFO,
133 : ("NaClDescImcShmMap: Mapping not NACL_ABI_MAP_SHARED,"
134 : " flags 0x%x\n"),
135 : flags);
136 0 : return -NACL_ABI_EINVAL;
137 : }
138 11 : if (0 != (NACL_ABI_MAP_FIXED & flags) && NULL == start_addr) {
139 0 : NaClLog(LOG_INFO,
140 : ("NaClDescImcShmMap: Mapping NACL_ABI_MAP_FIXED"
141 : " but start_addr is NULL\n"));
142 : }
143 : /* post-condition: if NULL == start_addr, then NACL_ABI_MAP_FIXED not set */
144 :
145 : /*
146 : * prot must not contain bits other than PROT_{READ|WRITE|EXEC}.
147 : */
148 11 : if (0 != (~(NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE | NACL_ABI_PROT_EXEC)
149 : & prot)) {
150 0 : NaClLog(LOG_INFO,
151 : "NaClDescImcShmMap: prot has other bits than"
152 : " PROT_{READ|WRITE|EXEC}\n");
153 0 : return -NACL_ABI_EINVAL;
154 : }
155 : /*
156 : * Map from NACL_ABI_ prot and flags bits to IMC library flags,
157 : * which will later map back into posix-style prot/flags on *x
158 : * boxen, and to MapViewOfFileEx arguments on Windows.
159 : */
160 11 : nacl_imc_prot = 0;
161 11 : if (NACL_ABI_PROT_READ & prot) {
162 8 : nacl_imc_prot |= NACL_PROT_READ;
163 : }
164 11 : if (NACL_ABI_PROT_WRITE & prot) {
165 8 : nacl_imc_prot |= NACL_PROT_WRITE;
166 : }
167 11 : if (NACL_ABI_PROT_EXEC & prot) {
168 0 : nacl_imc_prot |= NACL_PROT_EXEC;
169 : }
170 11 : nacl_imc_flags = NACL_MAP_SHARED;
171 11 : if (0 == (NACL_ABI_MAP_FIXED & flags)) {
172 : /* start_addr is a hint, and we just ignore the hint... */
173 8 : if (!NaClFindAddressSpace(&addr, len)) {
174 0 : NaClLog(1, "NaClDescImcShmMap: no address space?!?\n");
175 0 : return -NACL_ABI_ENOMEM;
176 : }
177 8 : start_addr = (void *) addr;
178 : }
179 11 : nacl_imc_flags |= NACL_MAP_FIXED;
180 :
181 11 : tmp_off64 = offset + len;
182 : /* just NaClRoundAllocPage, but in 64 bits */
183 11 : tmp_off64 = ((tmp_off64 + NACL_MAP_PAGESIZE - 1)
184 : & ~(uint64_t) (NACL_MAP_PAGESIZE - 1));
185 11 : if (tmp_off64 > INT32_MAX) {
186 0 : NaClLog(LOG_INFO,
187 : "NaClDescImcShmMap: total offset exceeds 32-bits\n");
188 0 : return -NACL_ABI_EOVERFLOW;
189 : }
190 :
191 : /*
192 : * For *x, we just map with MAP_FIXED and the kernel takes care of
193 : * atomically unmapping any existing memory. For Windows, we must
194 : * unmap existing memory first, which creates a race condition,
195 : * where some other innocent thread puts some other memory into the
196 : * hole, and that memory becomes vulnerable to attack by the
197 : * untrusted NaCl application.
198 : *
199 : * For now, abort the process. We will need to figure out how to
200 : * re-architect this code to do the address space move, since it is
201 : * deep surgery and we'll need to ensure that all threads have
202 : * stopped and any addresses derived from the old address space
203 : * would not be on any thread's call stack, i.e., stop the thread in
204 : * user space or before entering real service runtime code. This
205 : * means that no application thread may be indefinitely blocked
206 : * performing a service call in the service runtime, since otherwise
207 : * there is no way for us to stop all threads.
208 : *
209 : * TODO(bsy): We will probably return an internal error code
210 : * -NACL_ABI_E_MOVE_ADDRESS_SPACE to ask the caller to do the address space
211 : * dance.
212 : */
213 : for (addr = (uintptr_t) start_addr,
214 : end_addr = addr + len,
215 11 : tmp_off = (off_t) offset;
216 12340 : addr < end_addr;
217 12318 : addr += NACL_MAP_PAGESIZE, tmp_off += NACL_MAP_PAGESIZE) {
218 :
219 : /*
220 : * Minimize the time between the unmap and the map for the same
221 : * page: we interleave the unmap and map for the pages, rather
222 : * than do all the unmap first and then do all of the map
223 : * operations.
224 : */
225 12318 : if (0 !=
226 : (rv = (*effp->vtbl->UnmapMemory)(effp,
227 : addr,
228 : NACL_MAP_PAGESIZE))) {
229 0 : NaClLog(LOG_FATAL,
230 : ("NaClDescImcShmMap: error %d --"
231 : " could not unmap 0x%08"NACL_PRIxPTR", length 0x%x\n"),
232 : rv,
233 : addr,
234 : NACL_MAP_PAGESIZE);
235 : }
236 :
237 12318 : result = NaClMap((void *) addr,
238 : NACL_MAP_PAGESIZE,
239 : nacl_imc_prot,
240 : nacl_imc_flags,
241 : self->h,
242 : tmp_off);
243 12318 : if (NACL_MAP_FAILED == result) {
244 0 : return -NACL_ABI_E_MOVE_ADDRESS_SPACE;
245 : }
246 12318 : if (0 != (NACL_ABI_MAP_FIXED & flags) && result != (void *) addr) {
247 0 : NaClLog(LOG_FATAL,
248 : ("NaClDescImcShmMap: NACL_MAP_FIXED but"
249 : " got 0x%08"NACL_PRIxPTR" instead of 0x%08"NACL_PRIxPTR"\n"),
250 : (uintptr_t) result, addr);
251 : }
252 : }
253 11 : return (uintptr_t) start_addr;
254 : }
255 :
256 : static int NaClDescImcShmUnmapCommon(struct NaClDesc *vself,
257 : struct NaClDescEffector *effp,
258 : void *start_addr,
259 : size_t len,
260 6 : int safe_mode) {
261 : int retval;
262 : uintptr_t addr;
263 : uintptr_t end_addr;
264 :
265 : UNREFERENCED_PARAMETER(vself);
266 :
267 6 : retval = -NACL_ABI_EINVAL;
268 :
269 6 : for (addr = (uintptr_t) start_addr, end_addr = addr + len;
270 20 : addr < end_addr;
271 8 : addr += NACL_MAP_PAGESIZE) {
272 : int status;
273 :
274 : #if NACL_WINDOWS
275 : /*
276 : * On windows, we must unmap "properly", since overmapping will
277 : * not tear down existing page mappings.
278 : */
279 : #elif NACL_LINUX || NACL_OSX
280 8 : if (!safe_mode) {
281 : /*
282 : * unsafe unmap always unmaps, w/o overmapping with anonymous
283 : * memory. this is not necessary (nor desired) in safe_mode,
284 : * since overmapping with anonymous memory will atomically tear
285 : * down the mappings for these pages without leaving a timing
286 : * window open where the untrusted address space has unoccupied
287 : * page table entries.
288 : */
289 : #else
290 : # error "what platform?"
291 : #endif
292 : /*
293 : * Do the unmap "properly" through NaClUnmap.
294 : */
295 8 : status = NaClUnmap((void *) addr, NACL_MAP_PAGESIZE);
296 8 : if (0 != status) {
297 0 : NaClLog(LOG_FATAL, "NaClDescImcShmUnmapCommon: NaClUnmap failed\n");
298 0 : goto done;
299 : }
300 : #if NACL_LINUX || NACL_OSX
301 : }
302 : #endif
303 : /* there's still a race condition */
304 8 : if (safe_mode) {
305 : uintptr_t result = (*effp->vtbl->MapAnonymousMemory)(effp,
306 : addr,
307 : NACL_MAP_PAGESIZE,
308 0 : PROT_NONE);
309 0 : if (NaClPtrIsNegErrno(&result)) {
310 0 : NaClLog(LOG_ERROR, "NaClDescImcShmUnmapCommon: could not fill hole\n");
311 0 : retval = -NACL_ABI_E_MOVE_ADDRESS_SPACE;
312 0 : goto done;
313 : }
314 : }
315 : }
316 6 : retval = 0;
317 6 : done:
318 6 : return retval;
319 : }
320 :
321 : static int NaClDescImcShmUnmapUnsafe(struct NaClDesc *vself,
322 : struct NaClDescEffector *effp,
323 : void *start_addr,
324 6 : size_t len) {
325 6 : return NaClDescImcShmUnmapCommon(vself, effp, start_addr, len, 0);
326 : }
327 :
328 : static int NaClDescImcShmUnmap(struct NaClDesc *vself,
329 : struct NaClDescEffector *effp,
330 : void *start_addr,
331 0 : size_t len) {
332 0 : return NaClDescImcShmUnmapCommon(vself, effp, start_addr, len, 1);
333 : }
334 :
335 : static int NaClDescImcShmFstat(struct NaClDesc *vself,
336 6 : struct nacl_abi_stat *stbp) {
337 6 : struct NaClDescImcShm *self = (struct NaClDescImcShm *) vself;
338 :
339 6 : if (self->size > INT32_MAX) {
340 0 : return -NACL_ABI_EOVERFLOW;
341 : }
342 :
343 6 : stbp->nacl_abi_st_dev = 0;
344 6 : stbp->nacl_abi_st_ino = 0x6c43614e;
345 6 : stbp->nacl_abi_st_mode = (NACL_ABI_S_IFSHM |
346 : NACL_ABI_S_IRUSR |
347 : NACL_ABI_S_IWUSR);
348 6 : stbp->nacl_abi_st_nlink = 1;
349 6 : stbp->nacl_abi_st_uid = -1;
350 6 : stbp->nacl_abi_st_gid = -1;
351 6 : stbp->nacl_abi_st_rdev = 0;
352 6 : stbp->nacl_abi_st_size = (nacl_abi_off_t) self->size;
353 6 : stbp->nacl_abi_st_blksize = 0;
354 6 : stbp->nacl_abi_st_blocks = 0;
355 6 : stbp->nacl_abi_st_atime = 0;
356 6 : stbp->nacl_abi_st_mtime = 0;
357 6 : stbp->nacl_abi_st_ctime = 0;
358 :
359 6 : return 0;
360 : }
361 :
362 : static int NaClDescImcShmExternalizeSize(struct NaClDesc *vself,
363 : size_t *nbytes,
364 0 : size_t *nhandles) {
365 0 : struct NaClDescImcShm *self = (struct NaClDescImcShm *) vself;
366 :
367 0 : *nbytes = sizeof self->size;
368 0 : *nhandles = 1;
369 :
370 0 : return 0;
371 : }
372 :
373 : static int NaClDescImcShmExternalize(struct NaClDesc *vself,
374 0 : struct NaClDescXferState *xfer) {
375 0 : struct NaClDescImcShm *self = (struct NaClDescImcShm *) vself;
376 :
377 0 : *xfer->next_handle++ = self->h;
378 0 : memcpy(xfer->next_byte, &self->size, sizeof self->size);
379 0 : xfer->next_byte += sizeof self->size;
380 0 : return 0;
381 : }
382 :
383 : static struct NaClDescVtbl const kNaClDescImcShmVtbl = {
384 : {
385 : NaClDescImcShmDtor,
386 : },
387 : NaClDescImcShmMap,
388 : NaClDescImcShmUnmapUnsafe,
389 : NaClDescImcShmUnmap,
390 : NaClDescReadNotImplemented,
391 : NaClDescWriteNotImplemented,
392 : NaClDescSeekNotImplemented,
393 : NaClDescIoctlNotImplemented,
394 : NaClDescImcShmFstat,
395 : NaClDescGetdentsNotImplemented,
396 : NACL_DESC_SHM,
397 : NaClDescImcShmExternalizeSize,
398 : NaClDescImcShmExternalize,
399 : NaClDescLockNotImplemented,
400 : NaClDescTryLockNotImplemented,
401 : NaClDescUnlockNotImplemented,
402 : NaClDescWaitNotImplemented,
403 : NaClDescTimedWaitAbsNotImplemented,
404 : NaClDescSignalNotImplemented,
405 : NaClDescBroadcastNotImplemented,
406 : NaClDescSendMsgNotImplemented,
407 : NaClDescRecvMsgNotImplemented,
408 : NaClDescConnectAddrNotImplemented,
409 : NaClDescAcceptConnNotImplemented,
410 : NaClDescPostNotImplemented,
411 : NaClDescSemWaitNotImplemented,
412 : NaClDescGetValueNotImplemented,
413 : };
414 :
415 : int NaClDescImcShmInternalize(struct NaClDesc **out_desc,
416 : struct NaClDescXferState *xfer,
417 0 : struct NaClDescQuotaInterface *quota_interface) {
418 : int rv;
419 : struct NaClDescImcShm *ndisp;
420 : NaClHandle h;
421 : nacl_off64_t hsize;
422 :
423 : UNREFERENCED_PARAMETER(quota_interface);
424 0 : rv = -NACL_ABI_EIO;
425 0 : ndisp = NULL;
426 :
427 0 : if (xfer->next_handle == xfer->handle_buffer_end) {
428 0 : rv = -NACL_ABI_EIO;
429 0 : goto cleanup;
430 : }
431 0 : if (xfer->next_byte + sizeof ndisp->size > xfer->byte_buffer_end) {
432 0 : rv = -NACL_ABI_EIO;
433 0 : goto cleanup;
434 : }
435 :
436 0 : ndisp = malloc(sizeof *ndisp);
437 0 : if (NULL == ndisp) {
438 0 : rv = -NACL_ABI_ENOMEM;
439 0 : goto cleanup;
440 : }
441 :
442 0 : h = *xfer->next_handle;
443 0 : *xfer->next_handle++ = NACL_INVALID_HANDLE;
444 0 : memcpy(&hsize, xfer->next_byte, sizeof hsize);
445 0 : xfer->next_byte += sizeof hsize;
446 :
447 0 : if (0 == NaClDescImcShmCtor(ndisp, h, hsize)) {
448 0 : rv = -NACL_ABI_EIO;
449 0 : goto cleanup;
450 : }
451 :
452 0 : *out_desc = (struct NaClDesc *) ndisp;
453 0 : rv = 0;
454 :
455 0 : cleanup:
456 0 : if (rv < 0) {
457 0 : free(ndisp);
458 : }
459 0 : return rv;
460 : }
|