/* * __NaClSrpcImcFillBuf fills buffer from an IMC channel. It returns 1 if * successful, and 0 otherwise. */ NaClSrpcImcBuffer* __NaClSrpcImcFillbuf(NaClSrpcChannel* channel) { NaClSrpcImcBuffer* buffer; ssize_t retval; buffer = &channel->receive_buf; buffer->iovec[0].base = buffer->bytes; buffer->iovec[0].length = sizeof(buffer->bytes); NACL_SRPC_IMC_HEADER_DESC_LENGTH(*buffer) = SRPC_DESC_MAX; #ifdef __native_client__ retval = imc_recvmsg(channel->imc_handle, &buffer->header, 0); #else retval = NaClImcRecvTypedMessage(channel->imc_handle, &buffer->header, 0); #endif buffer->next_desc = 0; if (!NaClSSizeIsNegErrno(&retval)) { channel->receive_buf.next_byte = 0; channel->receive_buf.last_byte = nacl_abi_size_t_saturate(retval); } else { dprintf((SIDE "SRPC: READ: read failed.\n")); return NULL; } return buffer; }
int32_t NaClSysGetdents(struct NaClAppThread *natp, int d, uint32_t dirp, size_t count) { struct NaClApp *nap = natp->nap; int32_t retval = -NACL_ABI_EINVAL; ssize_t getdents_ret; uintptr_t sysaddr; struct NaClDesc *ndp; NaClLog(3, ("Entered NaClSysGetdents(0x%08"NACL_PRIxPTR", " "%d, 0x%08"NACL_PRIx32", " "%"NACL_PRIuS"[0x%"NACL_PRIxS"])\n"), (uintptr_t) natp, d, dirp, count, count); if (!NaClFileAccessEnabled()) { /* * Filesystem access is disabled, so disable the getdents() syscall. * We do this for security hardening, though it should be redundant, * because untrusted code should not be able to open any directory * descriptors (i.e. descriptors with a non-trivial Getdents() * implementation). */ return -NACL_ABI_EACCES; } ndp = NaClAppGetDesc(nap, d); if (NULL == ndp) { retval = -NACL_ABI_EBADF; goto cleanup; } /* * Generic NaClCopyOutToUser is not sufficient, since buffer size * |count| is arbitrary and we wouldn't want to have to allocate * memory in trusted address space to match. */ sysaddr = NaClUserToSysAddrRange(nap, dirp, count); if (kNaClBadAddress == sysaddr) { NaClLog(4, " illegal address for directory data\n"); retval = -NACL_ABI_EFAULT; goto cleanup_unref; } /* * Clamp count to INT32_MAX to avoid the possibility of Getdents returning * a value that is outside the range of an int32. */ if (count > INT32_MAX) { count = INT32_MAX; } /* * Grab addr space lock; getdents should not normally block, though * if the directory is on a networked filesystem this could, and * cause mmap to be slower on Windows. */ NaClXMutexLock(&nap->mu); getdents_ret = (*((struct NaClDescVtbl const *) ndp->base.vtbl)-> Getdents)(ndp, (void *) sysaddr, count); NaClXMutexUnlock(&nap->mu); /* drop addr space lock */ if ((getdents_ret < INT32_MIN && !NaClSSizeIsNegErrno(&getdents_ret)) || INT32_MAX < getdents_ret) { /* This should never happen, because we already clamped the input count */ NaClLog(LOG_FATAL, "Overflow in Getdents: return value is %"NACL_PRIxS, (size_t) getdents_ret); } else { retval = (int32_t) getdents_ret; } if (retval > 0) { NaClLog(4, "getdents returned %d bytes\n", retval); NaClLog(8, "getdents result: %.*s\n", retval, (char *) sysaddr); } else { NaClLog(4, "getdents returned %d\n", retval); } cleanup_unref: NaClDescUnref(ndp); cleanup: return retval; }
ssize_t NaClImcRecvTypedMessage( struct NaClDesc *channel, struct NaClImcTypedMsgHdr *nitmhp, int flags, struct NaClDescQuotaInterface *quota_interface) { int supported_flags; ssize_t retval; char *recv_buf; size_t user_bytes; NaClHandle kern_handle[NACL_ABI_IMC_DESC_MAX]; struct NaClIOVec recv_iov; struct NaClMessageHeader recv_hdr; ssize_t total_recv_bytes; struct NaClInternalHeader intern_hdr; size_t recv_user_bytes_avail; size_t tmp; char *user_data; size_t iov_copy_size; struct NaClDescXferState xfer; struct NaClDesc *new_desc[NACL_ABI_IMC_DESC_MAX]; int xfer_status; size_t i; size_t num_user_desc; NaClLog(4, "Entered NaClImcRecvTypedMsg(0x%08"NACL_PRIxPTR", " "0x%08"NACL_PRIxPTR", %d)\n", (uintptr_t) channel, (uintptr_t) nitmhp, flags); supported_flags = NACL_ABI_IMC_NONBLOCK; if (0 != (flags & ~supported_flags)) { NaClLog(LOG_WARNING, "WARNING: NaClImcRecvTypedMsg: unknown IMC flag used: 0x%x\n", flags); flags &= supported_flags; } if (nitmhp->iov_length > NACL_ABI_IMC_IOVEC_MAX) { NaClLog(4, "gather/scatter array too large\n"); return -NACL_ABI_EINVAL; } if (nitmhp->ndesc_length > NACL_ABI_IMC_USER_DESC_MAX) { NaClLog(4, "handle vector too long\n"); return -NACL_ABI_EINVAL; } user_bytes = 0; for (i = 0; i < nitmhp->iov_length; ++i) { if (user_bytes > SIZE_T_MAX - nitmhp->iov[i].length) { NaClLog(4, "integer overflow in iov length summation\n"); return -NACL_ABI_EINVAL; } user_bytes += nitmhp->iov[i].length; } /* * if user_bytes > NACL_ABI_IMC_USER_BYTES_MAX, * we will just never fill up all the buffer space. */ user_bytes = min_size(user_bytes, NACL_ABI_IMC_USER_BYTES_MAX); /* * user_bytes = \min(\sum_{i=0}{nitmhp->iov_length-1} nitmhp->iov[i].length, * NACL_ABI_IMC_USER_BYTES_MAX) */ recv_buf = NULL; memset(new_desc, 0, sizeof new_desc); /* * from here on, set retval and jump to cleanup code. */ recv_buf = malloc(NACL_ABI_IMC_BYTES_MAX); if (NULL == recv_buf) { NaClLog(4, "no memory for receive buffer\n"); retval = -NACL_ABI_ENOMEM; goto cleanup; } recv_iov.base = (void *) recv_buf; recv_iov.length = NACL_ABI_IMC_BYTES_MAX; recv_hdr.iov = &recv_iov; recv_hdr.iov_length = 1; for (i = 0; i < NACL_ARRAY_SIZE(kern_handle); ++i) { kern_handle[i] = NACL_INVALID_HANDLE; } if (NACL_DESC_IMC_SOCKET == ((struct NaClDescVtbl const *) channel->base.vtbl)->typeTag) { /* * Channel can transfer access rights. */ recv_hdr.handles = kern_handle; recv_hdr.handle_count = NACL_ARRAY_SIZE(kern_handle); NaClLog(4, "Connected socket, may transfer descriptors\n"); } else { /* * Channel cannot transfer access rights. The syscall would fail * if recv_iov.length is non-zero. */ recv_hdr.handles = (NaClHandle *) NULL; recv_hdr.handle_count = 0; NaClLog(4, "Transferable Data Only socket\n"); } recv_hdr.flags = 0; /* just to make it obvious; IMC will clear it for us */ total_recv_bytes = (*((struct NaClDescVtbl const *) channel->base.vtbl)-> LowLevelRecvMsg)(channel, &recv_hdr, flags); if (NaClSSizeIsNegErrno(&total_recv_bytes)) { NaClLog(1, "LowLevelRecvMsg failed, returned %"NACL_PRIdS"\n", total_recv_bytes); retval = total_recv_bytes; goto cleanup; } /* total_recv_bytes >= 0 */ /* * NB: recv_hdr.flags may already contain NACL_ABI_MESSAGE_TRUNCATED * and/or NACL_ABI_HANDLES_TRUNCATED. * * First, parse the NaClInternalHeader and any subsequent fields to * extract and internalize the NaClDesc objects from the array of * NaClHandle values. * * Copy out to user buffer. Possibly additional truncation may occur. * * Since total_recv_bytes >= 0, the cast to size_t is value preserving. */ if ((size_t) total_recv_bytes < sizeof intern_hdr) { NaClLog(4, ("only received %"NACL_PRIdS" (0x%"NACL_PRIxS") bytes," " but internal header is %"NACL_PRIdS" (0x%"NACL_PRIxS ") bytes\n"), total_recv_bytes, total_recv_bytes, sizeof intern_hdr, sizeof intern_hdr); retval = -NACL_ABI_EIO; goto cleanup; } memcpy(&intern_hdr, recv_buf, sizeof intern_hdr); /* * Future code should handle old versions in a backward compatible way. */ if (NACL_HANDLE_TRANSFER_PROTOCOL != intern_hdr.h.xfer_protocol_version) { NaClLog(4, ("protocol version mismatch:" " got %x, but can only handle %x\n"), intern_hdr.h.xfer_protocol_version, NACL_HANDLE_TRANSFER_PROTOCOL); /* * The returned value should be a special version mismatch error * code that, along with the recv_buf, permit retrying with later * decoders. */ retval = -NACL_ABI_EIO; goto cleanup; } if ((size_t) total_recv_bytes < (intern_hdr.h.descriptor_data_bytes + sizeof intern_hdr)) { NaClLog(4, ("internal header (size %"NACL_PRIdS" (0x%"NACL_PRIxS")) " "says there are " "%d (0x%x) NRD xfer descriptor bytes, " "but we received %"NACL_PRIdS" (0x%"NACL_PRIxS") bytes\n"), sizeof intern_hdr, sizeof intern_hdr, intern_hdr.h.descriptor_data_bytes, intern_hdr.h.descriptor_data_bytes, total_recv_bytes, total_recv_bytes); retval = -NACL_ABI_EIO; goto cleanup; } recv_user_bytes_avail = (total_recv_bytes - intern_hdr.h.descriptor_data_bytes - sizeof intern_hdr); /* * NaCl app asked for user_bytes, and we have recv_user_bytes_avail. * Set recv_user_bytes_avail to the min of these two values, as well * as inform the caller if data truncation occurred. */ if (user_bytes < recv_user_bytes_avail) { recv_hdr.flags |= NACL_ABI_RECVMSG_DATA_TRUNCATED; } recv_user_bytes_avail = min_size(recv_user_bytes_avail, user_bytes); retval = recv_user_bytes_avail; /* default from hence forth */ /* * Let UserDataSize := recv_user_bytes_avail. (bind to current value) */ user_data = recv_buf + sizeof intern_hdr + intern_hdr.h.descriptor_data_bytes; /* * Let StartUserData := user_data */ /* * Precondition: user_data in [StartUserData, StartUserData + UserDataSize]. * * Invariant: * user_data + recv_user_bytes_avail == StartUserData + UserDataSize */ for (i = 0; i < nitmhp->iov_length && 0 < recv_user_bytes_avail; ++i) { iov_copy_size = min_size(nitmhp->iov[i].length, recv_user_bytes_avail); memcpy(nitmhp->iov[i].base, user_data, iov_copy_size); user_data += iov_copy_size; /* * subtraction could not underflow due to how recv_user_bytes_avail was * computed; however, we are paranoid, in case the code changes. */ tmp = recv_user_bytes_avail - iov_copy_size; if (tmp > recv_user_bytes_avail) { NaClLog(LOG_FATAL, "NaClImcRecvTypedMessage: impossible underflow occurred"); } recv_user_bytes_avail = tmp; } /* * postcondition: recv_user_bytes_avail == 0. * * NB: 0 < recv_user_bytes_avail \rightarrow i < nitmhp->iov_length * must hold, due to how user_bytes is computed. We leave the * unnecessary test in the loop condition to avoid future code * changes from causing problems as defensive programming. */ /* * Now extract/internalize the NaClHandles as NaClDesc objects. * Note that we will extract beyond nitmhp->desc_length, since we * must still destroy the ones that are dropped. */ xfer.next_byte = recv_buf + sizeof intern_hdr; xfer.byte_buffer_end = xfer.next_byte + intern_hdr.h.descriptor_data_bytes; xfer.next_handle = kern_handle; xfer.handle_buffer_end = kern_handle + recv_hdr.handle_count; i = 0; while (xfer.next_byte < xfer.byte_buffer_end) { struct NaClDesc *out; xfer_status = NaClDescInternalizeFromXferBuffer(&out, &xfer, quota_interface); NaClLog(4, "NaClDescInternalizeFromXferBuffer: returned %d\n", xfer_status); if (0 == xfer_status) { /* end of descriptors reached */ break; } if (i >= NACL_ARRAY_SIZE(new_desc)) { NaClLog(LOG_FATAL, ("NaClImcRecvTypedMsg: trusted peer tried to send too many" " descriptors!\n")); } if (1 != xfer_status) { /* xfer_status < 0, out did not receive output */ retval = -NACL_ABI_EIO; goto cleanup; } new_desc[i] = out; out = NULL; ++i; } num_user_desc = i; /* actual number of descriptors received */ if (nitmhp->ndesc_length < num_user_desc) { nitmhp->flags |= NACL_ABI_RECVMSG_DESC_TRUNCATED; num_user_desc = nitmhp->ndesc_length; } /* transfer ownership to nitmhp->ndescv; some may be left behind */ for (i = 0; i < num_user_desc; ++i) { nitmhp->ndescv[i] = new_desc[i]; new_desc[i] = NULL; } /* cast is safe because we clamped num_user_desc earlier to * be no greater than the original value of nithmp->ndesc_length. */ nitmhp->ndesc_length = (nacl_abi_size_t)num_user_desc; /* retval is number of bytes received */ cleanup: free(recv_buf); /* * Note that we must exercise discipline when constructing NaClDesc * objects from NaClHandles -- the NaClHandle values *must* be set * to NACL_INVALID_HANDLE after the construction of the NaClDesc * where ownership of the NaClHandle is transferred into the NaCDesc * object. Otherwise, between new_desc and kern_handle cleanup code, * a NaClHandle might be closed twice. */ for (i = 0; i < NACL_ARRAY_SIZE(new_desc); ++i) { if (NULL != new_desc[i]) { NaClDescUnref(new_desc[i]); new_desc[i] = NULL; } } for (i = 0; i < NACL_ARRAY_SIZE(kern_handle); ++i) { if (NACL_INVALID_HANDLE != kern_handle[i]) { (void) NaClClose(kern_handle[i]); } } NaClLog(3, "NaClImcRecvTypedMsg: returning %"NACL_PRIdS"\n", retval); return retval; }
ssize_t NaClImcSendTypedMessage(struct NaClDesc *channel, const struct NaClImcTypedMsgHdr *nitmhp, int flags) { int supported_flags; ssize_t retval = -NACL_ABI_EINVAL; struct NaClMessageHeader kern_msg_hdr; /* xlated interface */ size_t i; /* * BEWARE: NaClImcMsgIoVec has the same layout as NaClIOVec, so * there will be type punning below to avoid copying from a struct * NaClImcMsgIoVec array to a struct NaClIOVec array in order to * call the IMC library code using kern_msg_hdr. */ struct NaClImcMsgIoVec kern_iov[NACL_ABI_IMC_IOVEC_MAX + 1]; struct NaClDesc **kern_desc; NaClHandle kern_handle[NACL_ABI_IMC_DESC_MAX]; size_t user_bytes; size_t sys_bytes; size_t sys_handles; size_t desc_bytes; size_t desc_handles; struct NaClInternalHeader *hdr; char *hdr_buf; struct NaClDescXferState xfer_state; static struct NaClInternalHeader const kNoHandles = { { NACL_HANDLE_TRANSFER_PROTOCOL, 0, }, /* and implicit zeros for pad bytes */ }; NaClLog(3, ("Entered" " NaClImcSendTypedMessage(0x%08"NACL_PRIxPTR", " "0x%08"NACL_PRIxPTR", 0x%x)\n"), (uintptr_t) channel, (uintptr_t) nitmhp, flags); /* * What flags do we know now? If a program was compiled using a * newer ABI than what this implementation knows, the extra flag * bits are ignored but will generate a warning. */ supported_flags = NACL_ABI_IMC_NONBLOCK; if (0 != (flags & ~supported_flags)) { NaClLog(LOG_WARNING, "WARNING: NaClImcSendTypedMessage: unknown IMC flag used: 0x%x\n", flags); flags &= supported_flags; } /* * use (ahem) RTTI -- or a virtual function that's been partially * evaluated/memoized -- to short circuit the error check, so that * cleanups are easier (rather than letting it fail at the * ExternalizeSize virtual function call). */ if (0 != nitmhp->ndesc_length && NACL_DESC_IMC_SOCKET != NACL_VTBL(NaClDesc, channel)->typeTag) { NaClLog(4, "not an IMC socket and trying to send descriptors!\n"); return -NACL_ABI_EINVAL; } if (nitmhp->iov_length > NACL_ABI_IMC_IOVEC_MAX) { NaClLog(4, "gather/scatter array too large\n"); return -NACL_ABI_EINVAL; } if (nitmhp->ndesc_length > NACL_ABI_IMC_USER_DESC_MAX) { NaClLog(4, "handle vector too long\n"); return -NACL_ABI_EINVAL; } memcpy(kern_iov + 1, (void *) nitmhp->iov, nitmhp->iov_length * sizeof *nitmhp->iov); /* * kern_iov[0] is the message header that tells the recipient where * the user data and capabilities data (e.g., NaClDescConnCap) * boundary is, as well as types of objects in the handles vector of * NaClHandle objects, which must be internalized. * * NB: we assume that communication is secure, and no external * entity can listen in or will inject bogus information. This is a * strong assumption, since falsification can cause loss of * type-safety. */ user_bytes = 0; for (i = 0; i < nitmhp->iov_length; ++i) { if (user_bytes > SIZE_T_MAX - kern_iov[i+1].length) { return -NACL_ABI_EINVAL; } user_bytes += kern_iov[i+1].length; } if (user_bytes > NACL_ABI_IMC_USER_BYTES_MAX) { return -NACL_ABI_EINVAL; } kern_desc = nitmhp->ndescv; hdr_buf = NULL; /* * NOTE: type punning w/ NaClImcMsgIoVec and NaClIOVec. * This breaks ansi type aliasing rules and hence we may use * soemthing like '-fno-strict-aliasing' */ kern_msg_hdr.iov = (struct NaClIOVec *) kern_iov; kern_msg_hdr.iov_length = nitmhp->iov_length + 1; /* header */ if (0 == nitmhp->ndesc_length) { kern_msg_hdr.handles = NULL; kern_msg_hdr.handle_count = 0; kern_iov[0].base = (void *) &kNoHandles; kern_iov[0].length = sizeof kNoHandles; } else { /* * \forall i \in [0, nitmhp->desc_length): kern_desc[i] != NULL. */ sys_bytes = 0; sys_handles = 0; /* nitmhp->desc_length <= NACL_ABI_IMC_USER_DESC_MAX */ for (i = 0; i < nitmhp->ndesc_length; ++i) { desc_bytes = 0; desc_handles = 0; retval = (*((struct NaClDescVtbl const *) kern_desc[i]->base.vtbl)-> ExternalizeSize)(kern_desc[i], &desc_bytes, &desc_handles); if (retval < 0) { NaClLog(1, ("NaClImcSendTypedMessage: ExternalizeSize" " returned %"NACL_PRIdS"\n"), retval); goto cleanup; } /* * No integer overflow should be possible given the max-handles * limits and actual resource use per handle involved. */ if (desc_bytes > NACL_ABI_SIZE_T_MAX - 1 || (desc_bytes + 1) > NACL_ABI_SIZE_T_MAX - sys_bytes || desc_handles > NACL_ABI_SIZE_T_MAX - sys_handles) { retval = -NACL_ABI_EOVERFLOW; goto cleanup; } sys_bytes += (1 + desc_bytes); sys_handles += desc_handles; } if (sys_handles > NACL_ABI_IMC_DESC_MAX) { NaClLog(LOG_FATAL, ("User had %"NACL_PRIdNACL_SIZE" descriptors," " which expanded into %"NACL_PRIdS "handles, more than" " the max of %d.\n"), nitmhp->ndesc_length, sys_handles, NACL_ABI_IMC_DESC_MAX); } /* a byte for NACL_DESC_TYPE_END_TAG, then rounded up to 0 mod 16 */ sys_bytes = (sys_bytes + 1 + 0xf) & ~0xf; /* bsy notes that sys_bytes should never exceed NACL_ABI_IMC_DESC_MAX * times the maximum protocol transfer size, which is currently set to * NACL_PATH_MAX, so it would be abnormal for this check to fail. * Including it anyway just in case something changes. */ if (sys_bytes > NACL_ABI_SIZE_T_MAX - sizeof *hdr) { NaClLog(LOG_FATAL, "NaClImcSendTypedMessage: " "Buffer size overflow (%"NACL_PRIdS" bytes)", sys_bytes); retval = -NACL_ABI_EOVERFLOW; goto cleanup; } hdr_buf = malloc(sys_bytes + sizeof *hdr); if (NULL == hdr_buf) { NaClLog(4, "NaClImcSendTypedMessage: out of memory for iov"); retval = -NACL_ABI_ENOMEM; goto cleanup; } kern_iov[0].base = (void *) hdr_buf; /* Checked above that sys_bytes <= NACL_ABI_SIZE_T_MAX - sizeof *hdr */ kern_iov[0].length = (nacl_abi_size_t)(sys_bytes + sizeof *hdr); hdr = (struct NaClInternalHeader *) hdr_buf; memset(hdr, 0, sizeof(*hdr)); /* Initilize the struct's padding bytes. */ hdr->h.xfer_protocol_version = NACL_HANDLE_TRANSFER_PROTOCOL; if (sys_bytes > UINT32_MAX) { /* * We really want: * sys_bytes > numeric_limits<typeof(hdr->h.descriptor_data_bytes)>:max() * in case the type changes. * * This should never occur in practice, since * NACL_ABI_IMC_DESC_MAX * NACL_PATH_MAX is far smaller than * UINT32_MAX. Furthermore, NACL_ABI_IMC_DESC_MAX * * NACL_PATH_MAX + user_bytes <= NACL_ABI_IMC_DESC_MAX * * NACL_PATH_MAX + NACL_ABI_IMC_USER_BYTES_MAX == max_xfer (so * max_xfer is upper bound on data transferred), and max_xfer <= * INT32_MAX = NACL_ABI_SSIZE_MAX <= SSIZE_T_MAX holds. */ retval = -NACL_ABI_EOVERFLOW; goto cleanup; } hdr->h.descriptor_data_bytes = (uint32_t) sys_bytes; xfer_state.next_byte = (char *) (hdr + 1); xfer_state.byte_buffer_end = xfer_state.next_byte + sys_bytes; xfer_state.next_handle = kern_handle; xfer_state.handle_buffer_end = xfer_state.next_handle + NACL_ABI_IMC_DESC_MAX; for (i = 0; i < nitmhp->ndesc_length; ++i) { retval = NaClDescExternalizeToXferBuffer(&xfer_state, kern_desc[i]); if (0 != retval) { NaClLog(4, ("NaClImcSendTypedMessage: Externalize for" " descriptor %"NACL_PRIdS" returned %"NACL_PRIdS"\n"), i, retval); goto cleanup; } } *xfer_state.next_byte++ = (char) NACL_DESC_TYPE_END_TAG; /* * zero fill the rest of memory to avoid leaking info from * otherwise uninitialized malloc'd memory. */ while (xfer_state.next_byte < xfer_state.byte_buffer_end) { *xfer_state.next_byte++ = '\0'; } kern_msg_hdr.handles = kern_handle; kern_msg_hdr.handle_count = (uint32_t) sys_handles; } NaClLog(4, "Invoking LowLevelSendMsg, flags 0x%x\n", flags); retval = (*((struct NaClDescVtbl const *) channel->base.vtbl)-> LowLevelSendMsg)(channel, &kern_msg_hdr, flags); NaClLog(4, "LowLevelSendMsg returned %"NACL_PRIdS"\n", retval); if (NaClSSizeIsNegErrno(&retval)) { /* * NaClWouldBlock uses TSD (for both the errno-based and * GetLastError()-based implementations), so this is threadsafe. */ if (0 != (flags & NACL_DONT_WAIT) && NaClWouldBlock()) { retval = -NACL_ABI_EAGAIN; } else if (-NACL_ABI_EMSGSIZE == retval) { /* * Allow the above layer to process when imc_sendmsg calls fail due * to the OS not supporting a large enough buffer. */ retval = -NACL_ABI_EMSGSIZE; } else { /* * TODO(bsy): the else case is some mysterious internal error. * should we destroy the channel? Was the failure atomic? Did * it send some partial data? Linux implementation appears * okay. * * We return EIO and let the caller deal with it. */ retval = -NACL_ABI_EIO; } } else if ((unsigned) retval < kern_iov[0].length) { /* * retval >= 0, so cast to unsigned is value preserving. */ retval = -NACL_ABI_ENOBUFS; } else { /* * The return value (number of bytes sent) should not include the * "out of band" additional information added by the service runtime. */ retval -= kern_iov[0].length; } cleanup: free(hdr_buf); NaClLog(4, "NaClImcSendTypedMessage: returning %"NACL_PRIdS"\n", retval); return retval; }
NaClErrorCode NaClElfImageLoadDynamically( struct NaClElfImage *image, struct NaClApp *nap, struct NaClDesc *ndp, struct NaClValidationMetadata *metadata) { ssize_t read_ret; int segnum; for (segnum = 0; segnum < image->ehdr.e_phnum; ++segnum) { const Elf_Phdr *php = &image->phdrs[segnum]; Elf_Addr vaddr = php->p_vaddr & ~(NACL_MAP_PAGESIZE - 1); Elf_Off offset = php->p_offset & ~(NACL_MAP_PAGESIZE - 1); Elf_Off filesz = php->p_offset + php->p_filesz - offset; Elf_Off memsz = php->p_offset + php->p_memsz - offset; int32_t result; /* * By checking if filesz is larger than memsz, we no longer run the risk of * a malicious ELF object overrunning into the trusted address space when * reading data of size "filez" into a buffer of size "memsz". */ if (filesz > memsz) { return LOAD_UNLOADABLE; } /* * We check for PT_LOAD directly rather than using the "loadable" * array because we are not using NaClElfImageValidateProgramHeaders() * to fill out the "loadable" array for this ELF object. This ELF * object does not have to fit such strict constraints (such as * having code at 0x20000), and safety checks are applied by * NaClTextDyncodeCreate() and NaClSysMmapIntern(). */ if (PT_LOAD != php->p_type) { continue; } if (0 != (php->p_flags & PF_X)) { /* Load code segment. */ /* * We make a copy of the code. This is not ideal given that this * code path is used only for loading the IRT, and we could assume * that the contents of the irt.nexe file will not change underneath * us. We should be able to mmap() the IRT's code segment instead of * copying it. * TODO(mseaborn): Reduce the amount of copying here. */ char *code_copy = malloc(filesz); if (NULL == code_copy) { NaClLog(LOG_ERROR, "NaClElfImageLoadDynamically: malloc failed\n"); return LOAD_NO_MEMORY; } read_ret = (*NACL_VTBL(NaClDesc, ndp)-> PRead)(ndp, code_copy, filesz, (nacl_off64_t) offset); if (NaClSSizeIsNegErrno(&read_ret) || (size_t) read_ret != filesz) { free(code_copy); NaClLog(LOG_ERROR, "NaClElfImageLoadDynamically: " "failed to read code segment\n"); return LOAD_READ_ERROR; } if (NULL != metadata) { metadata->code_offset = offset; } result = NaClTextDyncodeCreate(nap, (uint32_t) vaddr, code_copy, (uint32_t) filesz, metadata); free(code_copy); if (0 != result) { NaClLog(LOG_ERROR, "NaClElfImageLoadDynamically: " "failed to load code segment\n"); return LOAD_UNLOADABLE; } } else { /* Load data segment. */ void *paddr = (void *) NaClUserToSys(nap, vaddr); size_t mapping_size = NaClRoundAllocPage(memsz); /* * Note that we do not used NACL_ABI_MAP_FIXED because we do not * want to silently overwrite any existing mappings, such as the * user app's data segment or the stack. We detect overmapping * when mmap chooses not to use the preferred address we supply. * (Ideally mmap would provide a MAP_EXCL option for this * instead.) */ result = NaClSysMmapIntern( nap, (void *) (uintptr_t) vaddr, mapping_size, NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE, NACL_ABI_MAP_ANONYMOUS | NACL_ABI_MAP_PRIVATE, -1, 0); if ((int32_t) vaddr != result) { NaClLog(LOG_ERROR, "NaClElfImageLoadDynamically: " "failed to map data segment\n"); return LOAD_UNLOADABLE; } read_ret = (*NACL_VTBL(NaClDesc, ndp)-> PRead)(ndp, paddr, filesz, (nacl_off64_t) offset); if (NaClSSizeIsNegErrno(&read_ret) || (size_t) read_ret != filesz) { NaClLog(LOG_ERROR, "NaClElfImageLoadDynamically: " "failed to read data segment\n"); return LOAD_READ_ERROR; } /* * Note that we do not need to zero the BSS (the region from * p_filesz to p_memsz) because it should already be zero * filled. This would not be the case if we were mapping the * data segment from the file. */ if (0 == (php->p_flags & PF_W)) { /* Handle read-only data segment. */ int rc = NaClMprotect(paddr, mapping_size, NACL_ABI_PROT_READ); if (0 != rc) { NaClLog(LOG_ERROR, "NaClElfImageLoadDynamically: " "failed to mprotect read-only data segment\n"); return LOAD_MPROTECT_FAIL; } NaClVmmapAddWithOverwrite(&nap->mem_map, vaddr >> NACL_PAGESHIFT, mapping_size >> NACL_PAGESHIFT, NACL_ABI_PROT_READ, NACL_ABI_MAP_PRIVATE, NULL, 0, 0); } } }
NaClErrorCode NaClElfImageLoad(struct NaClElfImage *image, struct NaClDesc *ndp, struct NaClApp *nap) { int segnum; uintptr_t vaddr; uintptr_t paddr; uintptr_t end_vaddr; ssize_t read_ret; int safe_for_mmap; for (segnum = 0; segnum < image->ehdr.e_phnum; ++segnum) { const Elf_Phdr *php = &image->phdrs[segnum]; Elf_Off offset = (Elf_Off) NaClTruncAllocPage(php->p_offset); Elf_Off filesz = php->p_offset + php->p_filesz - offset; /* did we decide that we will load this segment earlier? */ if (!image->loadable[segnum]) { continue; } NaClLog(2, "loading segment %d\n", segnum); if (0 == php->p_filesz) { NaClLog(4, "zero-sized segment. ignoring...\n"); continue; } end_vaddr = php->p_vaddr + php->p_filesz; /* integer overflow? */ if (end_vaddr < php->p_vaddr) { NaClLog(LOG_FATAL, "parameter error should have been detected already\n"); } /* * is the end virtual address within the NaCl application's * address space? if it is, it implies that the start virtual * address is also. */ if (end_vaddr >= ((uintptr_t) 1U << nap->addr_bits)) { NaClLog(LOG_FATAL, "parameter error should have been detected already\n"); } vaddr = NaClTruncAllocPage(php->p_vaddr); paddr = NaClUserToSysAddr(nap, vaddr); CHECK(kNaClBadAddress != paddr); /* * Check NaClDescIsSafeForMmap(ndp) to see if it might be okay to * mmap. */ NaClLog(4, "NaClElfImageLoad: checking descriptor mmap safety\n"); safe_for_mmap = NaClDescIsSafeForMmap(ndp); if (safe_for_mmap) { NaClLog(4, "NaClElfImageLoad: safe-for-mmap\n"); } if (!safe_for_mmap && NACL_FI("ELF_LOAD_BYPASS_DESCRIPTOR_SAFETY_CHECK", 0, 1)) { NaClLog(LOG_WARNING, "WARNING: BYPASSING DESCRIPTOR SAFETY CHECK\n"); safe_for_mmap = 1; } if (safe_for_mmap) { NaClErrorCode map_status; NaClLog(4, "NaClElfImageLoad: safe-for-mmap\n"); map_status = NaClElfFileMapSegment(nap, ndp, php->p_flags, offset, filesz, vaddr, paddr); /* * NB: -Werror=switch-enum forces us to not use a switch. */ if (LOAD_OK == map_status) { /* Segment has been handled -- proceed to next segment */ continue; } else if (LOAD_STATUS_UNKNOWN != map_status) { /* * A real error! Return it so that this can be reported to * the embedding code (via start_module status). */ return map_status; } /* Fall through: pread-based fallback requested */ } NaClLog(4, "PReading %"NACL_PRIdElf_Xword" (0x%"NACL_PRIxElf_Xword") bytes to" " address 0x%"NACL_PRIxPTR", position %" NACL_PRIdElf_Off" (0x%"NACL_PRIxElf_Off")\n", filesz, filesz, paddr, offset, offset); /* * Tell valgrind that this memory is accessible and undefined. For more * details see * http://code.google.com/p/nativeclient/wiki/ValgrindMemcheck#Implementation_details */ NACL_MAKE_MEM_UNDEFINED((void *) paddr, filesz); read_ret = (*NACL_VTBL(NaClDesc, ndp)-> PRead)(ndp, (void *) paddr, filesz, (nacl_off64_t) offset); if (NaClSSizeIsNegErrno(&read_ret) || (size_t) read_ret != filesz) { NaClLog(LOG_ERROR, "load failure segment %d", segnum); return LOAD_SEGMENT_BAD_PARAM; } /* region from p_filesz to p_memsz should already be zero filled */ /* Tell Valgrind that we've mapped a segment of nacl_file. */ NaClFileMappingForValgrind(paddr, filesz, offset); } return LOAD_OK; }
/* * Attempt to map into the NaClApp object nap from the NaCl descriptor * ndp an ELF segment of type p_flags that start at file_offset for * segment_size bytes, to memory starting at paddr (system address). * If it is a code segment, make a scratch mapping and check * validation in readonly_text mode -- if it succeeds, we map into the * target address; if it fails, we return failure so that pread-based * loading can proceed. For rodata and data segments, less checking * is needed. In the text and data case, the end of the segment may * not land on a NACL_MAP_PAGESIZE boundary; when this occurs, we will * map in all whole NACL_MAP_PAGESIZE chunks, and pread in the tail * partial chunk. * * Returns: LOAD_OK, LOAD_STATUS_UNKNOWN, other error codes. * * LOAD_OK -- if the segment has been fully handled * LOAD_STATUS_UNKNOWN -- if pread-based fallback is required * other error codes -- if a fatal error occurs, and the caller * should propagate up * * See NaClSysMmapIntern in nacl_syscall_common.c for corresponding * mmap syscall where PROT_EXEC allows shared libraries to be mapped * into dynamic code space. */ static NaClErrorCode NaClElfFileMapSegment(struct NaClApp *nap, struct NaClDesc *ndp, Elf_Word p_flags, Elf_Off file_offset, Elf_Off segment_size, uintptr_t vaddr, uintptr_t paddr) { size_t rounded_filesz; /* 64k rounded */ int mmap_prot = 0; uintptr_t image_sys_addr; NaClValidationStatus validator_status = NaClValidationFailed; struct NaClValidationMetadata metadata; int read_last_page_if_partial_allocation_page = 1; ssize_t read_ret; struct NaClPerfCounter time_mmap_segment; NaClPerfCounterCtor(&time_mmap_segment, "NaClElfFileMapSegment"); rounded_filesz = NaClRoundAllocPage(segment_size); NaClLog(4, "NaClElfFileMapSegment: checking segment flags 0x%x" " to determine map checks\n", p_flags); /* * Is this the text segment? If so, map into scratch memory and * run validation (possibly cached result) with !stubout_mode, * readonly_text. If validator says it's okay, map directly into * target location with NACL_ABI_PROT_READ|_EXEC. If anything * failed, fall back to PRead. NB: the assumption is that there * is only one PT_LOAD with PF_R|PF_X segment; this assumption is * enforced by phdr seen_seg checks above in * NaClElfImageValidateProgramHeaders. * * After this function returns, we will be setting memory protection * in NaClMemoryProtection, so the actual memory protection used is * immaterial. * * For rodata and data/bss, we mmap with NACL_ABI_PROT_READ or * NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE as appropriate, * without doing validation. There is no fallback to PRead, since * we don't validate the contents. */ switch (p_flags) { case PF_R | PF_X: NaClLog(4, "NaClElfFileMapSegment: text segment and" " file is safe for mmap\n"); if (NACL_VTBL(NaClDesc, ndp)->typeTag != NACL_DESC_HOST_IO) { NaClLog(4, "NaClElfFileMapSegment: not supported type, got %d\n", NACL_VTBL(NaClDesc, ndp)->typeTag); return LOAD_STATUS_UNKNOWN; } /* * Unlike the mmap case, we do not re-run validation to * allow patching here; instead, we handle validation * failure by going to the pread_fallback case. In the * future, we should consider doing an in-place mapping and * allowing HLT patch validation, which should be cheaper * since those pages that do not require patching (hopefully * majority) will remain file-backed and not require swap * space, even if we had to fault in every page. */ NaClLog(1, "NaClElfFileMapSegment: mapping for validation\n"); NaClPerfCounterMark(&time_mmap_segment, "PreMap"); NaClPerfCounterIntervalLast(&time_mmap_segment); image_sys_addr = (*NACL_VTBL(NaClDesc, ndp)-> Map)(ndp, NaClDescEffectorTrustedMem(), (void *) NULL, rounded_filesz, NACL_ABI_PROT_READ, NACL_ABI_MAP_PRIVATE, file_offset); NaClPerfCounterMark(&time_mmap_segment, "MapForValidate"); NaClPerfCounterIntervalLast(&time_mmap_segment); if (NaClPtrIsNegErrno(&image_sys_addr)) { NaClLog(LOG_INFO, "NaClElfFileMapSegment: Could not make scratch mapping," " falling back to reading\n"); return LOAD_STATUS_UNKNOWN; } /* ask validator / validation cache */ NaClMetadataFromNaClDescCtor(&metadata, ndp); CHECK(segment_size == nap->static_text_end - NACL_TRAMPOLINE_END); validator_status = NACL_FI_VAL( "ELF_LOAD_FORCE_VALIDATION_STATUS", enum NaClValidationStatus, (*nap->validator-> Validate)(vaddr, (uint8_t *) image_sys_addr, segment_size, /* actual size */ 0, /* stubout_mode: no */ 1, /* readonly_text: yes */ nap->cpu_features, &metadata, nap->validation_cache)); NaClPerfCounterMark(&time_mmap_segment, "ValidateMapped"); NaClPerfCounterIntervalLast(&time_mmap_segment); NaClLog(3, "NaClElfFileMapSegment: validator_status %d\n", validator_status); NaClMetadataDtor(&metadata); /* * Remove scratch mapping, then map directly into untrusted * address space or pread. */ NaClDescUnmapUnsafe(ndp, (void *) image_sys_addr, rounded_filesz); NACL_MAKE_MEM_UNDEFINED((void *) paddr, rounded_filesz); if (NaClValidationSucceeded != validator_status) { NaClLog(3, ("NaClElfFileMapSegment: readonly_text validation for mmap" " failed. Will retry validation allowing HALT stubbing out" " of unsupported instruction extensions.\n")); return LOAD_STATUS_UNKNOWN; } NaClLog(1, "NaClElfFileMapSegment: mapping into code space\n"); /* * Windows appears to not allow RWX mappings. This interferes * with HALT_SLED and having to HALT pad the last page. We * allow partial code pages, so * read_last_page_if_partial_allocation_page will ensure that * the last page is writable, so we will be able to write HALT * instructions as needed. */ mmap_prot = NACL_ABI_PROT_READ | NACL_ABI_PROT_EXEC; /* * NB: the log string is used by tests/mmap_main_nexe/nacl.scons * and must be logged at a level that is less than or equal to * the requested verbosity level there. */ NaClLog(1, "NaClElfFileMapSegment: EXERCISING MMAP LOAD PATH\n"); nap->main_exe_prevalidated = 1; break; case PF_R | PF_W: /* read-write (initialized data) */ mmap_prot = NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE; /* * NB: the partial page processing will result in zeros * following the initialized data, so that the BSS will be zero. * On a typical system, this page is mapped in and the BSS * region is memset to zero, which means that this partial page * is faulted in. Rather than saving a syscall (pread) and * faulting it in, we just use the same code path as for code, * which is (slightly) simpler. */ break; case PF_R: /* read-only */ mmap_prot = NACL_ABI_PROT_READ; /* * For rodata, we allow mapping in "garbage" past a partial * page; this potentially eliminates a disk I/O operation * (if data section has no partial page), possibly delaying * disk spin-up if the code was in the validation cache. * And it saves another 64kB of swap. */ read_last_page_if_partial_allocation_page = 0; break; default: NaClLog(LOG_FATAL, "NaClElfFileMapSegment: unexpected p_flags %d\n", p_flags); } if (rounded_filesz != segment_size && read_last_page_if_partial_allocation_page) { uintptr_t tail_offset = rounded_filesz - NACL_MAP_PAGESIZE; size_t tail_size = segment_size - tail_offset; NaClLog(4, "NaClElfFileMapSegment: pread tail\n"); read_ret = (*NACL_VTBL(NaClDesc, ndp)-> PRead)(ndp, (void *) (paddr + tail_offset), tail_size, (nacl_off64_t) (file_offset + tail_offset)); NaClPerfCounterMark(&time_mmap_segment, "PRead tail"); NaClPerfCounterIntervalLast(&time_mmap_segment); if (NaClSSizeIsNegErrno(&read_ret) || (size_t) read_ret != tail_size) { NaClLog(LOG_ERROR, "NaClElfFileMapSegment: pread load of page tail failed\n"); return LOAD_SEGMENT_BAD_PARAM; } rounded_filesz -= NACL_MAP_PAGESIZE; } /* mmap in */ if (rounded_filesz == 0) { NaClLog(4, "NaClElfFileMapSegment: no pages to map, probably because" " the segment was a partial page, so it was processed by" " reading.\n"); } else { NaClLog(4, "NaClElfFileMapSegment: mapping %"NACL_PRIuS" (0x%" NACL_PRIxS") bytes to" " address 0x%"NACL_PRIxPTR", position %" NACL_PRIdElf_Off" (0x%"NACL_PRIxElf_Off")\n", rounded_filesz, rounded_filesz, paddr, file_offset, file_offset); image_sys_addr = (*NACL_VTBL(NaClDesc, ndp)-> Map)(ndp, nap->effp, (void *) paddr, rounded_filesz, mmap_prot, NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_FIXED, file_offset); NaClPerfCounterMark(&time_mmap_segment, "MapFinal"); NaClPerfCounterIntervalLast(&time_mmap_segment); if (image_sys_addr != paddr) { NaClLog(LOG_FATAL, ("NaClElfFileMapSegment: map to 0x%"NACL_PRIxPTR" (prot %x) " "failed: got 0x%"NACL_PRIxPTR"\n"), paddr, mmap_prot, image_sys_addr); } /* Tell Valgrind that we've mapped a segment of nacl_file. */ NaClFileMappingForValgrind(paddr, rounded_filesz, file_offset); } return LOAD_OK; }
struct NaClElfImage *NaClElfImageNew(struct NaClDesc *ndp, NaClErrorCode *err_code) { ssize_t read_ret; struct NaClElfImage *result; struct NaClElfImage image; union { Elf32_Ehdr ehdr32; #if NACL_BUILD_SUBARCH == 64 Elf64_Ehdr ehdr64; #endif } ehdr; int cur_ph; memset(image.loadable, 0, sizeof image.loadable); /* * We read the larger size of an ELFCLASS64 header even if it turns out * we're reading an ELFCLASS32 file. No usable ELFCLASS32 binary could * be so small that it's not larger than Elf64_Ehdr anyway. */ read_ret = (*NACL_VTBL(NaClDesc, ndp)->PRead)(ndp, &ehdr, sizeof ehdr, 0); if (NaClSSizeIsNegErrno(&read_ret) || (size_t) read_ret != sizeof ehdr) { *err_code = LOAD_READ_ERROR; NaClLog(2, "could not load elf headers\n"); return 0; } #if NACL_BUILD_SUBARCH == 64 if (ELFCLASS64 == ehdr.ehdr64.e_ident[EI_CLASS]) { /* * Convert ELFCLASS64 format to ELFCLASS32 format. * The initial four fields are the same in both classes. */ memcpy(image.ehdr.e_ident, ehdr.ehdr64.e_ident, EI_NIDENT); image.ehdr.e_ident[EI_CLASS] = ELFCLASS32; image.ehdr.e_type = ehdr.ehdr64.e_type; image.ehdr.e_machine = ehdr.ehdr64.e_machine; image.ehdr.e_version = ehdr.ehdr64.e_version; if (ehdr.ehdr64.e_entry > 0xffffffffU || ehdr.ehdr64.e_phoff > 0xffffffffU || ehdr.ehdr64.e_shoff > 0xffffffffU) { *err_code = LOAD_EHDR_OVERFLOW; NaClLog(2, "ELFCLASS64 file header fields overflow 32 bits\n"); return 0; } image.ehdr.e_entry = (Elf32_Addr) ehdr.ehdr64.e_entry; image.ehdr.e_phoff = (Elf32_Off) ehdr.ehdr64.e_phoff; image.ehdr.e_shoff = (Elf32_Off) ehdr.ehdr64.e_shoff; image.ehdr.e_flags = ehdr.ehdr64.e_flags; if (ehdr.ehdr64.e_ehsize != sizeof(ehdr.ehdr64)) { *err_code = LOAD_BAD_EHSIZE; NaClLog(2, "ELFCLASS64 file e_ehsize != %d\n", (int) sizeof(ehdr.ehdr64)); return 0; } image.ehdr.e_ehsize = sizeof(image.ehdr); image.ehdr.e_phentsize = sizeof(image.phdrs[0]); image.ehdr.e_phnum = ehdr.ehdr64.e_phnum; image.ehdr.e_shentsize = ehdr.ehdr64.e_shentsize; image.ehdr.e_shnum = ehdr.ehdr64.e_shnum; image.ehdr.e_shstrndx = ehdr.ehdr64.e_shstrndx; } else #endif { image.ehdr = ehdr.ehdr32; } NaClDumpElfHeader(2, &image.ehdr); *err_code = NaClElfImageValidateElfHeader(&image); if (LOAD_OK != *err_code) { return 0; } /* read program headers */ if (image.ehdr.e_phnum > NACL_MAX_PROGRAM_HEADERS) { *err_code = LOAD_TOO_MANY_PROG_HDRS; NaClLog(2, "too many prog headers\n"); return 0; } #if NACL_BUILD_SUBARCH == 64 if (ELFCLASS64 == ehdr.ehdr64.e_ident[EI_CLASS]) { /* * We'll load the 64-bit phdrs and convert them to 32-bit format. */ Elf64_Phdr phdr64[NACL_MAX_PROGRAM_HEADERS]; if (ehdr.ehdr64.e_phentsize != sizeof(Elf64_Phdr)) { *err_code = LOAD_BAD_PHENTSIZE; NaClLog(2, "bad prog headers size\n"); NaClLog(2, " ehdr64.e_phentsize = 0x%"NACL_PRIxElf_Half"\n", ehdr.ehdr64.e_phentsize); NaClLog(2, " sizeof(Elf64_Phdr) = 0x%"NACL_PRIxS"\n", sizeof(Elf64_Phdr)); return 0; } /* * We know the multiplication won't overflow since we rejected * e_phnum values larger than the small constant NACL_MAX_PROGRAM_HEADERS. */ read_ret = (*NACL_VTBL(NaClDesc, ndp)-> PRead)(ndp, &phdr64[0], image.ehdr.e_phnum * sizeof phdr64[0], (nacl_off64_t) image.ehdr.e_phoff); if (NaClSSizeIsNegErrno(&read_ret) || (size_t) read_ret != image.ehdr.e_phnum * sizeof phdr64[0]) { *err_code = LOAD_READ_ERROR; NaClLog(2, "cannot load tp prog headers\n"); return 0; } for (cur_ph = 0; cur_ph < image.ehdr.e_phnum; ++cur_ph) { if (phdr64[cur_ph].p_offset > 0xffffffffU || phdr64[cur_ph].p_vaddr > 0xffffffffU || phdr64[cur_ph].p_paddr > 0xffffffffU || phdr64[cur_ph].p_filesz > 0xffffffffU || phdr64[cur_ph].p_memsz > 0xffffffffU || phdr64[cur_ph].p_align > 0xffffffffU) { *err_code = LOAD_PHDR_OVERFLOW; NaClLog(2, "ELFCLASS64 program header fields overflow 32 bits\n"); return 0; } image.phdrs[cur_ph].p_type = phdr64[cur_ph].p_type; image.phdrs[cur_ph].p_offset = (Elf32_Off) phdr64[cur_ph].p_offset; image.phdrs[cur_ph].p_vaddr = (Elf32_Addr) phdr64[cur_ph].p_vaddr; image.phdrs[cur_ph].p_paddr = (Elf32_Addr) phdr64[cur_ph].p_paddr; image.phdrs[cur_ph].p_filesz = (Elf32_Word) phdr64[cur_ph].p_filesz; image.phdrs[cur_ph].p_memsz = (Elf32_Word) phdr64[cur_ph].p_memsz; image.phdrs[cur_ph].p_flags = phdr64[cur_ph].p_flags; image.phdrs[cur_ph].p_align = (Elf32_Word) phdr64[cur_ph].p_align; } } else #endif { if (image.ehdr.e_phentsize != sizeof image.phdrs[0]) { *err_code = LOAD_BAD_PHENTSIZE; NaClLog(2, "bad prog headers size\n"); NaClLog(2, " image.ehdr.e_phentsize = 0x%"NACL_PRIxElf_Half"\n", image.ehdr.e_phentsize); NaClLog(2, " sizeof image.phdrs[0] = 0x%"NACL_PRIxS"\n", sizeof image.phdrs[0]); return 0; } read_ret = (*NACL_VTBL(NaClDesc, ndp)-> PRead)(ndp, &image.phdrs[0], image.ehdr.e_phnum * sizeof image.phdrs[0], (nacl_off64_t) image.ehdr.e_phoff); if (NaClSSizeIsNegErrno(&read_ret) || (size_t) read_ret != image.ehdr.e_phnum * sizeof image.phdrs[0]) { *err_code = LOAD_READ_ERROR; NaClLog(2, "cannot load tp prog headers\n"); return 0; } } NaClLog(2, "=================================================\n"); NaClLog(2, "Elf Program headers\n"); NaClLog(2, "==================================================\n"); for (cur_ph = 0; cur_ph < image.ehdr.e_phnum; ++cur_ph) { NaClDumpElfProgramHeader(2, &image.phdrs[cur_ph]); } /* we delay allocating till the end to avoid cleanup code */ result = malloc(sizeof image); if (result == 0) { *err_code = LOAD_NO_MEMORY; NaClLog(LOG_FATAL, "no enough memory for image meta data\n"); return 0; } memcpy(result, &image, sizeof image); *err_code = LOAD_OK; return result; }
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; }
/* * This function converts addresses from user addresses to system * addresses, copying into kernel space as needed to avoid TOCvTOU * races, then invokes the descriptor's SendMsg() method. */ int32_t NaClSysImcSendmsg(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; /* copy of user-space data for validation */ 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]; /* kernel-side representatin of descriptors */ struct NaClDesc *kern_desc[NACL_ABI_IMC_USER_DESC_MAX]; struct NaClImcTypedMsgHdr kern_msg_hdr; struct NaClDesc *ndp; size_t i; NaClLog(3, ("Entered NaClSysImcSendmsg(0x%08"NACL_PRIxPTR", %d," " 0x%08"NACL_PRIx32", 0x%x)\n"), (uintptr_t) natp, d, nanimhp, flags); 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 contents */ /* * Some of these checks duplicate checks that will be done in the * nrd xfer library, but it is better to check before doing the * address translation of memory/descriptor vectors if those vectors * might be too long. Plus, we need to copy and validate vectors * for TOCvTOU race protection, and we must prevent overflows. The * nrd xfer library's checks should never fire when called from the * service runtime, but the nrd xfer library might be called from * other code. */ if (kern_nanimh.iov_length > NACL_ABI_IMC_IOVEC_MAX) { NaClLog(4, "gather/scatter array too large\n"); retval = -NACL_ABI_EINVAL; goto cleanup_leave; } if (kern_nanimh.desc_length > NACL_ABI_IMC_USER_DESC_MAX) { NaClLog(4, "handle vector too long\n"); retval = -NACL_ABI_EINVAL; goto cleanup_leave; } if (kern_nanimh.iov_length > 0) { 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; } 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) { retval = -NACL_ABI_EFAULT; goto cleanup_leave; } kern_iov[i].base = (void *) sysaddr; kern_iov[i].length = kern_naiov[i].length; } } ndp = NaClAppGetDesc(nap, d); if (NULL == ndp) { retval = -NACL_ABI_EBADF; goto cleanup_leave; } /* * make things easier for cleaup exit processing */ memset(kern_desc, 0, sizeof kern_desc); retval = -NACL_ABI_EINVAL; kern_msg_hdr.iov = kern_iov; kern_msg_hdr.iov_length = kern_nanimh.iov_length; if (0 == kern_nanimh.desc_length) { kern_msg_hdr.ndescv = 0; kern_msg_hdr.ndesc_length = 0; } else { if (!NaClCopyInFromUser(nap, usr_desc, kern_nanimh.descv, kern_nanimh.desc_length * sizeof usr_desc[0])) { retval = -NACL_ABI_EFAULT; goto cleanup; } for (i = 0; i < kern_nanimh.desc_length; ++i) { if (kKnownInvalidDescNumber == usr_desc[i]) { kern_desc[i] = (struct NaClDesc *) NaClDescInvalidMake(); } else { /* NaCl modules are ILP32, so this works on ILP32 and LP64 systems */ kern_desc[i] = NaClAppGetDesc(nap, usr_desc[i]); } if (NULL == kern_desc[i]) { retval = -NACL_ABI_EBADF; goto cleanup; } } kern_msg_hdr.ndescv = kern_desc; kern_msg_hdr.ndesc_length = kern_nanimh.desc_length; } kern_msg_hdr.flags = kern_nanimh.flags; /* 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)->SendMsg(ndp, &kern_msg_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); } if (NaClSSizeIsNegErrno(&ssize_retval)) { /* * NaClWouldBlock uses TSD (for both the errno-based and * GetLastError()-based implementations), so this is threadsafe. */ if (0 != (flags & NACL_DONT_WAIT) && NaClWouldBlock()) { retval = -NACL_ABI_EAGAIN; } else if (-NACL_ABI_EMSGSIZE == ssize_retval) { /* * Allow the caller to handle the case when imc_sendmsg fails because * the message is too large for the system to send in one piece. */ retval = -NACL_ABI_EMSGSIZE; } else { /* * TODO(bsy): the else case is some mysterious internal error. * Should we destroy the ndp or otherwise mark it as bad? Was * the failure atomic? Did it send some partial data? Linux * implementation appears okay. */ retval = -NACL_ABI_EIO; } } else if (ssize_retval > INT32_MAX || ssize_retval < INT32_MIN) { retval = -NACL_ABI_EOVERFLOW; } else { /* cast is safe due to range checks above */ retval = (int32_t)ssize_retval; } cleanup: for (i = 0; i < kern_nanimh.desc_length; ++i) { if (NULL != kern_desc[i]) { NaClDescUnref(kern_desc[i]); kern_desc[i] = NULL; } } NaClDescUnref(ndp); cleanup_leave: NaClLog(3, "NaClSysImcSendmsg: returning %d\n", retval); return retval; }