| 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 |