int NaClHostDescFdatasync(struct NaClHostDesc *d) { NaClHostDescCheckValidity("NaClHostDescFdatasync", d); /* fdatasync does not exist for osx. */ #if NACL_OSX if (-1 == fsync(d->d)) { #else if (-1 == fdatasync(d->d)) { #endif return -NaClXlateErrno(errno); } return 0; } int NaClHostDescFtruncate(struct NaClHostDesc *d, nacl_off64_t length) { NaClHostDescCheckValidity("NaClHostDescFtruncate", d); #if NACL_LINUX if (-1 == ftruncate64(d->d, length)) { #elif NACL_OSX if (-1 == ftruncate(d->d, length)) { #else # error Unsupported platform #endif return -NaClXlateErrno(errno); } return 0; }
int NaClClockGetTime(nacl_clockid_t clk_id, struct nacl_abi_timespec *tp) { int rv = -NACL_ABI_EINVAL; struct timespec host_time; if (!g_NaClClock_is_initialized) { NaClLog(LOG_FATAL, "NaClClockGetTime invoked without successful NaClClockInit\n"); } switch (clk_id) { case NACL_CLOCK_REALTIME: if (0 != clock_gettime(CLOCK_REALTIME, &host_time)) { rv = -NaClXlateErrno(errno); } else { rv = 0; } break; case NACL_CLOCK_MONOTONIC: if (0 != clock_gettime(CLOCK_MONOTONIC, &host_time)) { rv = -NaClXlateErrno(errno); } else { rv = 0; } break; case NACL_CLOCK_PROCESS_CPUTIME_ID: case NACL_CLOCK_THREAD_CPUTIME_ID: rv = -NACL_ABI_EINVAL; break; } if (0 == rv) { tp->tv_sec = host_time.tv_sec; tp->tv_nsec = host_time.tv_nsec; } return rv; }
int NaClHostDirOpen(struct NaClHostDir *d, char *path) { int fd; struct stat stbuf; int rv; NaClLog(3, "NaClHostDirOpen(0x%08"NACL_PRIxPTR", %s)\n", (uintptr_t) d, path); if (NULL == d) { NaClLog(LOG_FATAL, "NaClHostDirOpen: 'this' is NULL\n"); } NaClLog(3, "NaClHostDirOpen: invoking open(%s)\n", path); fd = open(path, O_RDONLY); NaClLog(3, "NaClHostDirOpen: got DIR* %d\n", fd); if (-1 == fd) { NaClLog(LOG_ERROR, "NaClHostDirOpen: open returned -1, errno %d\n", errno); return -NaClXlateErrno(errno); } /* check that it is really a directory */ if (-1 == fstat(fd, &stbuf)) { NaClLog(LOG_ERROR, "NaClHostDirOpen: fstat failed?!? errno %d\n", errno); (void) close(fd); return -NaClXlateErrno(errno); } if (!S_ISDIR(stbuf.st_mode)) { (void) close(fd); return -NACL_ABI_ENOTDIR; } rv = NaClHostDirCtor(d, fd); return rv; }
int NaClHostDescOpen(struct NaClHostDesc *d, char const *path, int flags, int mode) { int host_desc; struct stat stbuf; NaClLog(3, "NaClHostDescOpen(0x%08"NACL_PRIxPTR", %s, 0x%x, 0x%x)\n", (uintptr_t) d, path, flags, mode); if (NULL == d) { NaClLog(LOG_FATAL, "NaClHostDescOpen: 'this' is NULL\n"); } /* * Sanitize access flags. */ if (0 != (flags & ~NACL_ALLOWED_OPEN_FLAGS)) { return -NACL_ABI_EINVAL; } switch (flags & NACL_ABI_O_ACCMODE) { case NACL_ABI_O_RDONLY: case NACL_ABI_O_WRONLY: case NACL_ABI_O_RDWR: break; default: NaClLog(LOG_ERROR, "NaClHostDescOpen: bad access flags 0x%x.\n", flags); return -NACL_ABI_EINVAL; } flags = NaClMapOpenFlags(flags); mode = NaClMapOpenPerm(mode); NaClLog(3, "NaClHostDescOpen: invoking POSIX open(%s,0x%x,0%o)\n", path, flags, mode); host_desc = open(path, flags, mode); NaClLog(3, "NaClHostDescOpen: got descriptor %d\n", host_desc); if (-1 == host_desc) { NaClLog(LOG_ERROR, "NaClHostDescOpen: open returned -1, errno %d\n", errno); return -NaClXlateErrno(errno); } if (-1 == fstat(host_desc, &stbuf)) { NaClLog(LOG_ERROR, "NaClHostDescOpen: fstat failed?!? errno %d\n", errno); (void) close(host_desc); return -NaClXlateErrno(errno); } if (!S_ISREG(stbuf.st_mode)) { NaClLog(LOG_INFO, "NaClHostDescOpen: file type 0x%x, not regular\n", stbuf.st_mode); (void) close(host_desc); /* cannot access anything other than a real file */ return -NACL_ABI_EPERM; } // return NaClHostDescCtor(d, host_desc, NULL); /* d'b ### */ return NaClHostDescCtor(d, host_desc); }
int NaClClockGetRes(nacl_clockid_t clk_id, struct nacl_abi_timespec *res) { int rv = -NACL_ABI_EINVAL; struct timespec host_res; clockid_t host_clk_id; if (!g_NaClClock_is_initialized) { NaClLog(LOG_FATAL, "NaClClockGetRes invoked without successful NaClClockInit\n"); } switch (clk_id) { case NACL_CLOCK_REALTIME: host_clk_id = CLOCK_REALTIME; rv = 0; break; case NACL_CLOCK_MONOTONIC: host_clk_id = CLOCK_MONOTONIC; rv = 0; break; case NACL_CLOCK_PROCESS_CPUTIME_ID: case NACL_CLOCK_THREAD_CPUTIME_ID: rv = -NACL_ABI_EINVAL; break; } if (0 == rv) { if (0 != clock_getres(host_clk_id, &host_res)) { rv = -NaClXlateErrno(errno); } } if (0 == rv) { res->tv_sec = host_res.tv_sec; res->tv_nsec = host_res.tv_nsec; } return rv; }
int NaClHostDescFchdir(struct NaClHostDesc *d) { NaClHostDescCheckValidity("NaClHostDescFchdir", d); if (-1 == fchdir(d->d)) { return -NaClXlateErrno(errno); } return 0; }
/* * NaClDescXferableDataDescSendMsg implements imc_sendmsg For * data-only descriptors. We assume that whatever protocol exists at * the NaClSendDatagram level is still not thread safe, but that the * lack of thread safety will not have a significant impact on * security. This is still somewhat brittle: the low-level Windows * IMC code can be convinced to do the wrong thing still via sender * races, but the receiver, because it expects zero handles, will have * called ReceiveDatagram in such a way that any "received handles" * are closed. This implies that arbitrary Windows handles can be * made to be closed, including those not accessible by NaCl modules, * but fortunately this should only result in a denial of service as * the error caused by the use of an invalid handle is detected by the * service runtime and cause an abort. * * Note that it is still an application error to send or receive data * with a transferable data-only descriptor from two threads (or two * modules) simultaneously. */ static ssize_t NaClDescXferableDataDescSendMsg(struct NaClDesc *vself, struct NaClMessageHeader const *dgram, int flags) { struct NaClDescXferableDataDesc *self = ((struct NaClDescXferableDataDesc *) vself); int result; if (0 != dgram->handle_count) { /* * A transferable descriptor cannot be used to transfer other * descriptors. */ NaClLog(2, ("NaClDescXferableDataDescSendMsg: tranferable and" " non-zero handle_count\n")); return -NACL_ABI_EINVAL; } result = NaClSendDatagram(self->base.h, dgram, flags); if (-1 == result) { return -NaClXlateErrno(errno); } return result; }
int NaClHostDescFsync(struct NaClHostDesc *d) { NaClHostDescCheckValidity("NaClHostDescFsync", d); if (-1 == fsync(d->d)) { return -NaClXlateErrno(errno); } return 0; }
nacl_off64_t NaClHostDescSeek(struct NaClHostDesc *d, nacl_off64_t offset, int whence) { nacl_off64_t retval; NaClHostDescCheckValidity("NaClHostDescSeek", d); #if NACL_LINUX return ((-1 == (retval = lseek64(d->d, offset, whence))) ? -NaClXlateErrno(errno) : retval); #elif NACL_OSX return ((-1 == (retval = lseek(d->d, offset, whence))) ? -NaClXlateErrno(errno) : retval); #else # error "What Unix-like OS is this?" #endif }
int NaClHostDescFchmod(struct NaClHostDesc *d, nacl_abi_mode_t mode) { NaClHostDescCheckValidity("NaClHostDescFchmod", d); if (-1 == fchmod(d->d, NaClMapMode(mode))) { return -NaClXlateErrno(errno); } return 0; }
ssize_t NaClHostDescWrite(struct NaClHostDesc *d, void const *buf, size_t len) { /* * See NaClHostDescPWrite for details for why need_lock is required. */ int need_lock; ssize_t retval; NaClHostDescCheckValidity("NaClHostDescWrite", d); if (NACL_ABI_O_RDONLY == (d->flags & NACL_ABI_O_ACCMODE)) { NaClLog(3, "NaClHostDescWrite: RDONLY file\n"); return -NACL_ABI_EBADF; } need_lock = NACL_LINUX && (0 != (d->flags & NACL_ABI_O_APPEND)); /* * Grab O_APPEND attribute lock, in case pwrite occurs in another * thread. */ if (need_lock) { NaClHostDescExclusiveLock(d->d); } retval = write(d->d, buf, len); if (need_lock) { NaClHostDescExclusiveUnlock(d->d); } if (-1 == retval) { retval = -NaClXlateErrno(errno); } return retval; }
/* * NaClDescXferableDataDescLowLevelSendMsg implements imc_sendmsg For * data-only descriptors. We assume that whatever protocol exists at * the NaClSendDatagram level is still not thread safe, but that the * lack of thread safety will not have a significant impact on * security. This is still somewhat brittle: the low-level Windows * IMC code can be convinced to do the wrong thing still via sender * races, but the receiver, because it expects zero handles, will have * called ReceiveDatagram in such a way that any "received handles" * are closed. This implies that arbitrary Windows handles can be * made to be closed, including those not accessible by NaCl modules, * but fortunately this should only result in a denial of service as * the error caused by the use of an invalid handle is detected by the * service runtime and cause an abort. * * Note that it is still an application error to send or receive data * with a transferable data-only descriptor from two threads (or two * modules) simultaneously. */ static ssize_t NaClDescXferableDataDescLowLevelSendMsg(struct NaClDesc *vself, struct NaClMessageHeader const *dgram, int flags) { struct NaClDescXferableDataDesc *self = ((struct NaClDescXferableDataDesc *) vself); int result; if (0 != dgram->handle_count) { /* * A transferable descriptor cannot be used to transfer other * descriptors. */ NaClLog(2, ("NaClDescXferableDataDescLowLevelSendMsg: tranferable and" " non-zero handle_count\n")); return -NACL_ABI_EINVAL; } result = NaClSendDatagram(self->base.h, dgram, flags); if (-1 == result) { #if NACL_WINDOWS return -NaClXlateSystemError(GetLastError()); #elif NACL_LINUX || NACL_OSX return -NaClXlateErrno(errno); #else # error "Unknown target platform: cannot translate error code(s) from SendMsg" #endif } return result; }
int NaClHostDescIsatty(struct NaClHostDesc *d) { int retval; NaClHostDescCheckValidity("NaClHostDescIsatty", d); retval = isatty(d->d); /* When isatty fails it returns zero and sets errno. */ return (0 == retval) ? -NaClXlateErrno(errno) : retval; }
/* * This is not a host descriptor function, but is closely related to * fstat and should behave similarly. */ int NaClHostDescStat(char const *path, nacl_host_stat_t *nhsp) { if (NACL_HOST_STAT64(path, nhsp) == -1) { return -NaClXlateErrno(errno); } return 0; }
/* * See NaClHostDescStat below. */ int NaClHostDescFstat(struct NaClHostDesc *d, nacl_host_stat_t *nhsp) { NaClHostDescCheckValidity("NaClHostDescFstat", d); if (NACL_HOST_FSTAT64(d->d, nhsp) == -1) { return -NaClXlateErrno(errno); } return 0; }
nacl_off64_t NaClHostDescSeek(struct NaClHostDesc *d, nacl_off64_t offset, int whence) { nacl_off64_t retval; if (NULL == d) { NaClLog(LOG_FATAL, "NaClHostDescSeek: 'this' is NULL\n"); } #if NACL_LINUX return ((-1 == (retval = lseek64(d->d, offset, whence))) ? -NaClXlateErrno(errno) : retval); #elif NACL_OSX return ((-1 == (retval = lseek(d->d, offset, whence))) ? -NaClXlateErrno(errno) : retval); #else # error "What Unix-like OS is this?" #endif }
ssize_t NaClHostDescRead(struct NaClHostDesc *d, void *buf, size_t len) { ssize_t retval; NaClHostDescCheckValidity("NaClHostDescRead", d); return ((-1 == (retval = read(d->d, buf, len))) ? -NaClXlateErrno(errno) : retval); }
ssize_t NaClHostDescWrite(struct NaClHostDesc *d, void const *buf, size_t len) { ssize_t retval; NaClHostDescCheckValidity("NaClHostDescWrite", d); return ((-1 == (retval = write(d->d, buf, len))) ? -NaClXlateErrno(errno) : retval); }
int NaClHostDescClose(struct NaClHostDesc *d) { int retval; NaClHostDescCheckValidity("NaClHostDescClose", d); retval = close(d->d); if (-1 != retval) { d->d = -1; } return (-1 == retval) ? -NaClXlateErrno(errno) : retval; }
static ssize_t NaClStreamDirents(struct NaClHostDir *d, void *buf, size_t len) { ssize_t retval; size_t xferred = 0; ssize_t entry_size; NaClXMutexLock(&d->mu); while (len > 0) { NaClLog(4, "NaClStreamDirents: loop, xferred = %"NACL_PRIuS"\n", xferred); entry_size = NaClCopyDirent(d, buf, len); if (0 == entry_size) { CHECK(d->cur_byte == d->nbytes); retval = getdents(d->fd, (struct dirent *) d->dirent_buf, sizeof d->dirent_buf); if (-1 == retval) { if (xferred > 0) { /* next time through, we'll pick up the error again */ goto cleanup; } else { xferred = -NaClXlateErrno(errno); goto cleanup; } } else if (0 == retval) { goto cleanup; } d->cur_byte = 0; d->nbytes = retval; } else if (entry_size < 0) { /* * The only error return from NaClCopyDirent is NACL_ABI_EINVAL * due to destinaton buffer too small for the current entry. If * we had copied some entries before, we were successful; * otherwise report that the buffer is too small for the next * directory entry. */ if (xferred > 0) { goto cleanup; } else { xferred = entry_size; goto cleanup; } } /* entry_size > 0, maybe copy another */ buf = (void *) ((char *) buf + entry_size); CHECK(len >= (size_t) entry_size); len -= entry_size; xferred += entry_size; } /* perfect fit! */ cleanup: NaClXMutexUnlock(&d->mu); return xferred; }
int NaClHostDirClose(struct NaClHostDir *d) { int retval; if (NULL == d) { NaClLog(LOG_FATAL, "NaClHostDirClose: 'this' is NULL\n"); } NaClLog(3, "NaClHostDirClose(%d)\n", d->fd); retval = close(d->fd); d->fd = -1; return (-1 == retval) ? -NaClXlateErrno(errno) : retval; }
ssize_t NaClHostDescWrite(struct NaClHostDesc *d, void const *buf, size_t len) { ssize_t retval; if (NULL == d) { NaClLog(LOG_FATAL, "NaClHostDescWrite: 'this' is NULL\n"); } return ((-1 == (retval = write(d->d, buf, len))) ? -NaClXlateErrno(errno) : retval); }
int NaClHostDescUnmapUnsafe(void *start_addr, size_t len) { int retval; uintptr_t addr; addr = (uintptr_t) start_addr; return ((-1 == (retval = munmap(start_addr, len))) ? -NaClXlateErrno(errno) : retval); }
int NaClHostDescClose(struct NaClHostDesc *d) { int retval; if (NULL == d) { NaClLog(LOG_FATAL, "NaClHostDescClose: 'this' is NULL\n"); } retval = close(d->d); if (-1 != retval) { d->d = -1; } return (-1 == retval) ? -NaClXlateErrno(errno) : retval; }
int NaClHostDirClose(struct NaClHostDir *d) { int retval; if (NULL == d) { NaClLog(LOG_FATAL, "NaClHostDirClose: 'this' is NULL\n"); } NaClLog(3, "NaClHostDirClose(0x%08"NACL_PRIxPTR")\n", (uintptr_t) d->dirp); retval = closedir(d->dirp); if (-1 != retval) { d->dirp = NULL; } return (-1 == retval) ? -NaClXlateErrno(errno) : retval; }
ssize_t NaClHostDescRead(struct NaClHostDesc *d, void *buf, size_t len) { ssize_t retval; NaClHostDescCheckValidity("NaClHostDescRead", d); if (NACL_ABI_O_WRONLY == (d->flags & NACL_ABI_O_ACCMODE)) { NaClLog(3, "NaClHostDescRead: WRONLY file\n"); return -NACL_ABI_EBADF; } return ((-1 == (retval = read(d->d, buf, len))) ? -NaClXlateErrno(errno) : retval); }
int NaClHostDescUtimes(const char *filename, const struct nacl_abi_timeval *times) { struct timeval host_times[2]; if (times != NULL) { host_times[0].tv_sec = times[0].nacl_abi_tv_sec; host_times[0].tv_usec = times[0].nacl_abi_tv_usec; host_times[1].tv_sec = times[1].nacl_abi_tv_sec; host_times[1].tv_usec = times[1].nacl_abi_tv_usec; } if (-1 == utimes(filename, (times != NULL ? host_times : NULL))) { return -NaClXlateErrno(errno); } return 0; }
/* * In the level of NaClDescImcDescSendMsg, we do not know what * protocol is implemented by NaClSendDatagram (and indeed, in the * Windows implementation, the access rights transfer involves a more * complex protocol to get the peer process id). Because the * underlying low-level IMC implementation does not provide thread * safety, this is the source of race conditions: two simultaneous * imc_sendmsg syscalls to the same descriptor could get their various * protcol bits interleaved, so the receiver will get garbled data * that cause the wrong underlying host OS descriptor to be made * available to the NaCl module. * * In order to address this issue, we (1) make descriptors that can * transfer other descriptors non-transferable, and (2) we use locking * so that only one thread can be performing (the low level bits of) * imc_msgsend at a time. The non-transferability of such descriptors * addresses the multi-module scenario, as opposed to the * multi-threaded scenario, where a sender race cause receiver * confusion. */ static ssize_t NaClDescImcDescSendMsg(struct NaClDesc *vself, struct NaClMessageHeader const *dgram, int flags) { struct NaClDescImcDesc *self = ((struct NaClDescImcDesc *) vself); int result; NaClXMutexLock(&self->sendmsg_mu); result = NaClSendDatagram(self->base.h, dgram, flags); NaClXMutexUnlock(&self->sendmsg_mu); if (-1 == result) { return -NaClXlateErrno(errno); } return result; }
int NaClHostDescUnmap(void *start_addr, size_t len) { int retval; uintptr_t addr; addr = (uintptr_t) start_addr; return ((-1 == (retval = (uintptr_t) mmap(start_addr, len, PROT_NONE, (MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED), -1, (nacl_off64_t) 0))) ? -NaClXlateErrno(errno) : retval); }
int NaClHostDescOpen(struct NaClHostDesc *d, char const *path, int flags, int mode) { int host_desc; int posix_flags; NaClLog(3, "NaClHostDescOpen(0x%08"NACL_PRIxPTR", %s, 0x%x, 0x%x)\n", (uintptr_t) d, path, flags, mode); if (NULL == d) { NaClLog(LOG_FATAL, "NaClHostDescOpen: 'this' is NULL\n"); } /* * Sanitize access flags. */ if (0 != (flags & ~NACL_ALLOWED_OPEN_FLAGS)) { return -NACL_ABI_EINVAL; } switch (flags & NACL_ABI_O_ACCMODE) { case NACL_ABI_O_RDONLY: case NACL_ABI_O_WRONLY: case NACL_ABI_O_RDWR: break; default: NaClLog(LOG_ERROR, "NaClHostDescOpen: bad access flags 0x%x.\n", flags); return -NACL_ABI_EINVAL; } posix_flags = NaClMapOpenFlags(flags); #if NACL_LINUX posix_flags |= O_LARGEFILE; #endif mode = NaClMapOpenPerm(mode); NaClLog(3, "NaClHostDescOpen: invoking POSIX open(%s,0x%x,0%o)\n", path, posix_flags, mode); host_desc = open(path, posix_flags, mode); NaClLog(3, "NaClHostDescOpen: got descriptor %d\n", host_desc); if (-1 == host_desc) { NaClLog(2, "NaClHostDescOpen: open returned -1, errno %d\n", errno); return -NaClXlateErrno(errno); } return NaClHostDescCtor(d, host_desc, flags); }