OLD | NEW |
| (Empty) |
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. 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 <errno.h> | |
15 #include <stdlib.h> | |
16 #include <string.h> | |
17 #include <sys/types.h> | |
18 #include <sys/shm.h> | |
19 | |
20 #include "native_client/src/trusted/desc/nacl_desc_base.h" | |
21 #include "native_client/src/trusted/desc/nacl_desc_effector.h" | |
22 #include "native_client/src/trusted/desc/linux/nacl_desc_sysv_shm.h" | |
23 | |
24 #include "native_client/src/shared/platform/nacl_find_addrsp.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/internal_errno.h" | |
29 #include "native_client/src/trusted/service_runtime/nacl_config.h" | |
30 #include "native_client/src/trusted/service_runtime/include/bits/mman.h" | |
31 #include "native_client/src/trusted/service_runtime/include/sys/errno.h" | |
32 #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h" | |
33 #include "native_client/src/trusted/service_runtime/include/sys/stat.h" | |
34 #include "native_client/src/trusted/service_runtime/sel_util.h" | |
35 | |
36 | |
37 #if !defined(SIZE_T_MAX) | |
38 # define SIZE_T_MAX (~((size_t) 0)) | |
39 #endif | |
40 | |
41 /* | |
42 * This file contains the implementation of the NaClDescSysvShm | |
43 * subclass of NaClDesc. | |
44 * | |
45 * NaClDescSysvShm is the subclass that wraps SysV shared memory descriptors. | |
46 * | |
47 * Support for SysV shared memory descriptors is much more restricted than | |
48 * for other types of descriptors. In particular: | |
49 * 1) NaCl provides no method for untrusted creation of SysV shared memory. | |
50 * 2) Importing does not confer ownership of the descriptor, and hence | |
51 * we do not do the shmctl(IPC_RMID) to close it. The lifetime of the | |
52 * descriptor is controlled by the X server that created it, and returned | |
53 * it for use by Chrome. | |
54 */ | |
55 | |
56 static int NaClDescSysvShmSubclassCtorIntern(struct NaClDescSysvShm *self, | |
57 int id, | |
58 nacl_off64_t size, | |
59 int rmid_in_dtor) { | |
60 struct NaClDesc *basep = (struct NaClDesc *) self; | |
61 | |
62 /* | |
63 * off_t is signed, but size_t is not; historically size_t is for | |
64 * sizeof and similar, and off_t is also used for stat structure | |
65 * st_size member. This runtime test detects large object sizes | |
66 * that are silently converted to negative values. Additionally, | |
67 * the size must be a multiple of 4K. | |
68 */ | |
69 basep->base.vtbl = (struct NaClRefCountVtbl const *) NULL; | |
70 if ((size_t) size != NaClRoundPage((size_t) size) | |
71 || size < 0 | |
72 || SIZE_T_MAX < (uint64_t) size) { | |
73 return 0; | |
74 } | |
75 | |
76 self->id = id; | |
77 self->size = size; | |
78 self->rmid_in_dtor = rmid_in_dtor; | |
79 basep->base.vtbl = (struct NaClRefCountVtbl const *) &kNaClDescSysvShmVtbl; | |
80 return 1; | |
81 } | |
82 | |
83 static int NaClDescSysvShmCtorIntern(struct NaClDescSysvShm *self, | |
84 int id, | |
85 nacl_off64_t size, | |
86 int rmid_in_dtor) { | |
87 struct NaClDesc *basep = (struct NaClDesc *) self; | |
88 int rv; | |
89 | |
90 NACL_VTBL(NaClRefCount, self) = (struct NaClRefCountVtbl const *) NULL; | |
91 | |
92 if (!NaClDescCtor(basep)) { | |
93 return 0; | |
94 } | |
95 rv = NaClDescSysvShmSubclassCtorIntern(self, id, size, rmid_in_dtor); | |
96 if (!rv) { | |
97 (*NACL_VTBL(NaClRefCount, self)->Dtor)((struct NaClRefCount *) self); | |
98 } | |
99 (*NACL_VTBL(NaClDesc, basep)->SetFlags)(basep, NACL_ABI_O_RDWR); | |
100 return rv; | |
101 } | |
102 | |
103 int NaClDescSysvShmImportSubclassCtor(struct NaClDescSysvShm *self, | |
104 int id, | |
105 nacl_off64_t size) { | |
106 return NaClDescSysvShmSubclassCtorIntern(self, id, size, 0); | |
107 } | |
108 | |
109 int NaClDescSysvShmImportCtor(struct NaClDescSysvShm *self, | |
110 int id, | |
111 nacl_off64_t size) { | |
112 return NaClDescSysvShmCtorIntern(self, id, size, 0); | |
113 } | |
114 | |
115 /* | |
116 * Creates a NaClDesc containing a new shared memory region. | |
117 */ | |
118 int NaClDescSysvShmCtor(struct NaClDescSysvShm *self, | |
119 nacl_off64_t size) { | |
120 int id; | |
121 int retval; | |
122 | |
123 if (size < 0 || SIZE_T_MAX < (uint64_t) size) { | |
124 return 0; | |
125 } | |
126 /* Create the region. */ | |
127 id = shmget(IPC_PRIVATE, | |
128 (int) size, | |
129 IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR); | |
130 if (-1 == id) { | |
131 return 0; | |
132 } | |
133 /* Construct the descriptor. */ | |
134 retval = NaClDescSysvShmCtorIntern(self, id, size, 1); | |
135 /* If the constructor failed, mark the region for freeing. */ | |
136 if (0 == retval) { | |
137 (void) shmctl(id, IPC_RMID, NULL); | |
138 return 0; | |
139 } | |
140 /* Return success. */ | |
141 return 1; | |
142 } | |
143 | |
144 static void NaClDescSysvShmDtor(struct NaClRefCount *vself) { | |
145 struct NaClDescSysvShm *self = (struct NaClDescSysvShm *) vself; | |
146 | |
147 /* | |
148 * Importing does NOT confer ownership of the id. Hence the Dtor does not | |
149 * do the shmctl(IPC_RMID). | |
150 */ | |
151 if (self->rmid_in_dtor) { | |
152 (void) shmctl(self->id, IPC_RMID, NULL); | |
153 } | |
154 /* NACL_INVALID_HANDLE is also an invalid id for shmat. */ | |
155 self->id = NACL_INVALID_HANDLE; | |
156 vself->vtbl = (struct NaClRefCountVtbl const *) &kNaClDescVtbl; | |
157 (*vself->vtbl->Dtor)(vself); | |
158 } | |
159 | |
160 /* | |
161 * For now, we just use shmat. This means that the NaCl module's mmap | |
162 * syscall must ask for the entire size, with a zero offset. In the | |
163 * future, we may choose to shmat to elsewhere in the trusted address | |
164 * space, and then mremap from there into the untrusted address space, | |
165 * using MREMAP_FIXED to ask for the new location. This approach has | |
166 * its own hazards, however since on x86-32 we are short on address | |
167 * space already, and requiring shmat, mremap, munmap means that we | |
168 * have to have enough trusted address space free to temporarily hold | |
169 * the maximum sized sysv shm object. | |
170 */ | |
171 static uintptr_t NaClDescSysvShmMap(struct NaClDesc *vself, | |
172 struct NaClDescEffector *effp, | |
173 void *start_addr, | |
174 size_t len, | |
175 int prot, | |
176 int flags, | |
177 nacl_off64_t offset) { | |
178 struct NaClDescSysvShm *self = (struct NaClDescSysvShm *) vself; | |
179 | |
180 int host_flags; | |
181 void *result; | |
182 | |
183 UNREFERENCED_PARAMETER(effp); | |
184 | |
185 NaClLog(4, | |
186 "NaClDescSysVShmMmap(,,0x%08"NACL_PRIxPTR",0x%"NACL_PRIxS"," | |
187 "0x%x,0x%x,0x%08"NACL_PRIxNACL_OFF64")\n", | |
188 (uintptr_t) start_addr, len, prot, flags, offset); | |
189 /* | |
190 * shm must have NACL_ABI_MAP_SHARED in flags. we check, and may | |
191 * relax this in the future. | |
192 */ | |
193 if ((NACL_ABI_MAP_SHARED) != | |
194 (flags & (NACL_ABI_MAP_SHARING_MASK))) { | |
195 NaClLog(LOG_INFO, | |
196 ("NaClDescSysvShmMap: Mapping not" | |
197 " NACL_ABI_MAP_SHARED\n")); | |
198 return -NACL_ABI_EINVAL; | |
199 } | |
200 | |
201 if (0 != (NACL_ABI_MAP_FIXED & flags) && NULL == start_addr) { | |
202 NaClLog(LOG_INFO, | |
203 ("NaClDescSysvShmMap: Mapping NACL_ABI_MAP_FIXED" | |
204 " but start_addr is NULL\n")); | |
205 return -NACL_ABI_EINVAL; | |
206 } | |
207 /* post-condition: if NULL == start_addr, then NACL_ABI_MAP_FIXED not set */ | |
208 | |
209 if (NULL == start_addr) { | |
210 uintptr_t addr; | |
211 | |
212 if (!NaClFindAddressSpace(&addr, len)) { | |
213 NaClLog(1, "NaClDescSysvShmMap: could not find address space\n"); | |
214 return -NACL_ABI_ENOMEM; | |
215 } | |
216 if (-1 == munmap((void *) addr, len)) { | |
217 NaClLog(LOG_FATAL, | |
218 "Could not unmap found space at 0x%"NACL_PRIxPTR"\n", | |
219 addr); | |
220 } | |
221 start_addr = (void *) addr; | |
222 } | |
223 | |
224 /* | |
225 * shmat can only map the shared memory region starting at its beginning | |
226 * and continuing for its entire size. | |
227 */ | |
228 if (0 != offset || self->size > len) { | |
229 NaClLog(LOG_INFO, | |
230 "NaClDescSysvShmMap: Mapping at non-zero offset or length" | |
231 " mismatch\n"); | |
232 return -NACL_ABI_EINVAL; | |
233 } | |
234 /* | |
235 * prot must be not be PROT_NONE nor contain other than PROT_{READ|WRITE} | |
236 */ | |
237 if (NACL_ABI_PROT_NONE == prot) { | |
238 NaClLog(LOG_INFO, "NaClDescSysvShmMap: PROT_NONE not supported\n"); | |
239 return -NACL_ABI_EINVAL; | |
240 } | |
241 if (0 == (NACL_ABI_PROT_READ & prot)) { | |
242 NaClLog(LOG_INFO, "NaClDescSysvShmMap: PROT_READ not set\n"); | |
243 return -NACL_ABI_EINVAL; | |
244 } | |
245 if (0 != (~(NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE) & prot)) { | |
246 NaClLog(LOG_INFO, | |
247 "NaClDescSysvShmMap: prot has other bits than PROT_{READ|WRITE}\n"); | |
248 return -NACL_ABI_EINVAL; | |
249 } | |
250 /* | |
251 * Map from NACL_ABI_ prot and flags bits to shmat flags. | |
252 */ | |
253 host_flags = SHM_REMAP; | |
254 if (NACL_ABI_PROT_READ == prot) { | |
255 host_flags |= SHM_RDONLY; | |
256 } | |
257 | |
258 /* | |
259 * TODO(bsy): !NACL_ABI_MAP_FIXED, start_addr == NULL, find address | |
260 * space (assuming no race), and set start_addr appropriately. | |
261 * | |
262 * If SHM_REMAP is defined, use it. Else munmap and then shmat, | |
263 * leaving a timing hole for multithreaded race. Fail if we lose | |
264 * the race and let the user code retry. | |
265 */ | |
266 /* | |
267 * Attach (map) the shared memory region. | |
268 */ | |
269 result = shmat(self->id, (void *) start_addr, host_flags); | |
270 if (NACL_MAP_FAILED == result) { | |
271 NaClLog(LOG_FATAL, "NaClDescSysvMap: shmat failed %d\n", errno); | |
272 } | |
273 if (result != start_addr) { | |
274 NaClLog(LOG_FATAL, | |
275 ("NaClDescSysvShmMap: NACL_MAP_FIXED but" | |
276 " got 0x%08"NACL_PRIxPTR" instead of 0x%08"NACL_PRIxPTR"\n"), | |
277 (uintptr_t) result, (uintptr_t) start_addr); | |
278 } | |
279 | |
280 return (uintptr_t) start_addr; | |
281 } | |
282 | |
283 static int NaClDescSysvShmFstat(struct NaClDesc *vself, | |
284 struct nacl_abi_stat *stbp) { | |
285 struct NaClDescSysvShm *self = (struct NaClDescSysvShm *) vself; | |
286 | |
287 if (self->size > INT32_MAX) { | |
288 return -NACL_ABI_EOVERFLOW; | |
289 } | |
290 | |
291 stbp->nacl_abi_st_dev = 0; | |
292 stbp->nacl_abi_st_ino = 0x6c43614e; | |
293 stbp->nacl_abi_st_mode = (NACL_ABI_S_IFSHM_SYSV | | |
294 NACL_ABI_S_IRUSR | | |
295 NACL_ABI_S_IWUSR); | |
296 stbp->nacl_abi_st_nlink = 1; | |
297 stbp->nacl_abi_st_uid = -1; | |
298 stbp->nacl_abi_st_gid = -1; | |
299 stbp->nacl_abi_st_rdev = 0; | |
300 /* TODO(sehr): this should really use shmctl instead. */ | |
301 stbp->nacl_abi_st_size = (nacl_abi_off_t) self->size; | |
302 stbp->nacl_abi_st_blksize = 0; | |
303 stbp->nacl_abi_st_blocks = 0; | |
304 stbp->nacl_abi_st_atime = 0; | |
305 stbp->nacl_abi_st_mtime = 0; | |
306 stbp->nacl_abi_st_ctime = 0; | |
307 | |
308 return 0; | |
309 } | |
310 | |
311 static int NaClDescSysvShmExternalizeSize(struct NaClDesc *vself, | |
312 size_t *nbytes, | |
313 size_t *nhandles) { | |
314 struct NaClDescSysvShm *self = (struct NaClDescSysvShm *) vself; | |
315 int rv; | |
316 | |
317 rv = NaClDescExternalizeSize(vself, nbytes, nhandles); | |
318 if (0 != rv) { | |
319 return rv; | |
320 } | |
321 *nbytes += sizeof self->id + sizeof(nacl_off64_t); | |
322 | |
323 return 0; | |
324 } | |
325 | |
326 static int NaClDescSysvShmExternalize(struct NaClDesc *vself, | |
327 struct NaClDescXferState *xfer) { | |
328 struct NaClDescSysvShm *self = (struct NaClDescSysvShm *) vself; | |
329 nacl_off64_t size64 = self->size; | |
330 int rv; | |
331 | |
332 rv = NaClDescExternalize(vself, xfer); | |
333 if (0 != rv) { | |
334 return rv; | |
335 } | |
336 memcpy(xfer->next_byte, &self->id, sizeof self->id); | |
337 xfer->next_byte += sizeof self->id; | |
338 memcpy(xfer->next_byte, &size64, sizeof size64); | |
339 xfer->next_byte += sizeof size64; | |
340 return 0; | |
341 } | |
342 | |
343 struct NaClDescVtbl const kNaClDescSysvShmVtbl = { | |
344 { | |
345 NaClDescSysvShmDtor, | |
346 }, | |
347 NaClDescSysvShmMap, | |
348 NACL_DESC_UNMAP_NOT_IMPLEMENTED | |
349 NaClDescReadNotImplemented, | |
350 NaClDescWriteNotImplemented, | |
351 NaClDescSeekNotImplemented, | |
352 NaClDescPReadNotImplemented, | |
353 NaClDescPWriteNotImplemented, | |
354 NaClDescSysvShmFstat, | |
355 NaClDescGetdentsNotImplemented, | |
356 NaClDescSysvShmExternalizeSize, | |
357 NaClDescSysvShmExternalize, | |
358 NaClDescLockNotImplemented, | |
359 NaClDescTryLockNotImplemented, | |
360 NaClDescUnlockNotImplemented, | |
361 NaClDescWaitNotImplemented, | |
362 NaClDescTimedWaitAbsNotImplemented, | |
363 NaClDescSignalNotImplemented, | |
364 NaClDescBroadcastNotImplemented, | |
365 NaClDescSendMsgNotImplemented, | |
366 NaClDescRecvMsgNotImplemented, | |
367 NaClDescLowLevelSendMsgNotImplemented, | |
368 NaClDescLowLevelRecvMsgNotImplemented, | |
369 NaClDescConnectAddrNotImplemented, | |
370 NaClDescAcceptConnNotImplemented, | |
371 NaClDescPostNotImplemented, | |
372 NaClDescSemWaitNotImplemented, | |
373 NaClDescGetValueNotImplemented, | |
374 NaClDescSetMetadata, | |
375 NaClDescGetMetadata, | |
376 NaClDescSetFlags, | |
377 NaClDescGetFlags, | |
378 NaClDescIsattyNotImplemented, | |
379 NACL_DESC_SYSV_SHM, | |
380 }; | |
381 | |
382 int NaClDescSysvShmInternalize(struct NaClDesc **out_desc, | |
383 struct NaClDescXferState *xfer, | |
384 struct NaClDescQuotaInterface *quota_interface) { | |
385 int rv; | |
386 struct NaClDescSysvShm *ndisp; | |
387 int id; | |
388 nacl_off64_t size; | |
389 | |
390 UNREFERENCED_PARAMETER(quota_interface); | |
391 rv = -NACL_ABI_EIO; | |
392 | |
393 ndisp = malloc(sizeof *ndisp); | |
394 if (NULL == ndisp) { | |
395 rv = -NACL_ABI_ENOMEM; | |
396 goto cleanup; | |
397 } | |
398 | |
399 if (!NaClDescInternalizeCtor((struct NaClDesc *) ndisp, xfer)) { | |
400 free(ndisp); | |
401 ndisp = NULL; | |
402 rv = -NACL_ABI_ENOMEM; | |
403 goto cleanup; | |
404 } | |
405 | |
406 if (xfer->next_byte + sizeof ndisp->id + sizeof ndisp->size > | |
407 xfer->byte_buffer_end) { | |
408 rv = -NACL_ABI_EIO; | |
409 goto cleanup; | |
410 } | |
411 | |
412 memcpy(&id, xfer->next_byte, sizeof id); | |
413 xfer->next_byte += sizeof id; | |
414 memcpy(&size, xfer->next_byte, sizeof size); | |
415 xfer->next_byte += sizeof size; | |
416 | |
417 if (!NaClDescSysvShmImportSubclassCtor(ndisp, id, size)) { | |
418 rv = -NACL_ABI_EIO; | |
419 goto cleanup; | |
420 } | |
421 | |
422 *out_desc = (struct NaClDesc *) ndisp; | |
423 rv = 0; | |
424 | |
425 cleanup: | |
426 if (rv < 0) { | |
427 NaClDescSafeUnref((struct NaClDesc *) ndisp); | |
428 } | |
429 return rv; | |
430 } | |
OLD | NEW |