imcplugin demo: Extend to support stat() call
[nativeclient.git] / service_runtime / nacl_desc_imc_shm.c
blob47702a2bd9baf1577c1e73c5ef2524a4d3095304
1 /*
2 * Copyright 2008, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * NaCl Service Runtime. Transferrable shared memory objects.
36 #include "native_client/include/portability.h"
37 #include "native_client/include/nacl_platform.h"
39 #include <stdlib.h>
40 #include <string.h>
42 #include "native_client/service_runtime/nacl_desc_imc_shm.h"
44 #include "native_client/service_runtime/internal_errno.h"
45 #include "native_client/service_runtime/nacl_config.h"
46 #include "native_client/service_runtime/nacl_desc_base.h"
47 #include "native_client/service_runtime/nacl_desc_effector.h"
48 #include "native_client/service_runtime/nacl_desc_io.h"
49 #include "native_client/service_runtime/nacl_log.h"
50 #include "native_client/service_runtime/nacl_sync_checked.h"
52 #include "native_client/service_runtime/include/sys/errno.h"
53 #include "native_client/service_runtime/include/sys/mman.h"
54 #include "native_client/service_runtime/include/sys/stat.h"
56 #include "native_client/intermodule_comm/nacl_imc_c.h"
59 * This file contains the implementation of the NaClDescImcShm
60 * subclass of NaClDesc.
62 * NaClDescImcShm is the subclass that wraps IMC shm descriptors.
65 int NaClDescImcShmCtor(struct NaClDescImcShm *self,
66 NaClHandle h,
67 off_t size)
69 struct NaClDesc *basep = (struct NaClDesc *) self;
72 * off_t is signed, but size_t are not; historically size_t is for
73 * sizeof and similar, and off_t is also used for stat structure
74 * st_size member. This runtime test detects large object sizes
75 * that are silently converted to negative values.
77 basep->vtbl = (struct NaClDescVtbl *) NULL;
78 if (size < 0) {
79 return 0;
82 if (!NaClDescCtor(basep)) {
83 return 0;
85 self->h = h;
86 self->size = size;
87 basep->vtbl = &kNaClDescImcShmVtbl;
88 return 1;
91 void NaClDescImcShmDtor(struct NaClDesc *vself)
93 struct NaClDescImcShm *self = (struct NaClDescImcShm *) vself;
95 NaClClose(self->h);
96 self->h = NACL_INVALID_HANDLE;
97 vself->vtbl = (struct NaClDescVtbl *) NULL;
98 NaClDescDtor(vself);
101 int NaClDescImcShmMap(struct NaClDesc *vself,
102 struct NaClDescEffector *effp,
103 void *start_addr,
104 size_t len,
105 int prot,
106 int flags,
107 off_t offset)
109 struct NaClDescImcShm *self = (struct NaClDescImcShm *) vself;
111 int rv;
112 int nacl_prot;
113 int nacl_flags;
114 uintptr_t addr;
115 uintptr_t end_addr;
116 void *result;
117 off_t tmp_off;
120 * prot must be not be PROT_NONE nor contain other than PROT_{READ|WRITE}
122 if (NACL_ABI_PROT_NONE == prot) {
123 NaClLog(LOG_INFO, "NaClDescImcShmMap: PROT_NONE not supported\n");
124 return -NACL_ABI_EINVAL;
126 if (0 != (~(NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE) & prot)) {
127 NaClLog(LOG_INFO,
128 "NaClDescImcShmMap: prot has other bits than PROT_{READ|WRITE}\n");
129 return -NACL_ABI_EINVAL;
132 * Map from NACL_ABI_ prot and flags bits to IMC library flags,
133 * which will later map back into posix-style prot/flags on *x
134 * boxen, and to MapViewOfFileEx arguments on Windows.
136 nacl_prot = 0;
137 if (NACL_ABI_PROT_READ & prot) {
138 nacl_prot |= NACL_PROT_READ;
140 if (NACL_ABI_PROT_WRITE & prot) {
141 nacl_prot |= NACL_PROT_WRITE;
143 nacl_flags = NACL_MAP_SHARED | NACL_MAP_FIXED;
146 * For *x, we just map with MAP_FIXED and the kernel takes care of
147 * atomically unmapping any existing memory. For Windows, we must
148 * unmap existing memory first, which creates a race condition,
149 * where some other innocent thread puts some other memory into the
150 * hole, and that memory becomes vulnerable to attack by the
151 * untrusted NaCl application.
153 * For now, abort the process. We will need to figure out how to
154 * re-architect this code to do the address space move, since it is
155 * deep surgery and we'll need to ensure that all threads have
156 * stopped and any addresses derived from the old address space
157 * would not be on any thread's call stack, i.e., stop the thread in
158 * user space or before entering real service runtime code. This
159 * means that no application thread may be indefinitely blocked
160 * performing a service call in the service runtime, since otherwise
161 * there is no way for us to stop all threads.
163 * TODO: We will probably return an internal error code
164 * -NACL_ABI_E_MOVE_ADDRESS_SPACE to ask the caller to do the address space
165 * dance.
167 for (addr = (uintptr_t) start_addr, end_addr = addr + len, tmp_off = offset;
168 addr < end_addr;
169 addr += NACL_MAP_PAGESIZE, tmp_off += NACL_MAP_PAGESIZE) {
172 * Minimize the time between the unmap and the map for the same
173 * page: we interleave the unmap and map for the pages, rather
174 * than do all the unmap first and then do all of the map
175 * operations.
177 if (0 !=
178 (rv = (*effp->vtbl->UnmapMemory)(effp,
179 addr,
180 NACL_MAP_PAGESIZE))) {
181 NaClLog(LOG_FATAL,
182 ("NaClDescImcShmMap: error %d --"
183 " could not unmap 0x%08"PRIxPTR", length 0x%x\n"),
185 addr,
186 NACL_MAP_PAGESIZE);
189 result = NaClMap((void *) addr, NACL_MAP_PAGESIZE, nacl_prot, nacl_flags,
190 self->h, tmp_off);
191 if (NACL_MAP_FAILED == result) {
192 return -NACL_ABI_E_MOVE_ADDRESS_SPACE;
194 if (result != (void *) addr) {
195 NaClLog(LOG_FATAL,
196 ("NaClDescImcShmMap: NACL_MAP_FIXED but"
197 " got 0x%08"PRIxPTR" instead of 0x%08"PRIxPTR"\n"),
198 (uintptr_t) result, addr);
201 return (int) start_addr;
204 int NaClDescImcShmUnmapCommon(struct NaClDesc *vself,
205 struct NaClDescEffector *effp,
206 void *start_addr,
207 size_t len,
208 int safe_mode)
210 int status;
211 uintptr_t addr;
212 uintptr_t end_addr;
214 for (addr = (uintptr_t) start_addr, end_addr = addr + len;
215 addr < end_addr;
216 addr += NACL_MAP_PAGESIZE) {
218 * Do the unmap "properly" through NaClUnmap, in case that the IMC
219 * library is changed to do some bookkeeping.
221 status = NaClUnmap((void *) addr, NACL_MAP_PAGESIZE);
223 if (0 != status) {
224 NaClLog(LOG_FATAL, "NaClDescImcShmUnmapCommon: NaClUnmap failed\n");
225 return -NACL_ABI_EINVAL;
227 /* there's still a race condition */
228 if (safe_mode) {
229 if (NaClIsNegErrno((*effp->vtbl->MapAnonymousMemory)(effp,
230 addr,
231 NACL_MAP_PAGESIZE,
232 PROT_NONE))) {
233 NaClLog(LOG_ERROR, "NaClDescImcShmUnmapCommon: could not fill hole\n");
234 return -NACL_ABI_E_MOVE_ADDRESS_SPACE;
238 return 0;
241 int NaClDescImcShmUnmapUnsafe(struct NaClDesc *vself,
242 struct NaClDescEffector *effp,
243 void *start_addr,
244 size_t len)
246 return NaClDescImcShmUnmapCommon(vself, effp, start_addr, len, 0);
249 int NaClDescImcShmUnmap(struct NaClDesc *vself,
250 struct NaClDescEffector *effp,
251 void *start_addr,
252 size_t len)
254 return NaClDescImcShmUnmapCommon(vself, effp, start_addr, len, 1);
257 int NaClDescImcShmFstat(struct NaClDesc *vself,
258 struct NaClDescEffector *effp,
259 struct nacl_abi_stat *stbp)
261 struct NaClDescImcShm *self = (struct NaClDescImcShm *) vself;
263 stbp->nacl_abi_st_dev = 0;
264 stbp->nacl_abi_st_ino = 0x6c43614e;
265 stbp->nacl_abi_st_mode = NACL_ABI_S_IFREG | NACL_ABI_S_IRWXU;
266 stbp->nacl_abi_st_nlink = 1;
267 stbp->nacl_abi_st_uid = -1;
268 stbp->nacl_abi_st_gid = -1;
269 stbp->nacl_abi_st_rdev = 0;
270 stbp->nacl_abi_st_size = self->size; /* the only real reason for fstat */
271 stbp->nacl_abi_st_blksize = 0;
272 stbp->nacl_abi_st_blocks = 0;
273 stbp->nacl_abi_st_atime = 0;
274 stbp->nacl_abi_st_mtime = 0;
275 stbp->nacl_abi_st_ctime = 0;
277 return 0;
280 int NaClDescImcShmClose(struct NaClDesc *vself,
281 struct NaClDescEffector *effp)
283 NaClDescUnref(vself);
284 return 0;
287 int NaClDescImcShmExternalizeSize(struct NaClDesc *vself,
288 size_t *nbytes,
289 size_t *nhandles)
291 struct NaClDescImcShm *self = (struct NaClDescImcShm *) vself;
293 *nbytes = sizeof self->size;
294 *nhandles = 1;
296 return 0;
299 int NaClDescImcShmExternalize(struct NaClDesc *vself,
300 struct NaClDescXferState *xfer)
302 struct NaClDescImcShm *self = (struct NaClDescImcShm *) vself;
304 *xfer->next_handle++ = self->h;
305 memcpy(xfer->next_byte, &self->size, sizeof self->size);
306 xfer->next_byte += sizeof self->size;
307 return 0;
310 struct NaClDescVtbl const kNaClDescImcShmVtbl = {
311 NaClDescImcShmDtor,
312 NaClDescImcShmMap,
313 NaClDescImcShmUnmapUnsafe,
314 NaClDescImcShmUnmap,
315 NaClDescReadNotImplemented,
316 NaClDescWriteNotImplemented,
317 NaClDescSeekNotImplemented,
318 NaClDescIoctlNotImplemented,
319 NaClDescImcShmFstat,
320 NaClDescImcShmClose,
321 NaClDescGetdentsNotImplemented,
322 NACL_DESC_SHM,
323 NaClDescImcShmExternalizeSize,
324 NaClDescImcShmExternalize,
325 NaClDescLockNotImplemented,
326 NaClDescTryLockNotImplemented,
327 NaClDescUnlockNotImplemented,
328 NaClDescWaitNotImplemented,
329 NaClDescTimedWaitAbsNotImplemented,
330 NaClDescSignalNotImplemented,
331 NaClDescBroadcastNotImplemented,
332 NaClDescSendMsgNotImplemented,
333 NaClDescRecvMsgNotImplemented,
334 NaClDescConnectAddrNotImplemented,
335 NaClDescAcceptConnNotImplemented,
336 NaClDescPostNotImplemented,
337 NaClDescSemWaitNotImplemented,
338 NaClDescGetValueNotImplemented,
341 int NaClDescImcShmInternalize(struct NaClDesc **baseptr,
342 struct NaClDescXferState *xfer)
344 int rv;
345 struct NaClDescImcShm *ndisp;
346 NaClHandle h;
347 off_t hsize;
349 rv = -NACL_ABI_EIO;
350 ndisp = NULL;
352 if (xfer->next_handle == xfer->handle_buffer_end) {
353 rv = -NACL_ABI_EIO;
354 goto cleanup;
356 if (xfer->next_byte + sizeof ndisp->size > xfer->byte_buffer_end) {
357 rv = -NACL_ABI_EIO;
358 goto cleanup;
361 ndisp = malloc(sizeof *ndisp);
362 if (NULL == ndisp) {
363 rv = -NACL_ABI_ENOMEM;
364 goto cleanup;
367 h = *xfer->next_handle;
368 *xfer->next_handle++ = NACL_INVALID_HANDLE;
369 memcpy(&hsize, xfer->next_byte, sizeof hsize);
370 xfer->next_byte += sizeof hsize;
372 NaClDescImcShmCtor(ndisp, h, hsize);
374 *baseptr = (struct NaClDesc *) ndisp;
375 rv = 0;
377 cleanup:
378 if (rv < 0) {
379 free(ndisp);
381 return rv;