int32_t NaClSysFstat(struct NaClAppThread *natp, int d, uint32_t nasp) { struct NaClApp *nap = natp->nap; int32_t retval = -NACL_ABI_EINVAL; struct NaClDesc *ndp; struct nacl_abi_stat result; NaClLog(3, ("Entered NaClSysFstat(0x%08"NACL_PRIxPTR ", %d, 0x%08"NACL_PRIx32")\n"), (uintptr_t) natp, d, nasp); ndp = NaClAppGetDesc(nap, d); if (NULL == ndp) { NaClLog(4, "bad desc\n"); retval = -NACL_ABI_EBADF; goto cleanup; } retval = (*((struct NaClDescVtbl const *) ndp->base.vtbl)-> Fstat)(ndp, &result); if (0 == retval) { if (!NaClFileAccessEnabled()) { result.nacl_abi_st_ino = NACL_FAKE_INODE_NUM; } if (!NaClCopyOutToUser(nap, nasp, &result, sizeof result)) { retval = -NACL_ABI_EFAULT; } } NaClDescUnref(ndp); cleanup: return retval; }
int32_t NaClSysGetcwd(struct NaClAppThread *natp, uint32_t buffer, int len) { struct NaClApp *nap = natp->nap; int32_t retval = -NACL_ABI_EINVAL; char path[NACL_CONFIG_PATH_MAX]; if (!NaClAclBypassChecks) { retval = -NACL_ABI_EACCES; goto cleanup; } if (len >= NACL_CONFIG_PATH_MAX) len = NACL_CONFIG_PATH_MAX - 1; retval = NaClHostDescGetcwd(path, len); if (retval != 0) goto cleanup; if (!NaClCopyOutToUser(nap, buffer, &path, strlen(path) + 1)) retval = -NACL_ABI_EFAULT; cleanup: return retval; }
int32_t NaClSysLstat(struct NaClAppThread *natp, uint32_t pathname, uint32_t nasp) { struct NaClApp *nap = natp->nap; int32_t retval = -NACL_ABI_EINVAL; char path[NACL_CONFIG_PATH_MAX]; nacl_host_stat_t stbuf; NaClLog(3, ("Entered NaClSysLstat(0x%08"NACL_PRIxPTR", 0x%08"NACL_PRIx32"," " 0x%08"NACL_PRIx32")\n"), (uintptr_t) natp, pathname, nasp); if (!NaClAclBypassChecks) { return -NACL_ABI_EACCES; } retval = CopyPathFromUser(nap, path, sizeof path, pathname); if (0 != retval) return retval; /* * Perform a host stat. */ retval = NaClHostDescLstat(path, &stbuf); if (0 == retval) { struct nacl_abi_stat abi_stbuf; retval = NaClAbiStatHostDescStatXlateCtor(&abi_stbuf, &stbuf); if (!NaClCopyOutToUser(nap, nasp, &abi_stbuf, sizeof abi_stbuf)) { return -NACL_ABI_EFAULT; } } return retval; }
int32_t NaClSysImcMakeBoundSock(struct NaClAppThread *natp, uint32_t descs_addr) { /* * Create a bound socket descriptor and a socket address descriptor. */ struct NaClApp *nap = natp->nap; int32_t retval = -NACL_ABI_EINVAL; struct NaClDesc *pair[2]; int32_t usr_pair[2]; /* This syscall is not used in Chromium so is disabled by default. */ if (!NaClAclBypassChecks) { return -NACL_ABI_EACCES; } NaClLog(3, ("Entered NaClSysImcMakeBoundSock(0x%08"NACL_PRIxPTR"," " 0x%08"NACL_PRIx32")\n"), (uintptr_t) natp, descs_addr); retval = NaClCommonDescMakeBoundSock(pair); if (0 != retval) { goto cleanup; } usr_pair[0] = NaClAppSetDescAvail(nap, pair[0]); usr_pair[1] = NaClAppSetDescAvail(nap, pair[1]); if (!NaClCopyOutToUser(nap, descs_addr, usr_pair, sizeof usr_pair)) { /* * NB: The descriptors were briefly observable to untrusted code * in this window, even though the syscall had not returned yet, * and another thread which guesses their numbers could actually * use them, so the NaClDescSafeUnref inside NaClAppSetDesc below * might not actually deallocate right away. To avoid this, we * could grab the descriptor lock and hold it until after the * copyout is done, but that imposes an ordering between the * descriptor lock and the VM lock which can cause problems * elsewhere. */ NaClAppSetDesc(nap, usr_pair[0], NULL); NaClAppSetDesc(nap, usr_pair[1], NULL); retval = -NACL_ABI_EFAULT; goto cleanup; } retval = 0; cleanup: return retval; }
/* * This implements 64-bit offsets, so we use |offp| as an in/out * address so we can have a 64 bit return value. */ int32_t NaClSysLseek(struct NaClAppThread *natp, int d, uint32_t offp, int whence) { struct NaClApp *nap = natp->nap; nacl_abi_off_t offset; nacl_off64_t retval64; int32_t retval = -NACL_ABI_EINVAL; struct NaClDesc *ndp; NaClLog(3, ("Entered NaClSysLseek(0x%08"NACL_PRIxPTR", %d," " 0x%08"NACL_PRIx32", %d)\n"), (uintptr_t) natp, d, offp, whence); ndp = NaClAppGetDesc(nap, d); if (NULL == ndp) { retval = -NACL_ABI_EBADF; goto cleanup; } if (!NaClCopyInFromUser(nap, &offset, offp, sizeof offset)) { retval = -NACL_ABI_EFAULT; goto cleanup_unref; } NaClLog(4, "offset 0x%08"NACL_PRIx64"\n", (uint64_t) offset); retval64 = (*((struct NaClDescVtbl const *) ndp->base.vtbl)-> Seek)(ndp, (nacl_off64_t) offset, whence); if (NaClOff64IsNegErrno(&retval64)) { retval = (int32_t) retval64; } else { if (NaClCopyOutToUser(nap, offp, &retval64, sizeof retval64)) { retval = 0; } else { NaClLog(LOG_FATAL, "NaClSysLseek: in/out ptr became invalid at copyout?\n"); } } cleanup_unref: NaClDescUnref(ndp); cleanup: return retval; }
int32_t NaClSysReadlink(struct NaClAppThread *natp, uint32_t path, uint32_t buffer, uint32_t buffer_size) { struct NaClApp *nap = natp->nap; char pathname[NACL_CONFIG_PATH_MAX]; char realpath[NACL_CONFIG_PATH_MAX]; int32_t retval = -NACL_ABI_EINVAL; uint32_t result_size; if (!NaClAclBypassChecks) return -NACL_ABI_EACCES; retval = CopyPathFromUser(nap, pathname, sizeof pathname, path); if (0 != retval) return retval; retval = NaClHostDescReadlink(pathname, realpath, sizeof(realpath)); if (retval < 0) return retval; result_size = retval; CHECK(result_size <= sizeof(realpath)); /* Sanity check */ if (result_size == sizeof(realpath)) { /* * The result either got truncated or it fit exactly. Treat it as * truncation. * * We can't distinguish an exact fit from truncation without doing * another readlink() call. If result_size == buffer_size, we could * return success here, but there's little point, because untrusted * code can't distinguish the two either and we don't currently allow * using a larger buffer. */ return -NACL_ABI_ENAMETOOLONG; } if (result_size > buffer_size) result_size = buffer_size; if (!NaClCopyOutToUser(nap, buffer, realpath, result_size)) return -NACL_ABI_EFAULT; return result_size; }
int32_t NaClSysExceptionHandler(struct NaClAppThread *natp, uint32_t handler_addr, uint32_t old_handler) { struct NaClApp *nap = natp->nap; int32_t rv = -NACL_ABI_EINVAL; if (!nap->enable_exception_handling) { rv = -NACL_ABI_ENOSYS; goto no_lock_exit; } if (!NaClIsValidJumpTarget(nap, handler_addr)) { rv = -NACL_ABI_EFAULT; goto no_lock_exit; } NaClXMutexLock(&nap->exception_mu); /* * This needs to be done while holding the lock so that we don't * start two Windows debug exception handlers. */ if (handler_addr != 0) { if (!NaClDebugExceptionHandlerEnsureAttached(nap)) { rv = -NACL_ABI_ENOSYS; goto unlock_exit; } } if (0 != old_handler && !NaClCopyOutToUser(nap, (uintptr_t) old_handler, &nap->exception_handler, sizeof nap->exception_handler)) { rv = -NACL_ABI_EFAULT; goto unlock_exit; } nap->exception_handler = handler_addr; rv = 0; unlock_exit: NaClXMutexUnlock(&nap->exception_mu); no_lock_exit: return rv; }
int32_t NaClSysStat(struct NaClAppThread *natp, const char *pathname, struct nacl_abi_stat *buf) { struct NaClApp *nap = natp->nap; int32_t retval = -NACL_ABI_EINVAL; char path[NACL_CONFIG_PATH_MAX]; nacl_host_stat_t stbuf; NaClLog(3, ("Entered NaClSysStat(0x%08"NACL_PRIxPTR", 0x%08"NACL_PRIxPTR"," " 0x%08"NACL_PRIxPTR")\n"), (uintptr_t) natp, (uintptr_t) pathname, (uintptr_t) buf); retval = CopyPathFromUser(nap, path, sizeof path, (uintptr_t) pathname); if (0 != retval) goto cleanup; retval = NaClStatAclCheck(nap, path); if (0 != retval) goto cleanup; /* * Perform a host stat. */ retval = NaClHostDescStat(path, &stbuf); if (0 == retval) { struct nacl_abi_stat abi_stbuf; retval = NaClAbiStatHostDescStatXlateCtor(&abi_stbuf, &stbuf); if (!NaClCopyOutToUser(nap, (uintptr_t) buf, &abi_stbuf, sizeof abi_stbuf)) { retval = -NACL_ABI_EFAULT; } } cleanup: return retval; }
int32_t NaClSysImcSocketPair(struct NaClAppThread *natp, uint32_t descs_out) { struct NaClApp *nap = natp->nap; int32_t usr_pair[2]; struct NaClDesc *pair[2]; int32_t retval; NaClLog(3, ("Entered NaClSysImcSocketPair(0x%08"NACL_PRIxPTR " 0x%08"NACL_PRIx32")\n"), (uintptr_t) natp, descs_out); /* This syscall is not used in Chromium so is disabled by default. */ if (!NaClAclBypassChecks) { return -NACL_ABI_EACCES; } retval = NaClCommonDescSocketPair(pair); if (0 != retval) { goto cleanup; } usr_pair[0] = NaClAppSetDescAvail(nap, pair[0]); usr_pair[1] = NaClAppSetDescAvail(nap, pair[1]); if (!NaClCopyOutToUser(nap, (uintptr_t) descs_out, usr_pair, sizeof usr_pair)) { NaClAppSetDesc(nap, usr_pair[0], NULL); NaClAppSetDesc(nap, usr_pair[1], NULL); retval = -NACL_ABI_EFAULT; goto cleanup; } retval = 0; cleanup: return retval; }
int32_t NaClSysImcRecvmsg(struct NaClAppThread *natp, int d, uint32_t nanimhp, int flags) { struct NaClApp *nap = natp->nap; int32_t retval = -NACL_ABI_EINVAL; ssize_t ssize_retval; uintptr_t sysaddr; size_t i; struct NaClDesc *ndp; struct NaClAbiNaClImcMsgHdr kern_nanimh; struct NaClAbiNaClImcMsgIoVec kern_naiov[NACL_ABI_IMC_IOVEC_MAX]; struct NaClImcMsgIoVec kern_iov[NACL_ABI_IMC_IOVEC_MAX]; int32_t usr_desc[NACL_ABI_IMC_USER_DESC_MAX]; struct NaClImcTypedMsgHdr recv_hdr; struct NaClDesc *new_desc[NACL_ABI_IMC_DESC_MAX]; nacl_abi_size_t num_user_desc; struct NaClDesc *invalid_desc = NULL; NaClLog(3, ("Entered NaClSysImcRecvMsg(0x%08"NACL_PRIxPTR", %d," " 0x%08"NACL_PRIx32")\n"), (uintptr_t) natp, d, nanimhp); /* * First, we validate user-supplied message headers before * allocating a receive buffer. */ if (!NaClCopyInFromUser(nap, &kern_nanimh, nanimhp, sizeof kern_nanimh)) { NaClLog(4, "NaClImcMsgHdr not in user address space\n"); retval = -NACL_ABI_EFAULT; goto cleanup_leave; } /* copy before validating */ if (kern_nanimh.iov_length > NACL_ABI_IMC_IOVEC_MAX) { NaClLog(4, "gather/scatter array too large: %"NACL_PRIdNACL_SIZE"\n", kern_nanimh.iov_length); retval = -NACL_ABI_EINVAL; goto cleanup_leave; } if (kern_nanimh.desc_length > NACL_ABI_IMC_USER_DESC_MAX) { NaClLog(4, "handle vector too long: %"NACL_PRIdNACL_SIZE"\n", kern_nanimh.desc_length); retval = -NACL_ABI_EINVAL; goto cleanup_leave; } if (kern_nanimh.iov_length > 0) { /* * Copy IOV array into kernel space. Validate this snapshot and do * user->kernel address conversions on this snapshot. */ if (!NaClCopyInFromUser(nap, kern_naiov, (uintptr_t) kern_nanimh.iov, (kern_nanimh.iov_length * sizeof kern_naiov[0]))) { NaClLog(4, "gather/scatter array not in user address space\n"); retval = -NACL_ABI_EFAULT; goto cleanup_leave; } /* * Convert every IOV base from user to system address, validate * range of bytes are really in user address space. */ for (i = 0; i < kern_nanimh.iov_length; ++i) { sysaddr = NaClUserToSysAddrRange(nap, (uintptr_t) kern_naiov[i].base, kern_naiov[i].length); if (kNaClBadAddress == sysaddr) { NaClLog(4, "iov number %"NACL_PRIuS" not entirely in user space\n", i); retval = -NACL_ABI_EFAULT; goto cleanup_leave; } kern_iov[i].base = (void *) sysaddr; kern_iov[i].length = kern_naiov[i].length; } } if (kern_nanimh.desc_length > 0) { sysaddr = NaClUserToSysAddrRange(nap, (uintptr_t) kern_nanimh.descv, kern_nanimh.desc_length * sizeof(int32_t)); if (kNaClBadAddress == sysaddr) { retval = -NACL_ABI_EFAULT; goto cleanup_leave; } } ndp = NaClAppGetDesc(nap, d); if (NULL == ndp) { NaClLog(4, "receiving descriptor invalid\n"); retval = -NACL_ABI_EBADF; goto cleanup_leave; } recv_hdr.iov = kern_iov; recv_hdr.iov_length = kern_nanimh.iov_length; recv_hdr.ndescv = new_desc; recv_hdr.ndesc_length = NACL_ARRAY_SIZE(new_desc); memset(new_desc, 0, sizeof new_desc); recv_hdr.flags = 0; /* just to make it obvious; IMC will clear it for us */ /* lock user memory ranges in kern_naiov */ for (i = 0; i < kern_nanimh.iov_length; ++i) { NaClVmIoWillStart(nap, kern_naiov[i].base, kern_naiov[i].base + kern_naiov[i].length - 1); } ssize_retval = NACL_VTBL(NaClDesc, ndp)->RecvMsg(ndp, &recv_hdr, flags); /* unlock user memory ranges in kern_naiov */ for (i = 0; i < kern_nanimh.iov_length; ++i) { NaClVmIoHasEnded(nap, kern_naiov[i].base, kern_naiov[i].base + kern_naiov[i].length - 1); } /* * retval is number of user payload bytes received and excludes the * header bytes. */ NaClLog(3, "NaClSysImcRecvMsg: RecvMsg() returned %"NACL_PRIdS"\n", ssize_retval); if (NaClSSizeIsNegErrno(&ssize_retval)) { /* negative error numbers all have valid 32-bit representations, * so this cast is safe. */ retval = (int32_t) ssize_retval; goto cleanup; } else if (ssize_retval > INT32_MAX || ssize_retval < INT32_MIN) { retval = -NACL_ABI_EOVERFLOW; goto cleanup; } else { /* cast is safe due to range check above */ retval = (int32_t) ssize_retval; } /* * NB: recv_hdr.flags may contain NACL_ABI_MESSAGE_TRUNCATED and/or * NACL_ABI_HANDLES_TRUNCATED. */ kern_nanimh.flags = recv_hdr.flags; /* * Now internalize the NaClHandles as NaClDesc objects. */ num_user_desc = recv_hdr.ndesc_length; if (kern_nanimh.desc_length < num_user_desc) { kern_nanimh.flags |= NACL_ABI_RECVMSG_DESC_TRUNCATED; for (i = kern_nanimh.desc_length; i < num_user_desc; ++i) { NaClDescUnref(new_desc[i]); new_desc[i] = NULL; } num_user_desc = kern_nanimh.desc_length; } invalid_desc = (struct NaClDesc *) NaClDescInvalidMake(); /* prepare to write out to user space the descriptor numbers */ for (i = 0; i < num_user_desc; ++i) { if (invalid_desc == new_desc[i]) { usr_desc[i] = kKnownInvalidDescNumber; NaClDescUnref(new_desc[i]); } else { usr_desc[i] = NaClAppSetDescAvail(nap, new_desc[i]); } new_desc[i] = NULL; } if (0 != num_user_desc && !NaClCopyOutToUser(nap, (uintptr_t) kern_nanimh.descv, usr_desc, num_user_desc * sizeof usr_desc[0])) { NaClLog(LOG_FATAL, ("NaClSysImcRecvMsg: in/out ptr (descv %"NACL_PRIxPTR ") became invalid at copyout?\n"), (uintptr_t) kern_nanimh.descv); } kern_nanimh.desc_length = num_user_desc; if (!NaClCopyOutToUser(nap, nanimhp, &kern_nanimh, sizeof kern_nanimh)) { NaClLog(LOG_FATAL, "NaClSysImcRecvMsg: in/out ptr (iov) became" " invalid at copyout?\n"); } /* copy out updated desc count, flags */ cleanup: if (retval < 0) { for (i = 0; i < NACL_ARRAY_SIZE(new_desc); ++i) { if (NULL != new_desc[i]) { NaClDescUnref(new_desc[i]); new_desc[i] = NULL; } } } NaClDescUnref(ndp); NaClDescSafeUnref(invalid_desc); NaClLog(3, "NaClSysImcRecvMsg: returning %d\n", retval); cleanup_leave: return retval; }