/*
 * __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;
}
Exemple #2
0
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;
}
Exemple #5
0
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);
      }
    }
  }
Exemple #6
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;
}
Exemple #7
0
/*
 * 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;
}
Exemple #8
0
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;
}
Exemple #9
0
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;
}
Exemple #10
0
/*
 * 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;
}