| 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 #include <errno.h> | |
| 8 #include <string.h> | |
| 9 | |
| 10 #include "native_client/src/include/portability.h" | |
| 11 | |
| 12 #include "native_client/src/shared/platform/nacl_check.h" | |
| 13 #include "native_client/src/shared/platform/nacl_host_desc.h" | |
| 14 #include "native_client/src/shared/platform/nacl_log.h" | |
| 15 | |
| 16 #include "native_client/src/trusted/gio/gio_shm.h" | |
| 17 #include "native_client/src/trusted/service_runtime/include/bits/mman.h" | |
| 18 #include "native_client/src/trusted/service_runtime/include/sys/stat.h" | |
| 19 #include "native_client/src/trusted/service_runtime/nacl_config.h" | |
| 20 #include "native_client/src/trusted/service_runtime/sel_util.h" | |
| 21 | |
| 22 #include "native_client/src/trusted/desc/nacl_desc_base.h" | |
| 23 #include "native_client/src/trusted/desc/nacl_desc_effector_trusted_mem.h" | |
| 24 #include "native_client/src/trusted/desc/nacl_desc_imc_shm.h" | |
| 25 | |
| 26 /* | |
| 27 * This code maps in GIO_SHM_WINDOWSIZE bytes at a time for doing | |
| 28 * "I/O" from/to the shared memory object. This value must be an | |
| 29 * integer multiple of NACL_MAP_PAGESIZE. | |
| 30 */ | |
| 31 #define GIO_SHM_WINDOWSIZE (16 * NACL_MAP_PAGESIZE) | |
| 32 | |
| 33 /* | |
| 34 * Release current window if it exists, then map in window at the | |
| 35 * provided new_window_offset. This is akin to filbuf. | |
| 36 * | |
| 37 * Preconditions: 0 == (new_win_offset & (NACL_MAP_PAGESIZE - 1)) | |
| 38 * new_win_offset < self->shm_sz | |
| 39 */ | |
| 40 static int NaClGioShmSetWindow(struct NaClGioShm *self, | |
| 41 size_t new_win_offset) { | |
| 42 uintptr_t map_result; | |
| 43 size_t actual_len; | |
| 44 | |
| 45 NaClLog(4, | |
| 46 "NaClGioShmSetWindow: new_win_offset 0x%"NACL_PRIxS"\n", | |
| 47 new_win_offset); | |
| 48 if (0 != (new_win_offset & (NACL_MAP_PAGESIZE - 1))) { | |
| 49 NaClLog(LOG_FATAL, | |
| 50 ("NaClGioShmSetWindow: internal error, requested" | |
| 51 " new window offset 0x%"NACL_PRIxS" is not aligned.\n"), | |
| 52 new_win_offset); | |
| 53 } | |
| 54 | |
| 55 if (new_win_offset >= self->shm_sz) { | |
| 56 NaClLog(LOG_FATAL, | |
| 57 ("NaClGioShmSetWindow: setting window beyond end of shm object" | |
| 58 " offset 0x%"NACL_PRIxS", size 0x%"NACL_PRIxS"\n"), | |
| 59 new_win_offset, self->shm_sz); | |
| 60 } | |
| 61 | |
| 62 if (NULL != self->cur_window) { | |
| 63 NaClDescUnmapUnsafe(self->shmp, (void *) self->cur_window, | |
| 64 self->window_size); | |
| 65 } | |
| 66 self->cur_window = NULL; | |
| 67 self->window_size = 0; | |
| 68 | |
| 69 /* | |
| 70 * The Map virtual function will NOT pad space beyond the end of the | |
| 71 * memory mapping object with zero-filled pages. This is done for | |
| 72 * user code in nacl_syscall_common.c (NaClSysMmap), and the Map | |
| 73 * virtual function exposes the behavioral inconsistencies wrt | |
| 74 * allowing but ignoring mapping an offset beyond the end of file | |
| 75 * (linux) versus disallowing the mapping (MapViewOfFileEx). | |
| 76 * | |
| 77 * Here, we know the actual size of the shm object, and can deal | |
| 78 * with it. | |
| 79 */ | |
| 80 actual_len = GIO_SHM_WINDOWSIZE; | |
| 81 if (actual_len > self->shm_sz - new_win_offset) { | |
| 82 actual_len = self->shm_sz - new_win_offset; | |
| 83 } | |
| 84 map_result = | |
| 85 (*((struct NaClDescVtbl const *) self->shmp->base.vtbl)-> | |
| 86 Map)(self->shmp, | |
| 87 NaClDescEffectorTrustedMem(), | |
| 88 (void *) NULL, | |
| 89 actual_len, | |
| 90 NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE, | |
| 91 NACL_ABI_MAP_SHARED, | |
| 92 (nacl_off64_t) new_win_offset); | |
| 93 NaClLog(4, | |
| 94 "NaClGioShmSetWindow: Map returned 0x%"NACL_PRIxPTR"\n", | |
| 95 map_result); | |
| 96 if (NaClPtrIsNegErrno(&map_result)) { | |
| 97 return 0; | |
| 98 } | |
| 99 | |
| 100 self->cur_window = (char *) map_result; | |
| 101 self->window_size = actual_len; | |
| 102 self->window_offset = new_win_offset; | |
| 103 | |
| 104 return 1; | |
| 105 } | |
| 106 | |
| 107 static ssize_t NaClGioShmReadOrWrite(struct Gio *vself, | |
| 108 void *buf, | |
| 109 size_t count, | |
| 110 int is_write) { | |
| 111 struct NaClGioShm *self = (struct NaClGioShm *) vself; | |
| 112 size_t new_window_offset; | |
| 113 size_t transfer; | |
| 114 size_t window_end; | |
| 115 size_t window_remain; | |
| 116 size_t sofar; | |
| 117 | |
| 118 NaClLog(4, | |
| 119 ("NaClGioShmReadOrWrite: 0x%"NACL_PRIxPTR"," | |
| 120 " 0x%"NACL_PRIxPTR", 0x%"NACL_PRIxS", %d\n"), | |
| 121 (uintptr_t) vself, | |
| 122 (uintptr_t) buf, | |
| 123 count, | |
| 124 is_write); | |
| 125 sofar = 0; | |
| 126 while (count > 0) { | |
| 127 NaClLog(4, "NaClGioShmReadOrWrite: count 0x%"NACL_PRIxS"\n", count); | |
| 128 if (self->io_offset >= self->shm_sz) { | |
| 129 break; | |
| 130 } | |
| 131 NaClLog(4, " cur_window 0x%"NACL_PRIxPTR"\n", | |
| 132 (uintptr_t) self->cur_window); | |
| 133 NaClLog(4, " io_offset 0x%"NACL_PRIxS"\n", self->io_offset); | |
| 134 NaClLog(4, "window_offset 0x%"NACL_PRIxS"\n", self->window_offset); | |
| 135 if (NULL == self->cur_window | |
| 136 || self->io_offset < self->window_offset | |
| 137 || self->window_offset + self->window_size <= self->io_offset) { | |
| 138 /* | |
| 139 * io_offset is outside the window. move the window so that | |
| 140 * it's within. | |
| 141 */ | |
| 142 NaClLog(4, "Seek required\n"); | |
| 143 | |
| 144 new_window_offset = (self->io_offset | |
| 145 & (~(((size_t) NACL_MAP_PAGESIZE) - 1))); | |
| 146 NaClLog(4, "new_window_offset 0x%"NACL_PRIxS"\n", new_window_offset); | |
| 147 CHECK(0 == (new_window_offset & | |
| 148 (((size_t) NACL_MAP_PAGESIZE)-1))); | |
| 149 if (!NaClGioShmSetWindow(self, new_window_offset)) { | |
| 150 if (0 == sofar) { | |
| 151 errno = EIO; | |
| 152 sofar = -1; | |
| 153 } | |
| 154 return sofar; | |
| 155 } | |
| 156 } else { | |
| 157 NaClLog(4, "no seek required\n"); | |
| 158 } | |
| 159 NaClLog(4, " cur_window 0x%"NACL_PRIxPTR"\n", | |
| 160 (uintptr_t) self->cur_window); | |
| 161 NaClLog(4, " io_offset 0x%"NACL_PRIxS"\n", self->io_offset); | |
| 162 NaClLog(4, "window_offset 0x%"NACL_PRIxS"\n", self->window_offset); | |
| 163 | |
| 164 CHECK(self->window_offset <= self->io_offset); | |
| 165 CHECK(self->io_offset < self->window_offset + self->window_size); | |
| 166 | |
| 167 transfer = count; | |
| 168 window_end = self->window_offset + self->window_size; | |
| 169 if (window_end > self->shm_sz) { | |
| 170 window_end = self->shm_sz; | |
| 171 } | |
| 172 window_remain = window_end - self->io_offset; | |
| 173 | |
| 174 NaClLog(4, "remaining in window 0x%"NACL_PRIxS"\n", window_remain); | |
| 175 | |
| 176 CHECK(window_remain <= GIO_SHM_WINDOWSIZE); | |
| 177 | |
| 178 if (transfer > window_remain) { | |
| 179 transfer = window_remain; | |
| 180 } | |
| 181 | |
| 182 NaClLog(4, "transfer 0x%"NACL_PRIxS"\n", transfer); | |
| 183 | |
| 184 if (is_write) { | |
| 185 NaClLog(4, | |
| 186 ("about to \"write\" memcpy(0x%"NACL_PRIxPTR", " | |
| 187 " 0x%"NACL_PRIxPTR", 0x%"NACL_PRIxS" bytes)\n"), | |
| 188 (uintptr_t) (self->cur_window | |
| 189 + (self->io_offset - self->window_offset)), | |
| 190 (uintptr_t) buf, | |
| 191 transfer); | |
| 192 | |
| 193 memcpy(self->cur_window + (self->io_offset - self->window_offset), | |
| 194 buf, | |
| 195 transfer); | |
| 196 } else { | |
| 197 NaClLog(4, | |
| 198 ("about to \"read\" memcpy(0x%"NACL_PRIxPTR", " | |
| 199 " 0x%"NACL_PRIxPTR", 0x%"NACL_PRIxS" bytes)\n"), | |
| 200 (uintptr_t) buf, | |
| 201 (uintptr_t) (self->cur_window | |
| 202 + (self->io_offset - self->window_offset)), | |
| 203 transfer); | |
| 204 | |
| 205 memcpy(buf, | |
| 206 self->cur_window + (self->io_offset - self->window_offset), | |
| 207 transfer); | |
| 208 } | |
| 209 self->io_offset += transfer; | |
| 210 sofar += transfer; | |
| 211 | |
| 212 buf = (void *)((uintptr_t) buf + transfer); | |
| 213 count -= transfer; | |
| 214 } | |
| 215 | |
| 216 return sofar; | |
| 217 } | |
| 218 | |
| 219 static ssize_t NaClGioShmRead(struct Gio *vself, | |
| 220 void *buf, | |
| 221 size_t count) { | |
| 222 return NaClGioShmReadOrWrite(vself, buf, count, 0); | |
| 223 } | |
| 224 | |
| 225 static ssize_t NaClGioShmWrite(struct Gio *vself, | |
| 226 const void *buf, | |
| 227 size_t count) { | |
| 228 return NaClGioShmReadOrWrite(vself, (void *) buf, count, 1); | |
| 229 } | |
| 230 | |
| 231 static off_t NaClGioShmSeek(struct Gio *vself, | |
| 232 off_t offset, | |
| 233 int whence) { | |
| 234 struct NaClGioShm *self = (struct NaClGioShm *) vself; | |
| 235 size_t new_pos = (size_t) -1; | |
| 236 | |
| 237 NaClLog(4, "NaClGioShmSeek(0x%"NACL_PRIxPTR", %ld (0x%lx), %d)\n", | |
| 238 (uintptr_t) vself, (long) offset, (long) offset, whence); | |
| 239 /* | |
| 240 * Note that if sizeof(new_pos) < sizeof(offset), we are dropping | |
| 241 * high-order bits and we do not detect this. However, the check | |
| 242 * after the switch keeps the values somewhat sane: we will never | |
| 243 * set the I/O offset to be outside the range [0, self->shm_sz]. | |
| 244 */ | |
| 245 switch (whence) { | |
| 246 case SEEK_SET: | |
| 247 new_pos = (size_t) offset; | |
| 248 break; | |
| 249 case SEEK_CUR: | |
| 250 new_pos = self->io_offset + offset; | |
| 251 break; | |
| 252 case SEEK_END: | |
| 253 new_pos = self->shm_sz + offset; | |
| 254 break; | |
| 255 } | |
| 256 /* allow equality, so setting to the end of file is okay */ | |
| 257 if (self->shm_sz < new_pos) { | |
| 258 NaClLog(4, " invalid offset\n"); | |
| 259 errno = EINVAL; | |
| 260 return -1; | |
| 261 } | |
| 262 NaClLog(4, " setting to %ld (0x%lx)\n", (long) new_pos, (long) new_pos); | |
| 263 /* sizeof(off_t) >= sizeof(size_t) */ | |
| 264 self->io_offset = new_pos; | |
| 265 return (off_t) self->io_offset; | |
| 266 } | |
| 267 | |
| 268 static int NaClGioShmFlush(struct Gio *vself) { | |
| 269 UNREFERENCED_PARAMETER(vself); | |
| 270 return 0; | |
| 271 } | |
| 272 | |
| 273 static int NaClGioShmClose(struct Gio *vself) { | |
| 274 struct NaClGioShm *self = (struct NaClGioShm *) vself; | |
| 275 | |
| 276 if (NULL != self->cur_window) { | |
| 277 NaClDescUnmapUnsafe(self->shmp, (void *) self->cur_window, | |
| 278 NACL_MAP_PAGESIZE); | |
| 279 } | |
| 280 self->cur_window = NULL; | |
| 281 | |
| 282 if (NULL == self->shmp) { | |
| 283 NaClLog(LOG_ERROR, "NaClGioShmClose: double close detected\n"); | |
| 284 errno = EIO; | |
| 285 return -1; | |
| 286 } | |
| 287 | |
| 288 NaClDescUnref(self->shmp); | |
| 289 self->shmp = NULL; /* double close will fault */ | |
| 290 return 0; | |
| 291 } | |
| 292 | |
| 293 static void NaClGioShmDtor(struct Gio *vself) { | |
| 294 struct NaClGioShm *self = (struct NaClGioShm *) vself; | |
| 295 | |
| 296 /* | |
| 297 * Users of Gio objects are expected to Close then Dtor, but Dtor | |
| 298 * should cleanup regardless. | |
| 299 */ | |
| 300 if (NULL != self->shmp) { | |
| 301 if (-1 == (*vself->vtbl->Close)(vself)) { | |
| 302 NaClLog(LOG_ERROR, "NaClGioShmDtor: auto Close failed!\n"); | |
| 303 } | |
| 304 } | |
| 305 | |
| 306 self->shmp = NULL; | |
| 307 self->base.vtbl = NULL; | |
| 308 } | |
| 309 | |
| 310 const struct GioVtbl kNaClGioShmVtbl = { | |
| 311 NaClGioShmDtor, | |
| 312 NaClGioShmRead, | |
| 313 NaClGioShmWrite, | |
| 314 NaClGioShmSeek, | |
| 315 NaClGioShmFlush, | |
| 316 NaClGioShmClose, | |
| 317 }; | |
| 318 | |
| 319 | |
| 320 static int NaClGioShmCtorIntern(struct NaClGioShm *self, | |
| 321 struct NaClDesc *shmp, | |
| 322 size_t shm_size) { | |
| 323 struct nacl_abi_stat stbuf; | |
| 324 int vfret; | |
| 325 int rval = 0; | |
| 326 | |
| 327 self->base.vtbl = NULL; | |
| 328 | |
| 329 self->shmp = NULL; | |
| 330 self->cur_window = NULL; | |
| 331 | |
| 332 if (0 != (vfret = (*((struct NaClDescVtbl const *) shmp->base.vtbl)-> | |
| 333 Fstat)(shmp, &stbuf))) { | |
| 334 NaClLog(1, "NaClGioShmCtorIntern: Fstat virtual function returned %d\n", | |
| 335 vfret); | |
| 336 goto cleanup; | |
| 337 } | |
| 338 /* | |
| 339 * nacl_abi_off_t is signed 32-bit quantity, but we don't want to | |
| 340 * hardwire in that knowledge here. | |
| 341 * | |
| 342 * size_t is unsigned, and may be 32-bits or 64-bits, depending on | |
| 343 * the underlying host OS. | |
| 344 * | |
| 345 * we want to ensure that the shm's size, as reported by the desc | |
| 346 * abstraction and thus is in nacl_abi_off_t, is at least that | |
| 347 * claimed by the ctor argument. so, if (as Integers) | |
| 348 * | |
| 349 * stbuf.nacl_abi_st_size < shm_size | |
| 350 * | |
| 351 * holds, this is an error. however, the value-preserving cast rule | |
| 352 * makes this harder. | |
| 353 * | |
| 354 * Note that for signed sizes (ssize_t), the kernel ABI generally | |
| 355 * only reserve -1 for error, and asking for an I/O operation via a | |
| 356 * size_t that would succeed but yield a ssize_t return value that | |
| 357 * is negative is okay, since -1 is never valid as an I/O size on a | |
| 358 * von Neuman machine (except for a writev where the iov entries | |
| 359 * overlap): there just isn't that much data to read/write, when the | |
| 360 * instructions also take up space in the process address space. | |
| 361 * Whether requiring the programmer to detect this corner case is | |
| 362 * advisable is a different argument -- similar to negative ssize_t | |
| 363 * sizes, the syscall can just succeed with a partial transfer to | |
| 364 * avoid returning -1 on a success, just as we could avoid returning | |
| 365 * negative values; in practice, we do the latter, since we often | |
| 366 * see code written that tests for syscall error by comparing the | |
| 367 * return value to see if it is less than zero, rather than if it is | |
| 368 * equal to -1. | |
| 369 */ | |
| 370 if (stbuf.nacl_abi_st_size < 0) { | |
| 371 NaClLog(LOG_ERROR, "NaClGioShmCtorIntern: actual shm size negative\n"); | |
| 372 goto cleanup; | |
| 373 } | |
| 374 if (stbuf.nacl_abi_st_size <= (nacl_abi_off_t) SIZE_T_MAX | |
| 375 && (size_t) stbuf.nacl_abi_st_size < shm_size) { | |
| 376 NaClLog(LOG_ERROR, | |
| 377 "NaClGioShmCtorIntern: claimed shm file size greater than" | |
| 378 " actual shm segment size\n"); | |
| 379 goto cleanup; | |
| 380 } | |
| 381 if (OFF_T_MAX < SIZE_T_MAX && (size_t) OFF_T_MAX < shm_size) { | |
| 382 NaClLog(LOG_ERROR, | |
| 383 ("NaClGioShmCtorIntern: claimed shm file size greater than" | |
| 384 " off_t max value, %"NACL_PRId64"\n"), | |
| 385 (int64_t) OFF_T_MAX); | |
| 386 goto cleanup; | |
| 387 } | |
| 388 | |
| 389 self->shmp = NaClDescRef(shmp); | |
| 390 | |
| 391 self->io_offset = 0; | |
| 392 self->shm_sz = shm_size; | |
| 393 self->window_offset = 0; | |
| 394 | |
| 395 self->base.vtbl = &kNaClGioShmVtbl; | |
| 396 | |
| 397 if (!NaClGioShmSetWindow(self, 0)) { | |
| 398 NaClLog(LOG_ERROR, | |
| 399 ("NaClGioShmCtorIntern: initial seek to beginning failed\n")); | |
| 400 NaClDescUnref(self->shmp); | |
| 401 self->shmp = NULL; | |
| 402 self->shm_sz = 0; | |
| 403 self->base.vtbl = NULL; | |
| 404 goto cleanup; | |
| 405 } | |
| 406 | |
| 407 rval = 1; | |
| 408 cleanup: | |
| 409 return rval; | |
| 410 } | |
| 411 | |
| 412 int NaClGioShmCtor(struct NaClGioShm *self, | |
| 413 struct NaClDesc *shmp, | |
| 414 size_t shm_size) { | |
| 415 | |
| 416 int rv; | |
| 417 | |
| 418 CHECK(shm_size == NaClRoundAllocPage(shm_size)); | |
| 419 | |
| 420 rv = NaClGioShmCtorIntern(self, shmp, shm_size); | |
| 421 | |
| 422 return rv; | |
| 423 } | |
| 424 | |
| 425 int NaClGioShmAllocCtor(struct NaClGioShm *self, | |
| 426 size_t shm_size) { | |
| 427 struct NaClDescImcShm *shmp; | |
| 428 int rv; | |
| 429 | |
| 430 CHECK(shm_size == NaClRoundAllocPage(shm_size)); | |
| 431 | |
| 432 shmp = malloc(sizeof *shmp); | |
| 433 if (NULL == shmp) { | |
| 434 return 0; | |
| 435 } | |
| 436 if (!NaClDescImcShmAllocCtor(shmp, shm_size, /* executable= */ 0)) { | |
| 437 free(shmp); | |
| 438 return 0; | |
| 439 } | |
| 440 | |
| 441 rv = NaClGioShmCtorIntern(self, (struct NaClDesc *) shmp, shm_size); | |
| 442 NaClDescUnref((struct NaClDesc *) shmp); | |
| 443 | |
| 444 if (!rv) { | |
| 445 free(shmp); | |
| 446 } | |
| 447 return rv; | |
| 448 } | |
| OLD | NEW |