Пример #1
0
/*
 * Reads the header of the message to determine whether to call RecvRequest or
 * RecvResponse.
 */
static ssize_t SrpcPeekMessage(struct NaClSrpcMessageChannel* channel,
                               NaClSrpcRpc* rpc) {
  struct NaClImcMsgIoVec iov[1];
  const size_t kMaxIovLen = NACL_ARRAY_SIZE(iov);
  size_t iov_len;
  NaClSrpcMessageHeader header;
  size_t expected_bytes;
  ssize_t retval;

  iov_len = 0;
  expected_bytes = 0;
  AddIovEntry(rpc, kRpcSize, kMaxIovLen, iov, &iov_len, &expected_bytes);
  header.iov = iov;
  header.iov_length = NACL_ARRAY_SIZE(iov);
  header.NACL_SRPC_MESSAGE_HEADER_DESCV = NULL;
  header.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH = 0;
  retval = NaClSrpcMessageChannelPeek(channel, &header);
  /*
   * Check that the peek read the RPC and that the argument lengths are sane.
   */
  if (retval < (ssize_t) expected_bytes) {
    retval = ErrnoFromImcRet(retval);
  }
  else if (rpc->value_len > NACL_SRPC_MAX_ARGS ||
           rpc->template_len > NACL_SRPC_MAX_ARGS) {
    retval = -NACL_ABI_EIO;
  }
  return retval;
}
Пример #2
0
static ssize_t RecvResponse(struct NaClSrpcMessageChannel* channel,
                            NaClSrpcRpc* rpc,
                            NaClSrpcArg** results) {
  NaClSrpcArg* result_copy[NACL_SRPC_MAX_ARGS + 1];
  struct NaClImcMsgIoVec iov[IOV_ENTRY_MAX];
  const size_t kMaxIovLen = NACL_ARRAY_SIZE(iov);
  size_t iov_len = 0;
  NaClSrpcMessageHeader header;
  NaClSrpcImcDescType descs[NACL_SRPC_MAX_ARGS];
  size_t expected_bytes;
  ssize_t retval;
  size_t i;

  if (results == NULL) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "RecvResponse: results should not be NULL\n");
    retval = -NACL_ABI_EINVAL;
    goto done;
  }
  /*
   * SrpcPeekMessage should have been called before this function, and should
   * have populated rpc.  Make sure that rpc points to a sane header.
   */
  if (rpc->is_request ||
      rpc->template_len > 0 ||
      rpc->value_len > NACL_SRPC_MAX_ARGS ||
      rpc->value_len != VectorLen(results)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "RecvResponse: rpc header invalid: is_request %"NACL_PRIu32", "
                "template_len %"NACL_PRIu32", value_len %"NACL_PRIu32"\n",
                rpc->is_request,
                rpc->template_len,
                rpc->value_len);
    return -NACL_ABI_EINVAL;
  }

  /*
   * Having read the header we know how many elements the results vector
   * contains.  The next peek reads the fixed portion of the results vectors,
   * but cannot yet read the variable length portion, because we do not yet
   * know the counts of array types or strings.  Because the results read
   * could conflict with the expected types, we need to read the fixed portion
   * into a copy.
   */
  if (!AllocateArgs(result_copy, rpc->value_len)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "RecvResponse: AllocateArgs failed\n");
    retval = -NACL_ABI_EINVAL;
    goto done;
  }
  iov_len = 0;
  expected_bytes = 0;
  AddIovEntry(rpc, kRpcSize, kMaxIovLen, iov, &iov_len, &expected_bytes);
  for (i = 0; i < rpc->value_len; ++i) {
    AddIovEntry(result_copy[i], kArgSize, kMaxIovLen, iov, &iov_len,
                &expected_bytes);
  }
  header.iov = iov;
  header.iov_length = (nacl_abi_size_t) iov_len;
  header.NACL_SRPC_MESSAGE_HEADER_DESCV = NULL;
  header.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH = 0;
  retval = NaClSrpcMessageChannelPeek(channel, &header);
  if (retval < (ssize_t) expected_bytes) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "RecvResponse: NaClSrpcMessageChannelPeek incomplete: "
                "expected %"NACL_PRIdS", got %"NACL_PRIdS"\n",
                expected_bytes,
                retval);
    retval = ErrnoFromImcRet(retval);
    goto done;
  }

  /*
   * Check that the peeked results vector's types conform to the types passed
   * in and that any nonfixed size arguments are no larger than the counts
   * passed in from the caller.  If the values are acceptable, we copy the
   * actual sizes to the caller's vector.
   */
  if (!CheckMatchAndCopyCounts(rpc->value_len, results, result_copy)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "RecvResponse: CheckMatchAndCopyCounts failed\n");
    retval = -NACL_ABI_EIO;
    goto done;
  }

  /*
   * After peeking the fixed portion of the results vector we are ready to
   * read the nonfixed portion as well.  So the read just adds the IOV entries
   * for the nonfixed portion of results.
   */
  iov_len = 0;
  expected_bytes = 0;
  AddIovEntry(rpc, kRpcSize, kMaxIovLen, iov, &iov_len, &expected_bytes);
  AddFixed(results, rpc->value_len, kMaxIovLen, iov, &iov_len, &expected_bytes);
  if (!AddNonfixedForRead(results, rpc->value_len, kMaxIovLen,
                          0, 1, iov, &iov_len, &expected_bytes)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "RecvResponse: AddNonfixedForRead failed\n");
    retval = -NACL_ABI_EIO;
    goto done;
  }
  header.iov = iov;
  header.iov_length = (nacl_abi_size_t) iov_len;
  header.NACL_SRPC_MESSAGE_HEADER_DESCV = descs;
  header.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH = NACL_ARRAY_SIZE(descs);
  retval = NaClSrpcMessageChannelReceive(channel, &header);
  if (retval < (ssize_t) expected_bytes) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "RecvResponse: NaClSrpcMessageChannelReceive incomplete: "
                "expected %"NACL_PRIdS", got %"NACL_PRIdS"\n",
                expected_bytes,
                retval);
    retval = ErrnoFromImcRet(retval);
    goto done;
  }

  /*
   * The read left any descriptors returned in the descs array.  We need to
   * copy those descriptors to the results vector.
   */
  if (!GetHandles(results, rpc->value_len,
                  descs, header.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "RecvResponse: GetHandles failed\n");
    retval = -NACL_ABI_EIO;
  }

 done:
  FreeArgs(result_copy);
  return retval;
}
Пример #3
0
static ssize_t RecvRequest(struct NaClSrpcMessageChannel* channel,
                           NaClSrpcRpc* rpc,
                           NaClSrpcArg** inputs,
                           NaClSrpcArg** results) {
  struct NaClImcMsgIoVec iov[IOV_ENTRY_MAX];
  const size_t kMaxIovLen = NACL_ARRAY_SIZE(iov);
  size_t iov_len;
  NaClSrpcMessageHeader header;
  NaClSrpcImcDescType descs[NACL_SRPC_MAX_ARGS];
  size_t expected_bytes;
  ssize_t retval;

  /*
   * SrpcPeekMessage should have been called before this function, and should
   * have populated rpc.  Make sure that rpc points to a sane header.
   */
  if (!rpc->is_request ||
      rpc->template_len > NACL_SRPC_MAX_ARGS ||
      rpc->value_len > NACL_SRPC_MAX_ARGS) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "RecvRequest: rpc header invalid: is_request %"NACL_PRIu32", "
                "template_len %"NACL_PRIu32", value_len %"NACL_PRIu32"\n",
                rpc->is_request,
                rpc->template_len,
                rpc->value_len);
    retval = -NACL_ABI_EINVAL;
    goto done;
  }
  /*
   * A request will contain two vectors of NaClSrpcArgs.  Set the index
   * pointers passed in to new argument vectors that will be filled during
   * the next peek.
   */
  if (!AllocateArgs(results, rpc->template_len) ||
      !AllocateArgs(inputs, rpc->value_len)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "RecvRequest: AllocateArgs failed\n");
    retval = -NACL_ABI_EINVAL;
    goto done;
  }

  /*
   * Having read the header we know how many elements each argument vector
   * contains.  The next peek reads the fixed portion of these argument vectors,
   * but cannot yet read the variable length portion, because we do not yet
   * know the counts of array types or strings.
   */
  iov_len = 0;
  expected_bytes = 0;
  AddIovEntry(rpc, kRpcSize, kMaxIovLen, iov, &iov_len, &expected_bytes);
  AddFixed(results, rpc->template_len, kMaxIovLen, iov, &iov_len,
           &expected_bytes);
  AddFixed(inputs, rpc->value_len, kMaxIovLen, iov, &iov_len, &expected_bytes);
  header.iov = iov;
  header.iov_length = (nacl_abi_size_t) iov_len;
  header.NACL_SRPC_MESSAGE_HEADER_DESCV = NULL;
  header.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH = 0;
  retval = NaClSrpcMessageChannelPeek(channel, &header);
  if (retval < (ssize_t) expected_bytes) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "RecvRequest:"
                "NaClSrpcMessageChannelPeek incomplete: expected %"
                NACL_PRIdS", got %"NACL_PRIdS"\n",
                expected_bytes,
                retval);
    retval = ErrnoFromImcRet(retval);
    goto done;
  }

  /*
   * After peeking the fixed portion of the argument vectors we are ready to
   * read the nonfixed portions as well.  So the read just adds the IOV entries
   * for the nonfixed portions of the arguments.
   */
  iov_len = 0;
  expected_bytes = 0;
  AddIovEntry(rpc, kRpcSize, kMaxIovLen, iov, &iov_len, &expected_bytes);
  ClearTemplateStringLengths(results, rpc->template_len);
  AddFixed(results, rpc->template_len, kMaxIovLen, iov, &iov_len,
           &expected_bytes);
  AddFixed(inputs, rpc->value_len, kMaxIovLen, iov, &iov_len, &expected_bytes);
  if (!AddNonfixedForRead(results, rpc->template_len, kMaxIovLen,
                          1, 0, iov, &iov_len, &expected_bytes)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "RecvRequest: AllocateArgs failed for results\n");
    retval = -NACL_ABI_EIO;
    goto done;
  }
  if (!AddNonfixedForRead(inputs, rpc->value_len, kMaxIovLen,
                          1, 1, iov, &iov_len, &expected_bytes)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "RecvRequest: AllocateArgs failed for inputs\n");
    retval = -NACL_ABI_EIO;
    goto done;
  }
  header.iov = iov;
  header.iov_length = (nacl_abi_size_t) iov_len;
  header.NACL_SRPC_MESSAGE_HEADER_DESCV = descs;
  header.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH = NACL_ARRAY_SIZE(descs);
  retval = NaClSrpcMessageChannelReceive(channel, &header);
  if (retval < (ssize_t) expected_bytes) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "RecvRequest:"
                " NaClSrpcMessageChannelReceive incomplete: expected %"
                NACL_PRIdS", got %"NACL_PRIdS"\n",
                expected_bytes,
                retval);
    retval = ErrnoFromImcRet(retval);
    goto done;
  }

  /*
   * The read left any descriptors passed in the descs array.  We need to
   * copy those descriptors to the inputs vector.
   */
  if (!GetHandles(inputs, rpc->value_len,
                  descs, header.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "RecvRequest: GetHandles failed\n");
    retval = -NACL_ABI_EIO;
    goto done;
  }
  /*
   * Success, the caller has taken ownership of the memory we allocated
   * for inputs and results.
   */
  inputs = NULL;
  results = NULL;

 done:
  FreeArgs(inputs);
  FreeArgs(results);
  return retval;
}
/*
 * Sends message over the channel. It returns 1 if successful, or 0 otherwise.
 */
ssize_t NaClSrpcMessageChannelSend(struct NaClSrpcMessageChannel* channel,
                                   const NaClSrpcMessageHeader* header) {
  ssize_t imc_ret;
  struct NaClImcMsgIoVec* iovec = NULL;
  NaClSrpcMessageHeader remaining;
  NaClSrpcMessageHeader frag_hdr;
  LengthHeader total_size;
  LengthHeader fragment_size;
  size_t expected_bytes_sent;
  ssize_t retval = -NACL_ABI_EINVAL;

  iovec = CopyAndAddIovs(header->iov, header->iov_length, 2);
  if (NULL == iovec) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelSend: CopyAndAddIovs failed.\n");
    goto done;
  }
  remaining.iov = iovec;
  remaining.iov_length = header->iov_length + 2;
  remaining.NACL_SRPC_MESSAGE_HEADER_DESCV =
      header->NACL_SRPC_MESSAGE_HEADER_DESCV;
  remaining.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH =
      header->NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH;
  remaining.iov[0].base = &total_size;
  remaining.iov[0].length = sizeof total_size;
  remaining.iov[1].base = &fragment_size;
  remaining.iov[1].length = sizeof fragment_size;
  if (-1 == HeaderTotalBytes(&remaining, 0)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelSend: header size overflow.\n");
    goto done;
  }
  /*
   * Send the first (possibly only) fragment.
   * HeaderTotalBytes returns -1 if the total is greater than
   * NACL_ABI_SSIZE_T_MAX.
   */
  total_size.byte_count = (nacl_abi_size_t) HeaderTotalBytes(&remaining, 2);
  if (-1 == (nacl_abi_ssize_t) total_size.byte_count) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelSend: HeaderTotalBytes failed.\n");
    goto done;
  }
  total_size.desc_count = header->NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH;
  /*
   * Compute the first fragment's message descriptor and fragment descriptor,
   * limiting the bytes and descriptors sent in the first fragment to preset
   * amounts.
   */
  if (!ComputeFragmentSizes(&remaining, FIRST_FRAGMENT, &fragment_size)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelSend:"
                " first ComputeFragmentSize failed.\n");
    goto done;
  }
  NaClSrpcLog(3,
              "NaClSrpcMessageChannelSend: new message, bytes %"
              NACL_PRIdNACL_SIZE", descs %"NACL_PRIdNACL_SIZE".\n",
              total_size.byte_count,
              total_size.desc_count);
  NaClSrpcLog(3,
              "NaClSrpcMessageChannelSend: first fragment, bytes %"
              NACL_PRIdNACL_SIZE", descs %"NACL_PRIdNACL_SIZE".\n",
              fragment_size.byte_count,
              fragment_size.desc_count);
  if (NACL_ABI_SSIZE_T_MAX - kFragmentOverhead[FIRST_FRAGMENT] <
      fragment_size.byte_count) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelSend:"
                " fragment size would cause overflow.\n");
    goto done;
  }
  expected_bytes_sent =
      fragment_size.byte_count + kFragmentOverhead[FIRST_FRAGMENT];
  if (expected_bytes_sent > NaClSrpcMaxImcSendmsgSize) {
    NaClSrpcLog(NACL_SRPC_LOG_FATAL,
                "NaClSrpcMessageChannelSend: expected bytes %"
                NACL_PRIdS" exceed maximum allowed %"NACL_PRIdNACL_SIZE"\n",
                expected_bytes_sent, NaClSrpcMaxImcSendmsgSize);
  }
  if (!BuildFragmentHeader(&remaining, &fragment_size, 2, &frag_hdr)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelSend:"
                " could not build fragment header.\n");
    goto done;
  }
  /*
   * The first message has at least three iov entries: one for the (message)
   * total_size descriptor, one for the fragment_size descriptor, and at
   * least one for the first fragment's bytes and descs.
   */
  imc_ret = ImcSendmsg(channel->desc.raw_desc, &frag_hdr, 0);
  free(frag_hdr.iov);
  if ((size_t) imc_ret != expected_bytes_sent) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelSend: first send failed, %"
                NACL_PRIdS" != %"NACL_PRIdS".\n",
                expected_bytes_sent,
                imc_ret);
    retval = ErrnoFromImcRet(imc_ret);
    goto done;
  }
  ConsumeFragment(&remaining, &fragment_size, 2);
  NaClSrpcLog(3,
              "NaClSrpcMessageChannelSend: first send succeeded.\n");
  /*
   * Each subsequent fragment contains the bytes starting at next_byte and
   * the descs starting at next_desc.
   */
  while (remaining.iov_length > 0 ||
         remaining.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH > 0) {
    NaClSrpcMessageHeader frag_hdr;
    /*
     * Each subsequent message has two iov entries: one for the fragment_size
     * descriptor, and one for the fragment's bytes and descs.
     * We add the fragment length descriptor to the preceding iov entry,
     * which is safe, because we know that ConsumeFragment always consumes
     * at least the fragment length descriptor from last time.
     */
    remaining.iov = remaining.iov - 1;
    remaining.iov_length = remaining.iov_length + 1;
    remaining.iov[0].base = &fragment_size;
    remaining.iov[0].length = sizeof fragment_size;
    if (-1 == HeaderTotalBytes(&remaining, 0)) {
      NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                  "NaClSrpcMessageChannelSend: header size overflow.\n");
      goto done;
    }
    /*
     * The fragment sizes are again limited.
     */
    if (!ComputeFragmentSizes(&remaining, LATER_FRAGMENT, &fragment_size)) {
      NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                  "NaClSrpcMessageChannelSend:"
                  " other ComputeFragmentSize failed.\n");
      retval = -NACL_ABI_EIO;
      goto done;
    }
    NaClSrpcLog(3,
                "NaClSrpcMessageChannelSend: next fragment, bytes %"
                NACL_PRIdNACL_SIZE", descs %"NACL_PRIdNACL_SIZE".\n",
                fragment_size.byte_count,
                fragment_size.desc_count);
    if (!BuildFragmentHeader(&remaining, &fragment_size, 1, &frag_hdr)) {
      NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                  "NaClSrpcMessageChannelSend:"
                  " could not build fragment header.\n");
      retval = -NACL_ABI_EIO;
      goto done;
    }
    /*
     * Send the fragment.
     */
    if (NACL_ABI_SSIZE_T_MAX - kFragmentOverhead[LATER_FRAGMENT] <
        fragment_size.byte_count) {
      NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                  "NaClSrpcMessageChannelSend:"
                  " fragment size would cause overflow.\n");
      goto done;
    }
    expected_bytes_sent =
        fragment_size.byte_count + kFragmentOverhead[LATER_FRAGMENT];
    if (expected_bytes_sent > NaClSrpcMaxImcSendmsgSize) {
      NaClSrpcLog(NACL_SRPC_LOG_FATAL,
                  "NaClSrpcMessageChannelSend: expected bytes %"
                  NACL_PRIdS" exceed maximum allowed %"NACL_PRIdNACL_SIZE"\n",
                  expected_bytes_sent, NaClSrpcMaxImcSendmsgSize);
    }
    imc_ret = ImcSendmsg(channel->desc.raw_desc, &frag_hdr, 0);
    free(frag_hdr.iov);
    if ((size_t) imc_ret != expected_bytes_sent) {
      NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                  "NaClSrpcMessageChannelSend: send error.\n");
      retval = ErrnoFromImcRet(imc_ret);
      goto done;
    }
    ConsumeFragment(&remaining, &fragment_size, 1);
  }
  NaClSrpcLog(3,
              "NaClSrpcMessageChannelSend: complete send, sent %"
              NACL_PRIdNACL_SIZE" bytes and %"NACL_PRIdNACL_SIZE" descs.\n",
              total_size.byte_count,
              total_size.desc_count);
  retval = (ssize_t) total_size.byte_count;

 done:
  free(iovec);
  return retval;
}
/*
 * Receive a message from channel.  On success it returns the number of
 * bytes read; otherwise, returns -1.
 */
ssize_t NaClSrpcMessageChannelReceive(struct NaClSrpcMessageChannel* channel,
                                      NaClSrpcMessageHeader* header) {
  /*
   * TODO(sehr): A large prefix of this function is common with Peek.
   * Find a way to merge them.
   */
  ssize_t imc_ret = -1;
  NaClSrpcMessageHeader header_copy;
  struct NaClImcMsgIoVec* iovec = NULL;
  LengthHeader total_size;
  LengthHeader fragment_size;
  LengthHeader processed_size;
  size_t bytes_received;
  size_t descs_received;
  ssize_t retval = -NACL_ABI_EINVAL;

  NaClSrpcLog(3, "NaClSrpcMessageChannelReceive: waiting for message.\n");
  /*
   * The first fragment consists of two LengthHeaders and a fraction of the
   * bytes (starting at 0) and the fraction of descs (starting at 0).
   */
  iovec = CopyAndAddIovs(header->iov, header->iov_length, 2);
  if (NULL == iovec) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelReceive: CopyAndAddIovs failed.\n");
    goto done;
  }
  header_copy.iov = iovec;
  header_copy.iov_length = header->iov_length + 2;
  header_copy.NACL_SRPC_MESSAGE_HEADER_DESCV =
      header->NACL_SRPC_MESSAGE_HEADER_DESCV;
  /* SRPC_DESC_MAX <= NACL_ABI_SIZE_T_MAX, so the cast is safe. */
  header_copy.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH = (nacl_abi_size_t)
      size_min(SRPC_DESC_MAX, header->NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH);
  header_copy.iov[0].base = &total_size;
  header_copy.iov[0].length = sizeof total_size;
  header_copy.iov[1].base = &fragment_size;
  header_copy.iov[1].length = sizeof fragment_size;
  header_copy.flags = 0;
  if (-1 == HeaderTotalBytes(&header_copy, 0)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelReceive: header size overflow.\n");
    goto done;
  }
  /*
   * The message receive should return at least
   * kFragmentOverhead[FIRST_FRAGMENT] bytes.
   */
  imc_ret = MessageChannelBufferRead(channel, &header_copy, 0);
  if (imc_ret < (ssize_t) kFragmentOverhead[FIRST_FRAGMENT]) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelReceive: read failed (%"NACL_PRIdS").\n",
                imc_ret);
    retval = ErrnoFromImcRet(imc_ret);
    goto done;
  }
  /* Comparison above guarantees no underflow. */
  bytes_received = imc_ret - kFragmentOverhead[FIRST_FRAGMENT];
  descs_received = header_copy.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH;
  if (!MessageLengthsAreSane(
          &total_size,
          &fragment_size,
          (size_t) imc_ret,
          header_copy.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelReceive:"
                " first fragment descriptor check failed.\n");
    retval = -NACL_ABI_EIO;
    goto done;
  }
  NaClSrpcLog(3,
              "NaClSrpcMessageChannelReceive:"
              " new message, bytes %"NACL_PRIdNACL_SIZE
              ", descs %"NACL_PRIdNACL_SIZE".\n",
              total_size.byte_count,
              total_size.desc_count);
  NaClSrpcLog(3,
              "NaClSrpcMessageChannelReceive:"
              " first fragment, bytes %"NACL_PRIdNACL_SIZE
              ", descs %"NACL_PRIdNACL_SIZE".\n",
              fragment_size.byte_count,
              fragment_size.desc_count);
  processed_size = fragment_size;
  ConsumeFragment(&header_copy, &fragment_size, 2);
  /*
   * Get the remaining fragments.
   */
  while (processed_size.byte_count < total_size.byte_count ||
         processed_size.desc_count < total_size.desc_count) {
    /*
     * The non-first fragments consist of a single LengthHeader and a
     * portion of the remaining iov entries and descv entries.  We add the
     * fragment length descriptor to the preceding iov entry, which is safe,
     * because we know that ConsumeFragment always consumes at least the
     * fragment length descriptor from last time.
     */
    header_copy.iov = header_copy.iov - 1;
    header_copy.iov_length = header_copy.iov_length + 1;
    header_copy.iov[0].base = &fragment_size;
    header_copy.iov[0].length = sizeof fragment_size;
    header_copy.NACL_SRPC_MESSAGE_HEADER_DESCV =
        header->NACL_SRPC_MESSAGE_HEADER_DESCV + descs_received;
    header_copy.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH = (nacl_abi_size_t)
        size_min(SRPC_DESC_MAX,
                 (header->NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH -
                  descs_received));
    if (-1 == HeaderTotalBytes(&header_copy, 0)) {
      NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                  "NaClSrpcMessageChannelReceive: header size overflow.\n");
      goto done;
    }
    /*
     * The message receive should return at least
     * kFragmentOverhead[LATER_FRAGMENT] bytes.  This is needed to make sure
     * that we can correctly maintain the index into bytes and descs.
     */
    imc_ret = ImcRecvmsg(channel->desc.raw_desc, &header_copy, 0);
    if (imc_ret < (ssize_t) kFragmentOverhead[LATER_FRAGMENT]) {
      NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                  "NaClSrpcMessageChannelReceive: read failed (%"
                  NACL_PRIdS").\n",
                  imc_ret);
      retval = ErrnoFromImcRet(imc_ret);
      goto done;
    }
    /* Comparison above guarantees no underflow. */
    bytes_received += imc_ret - kFragmentOverhead[LATER_FRAGMENT];
    descs_received += header_copy.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH;
    if (!FragmentLengthIsSane(
            &fragment_size,
            (size_t) imc_ret,
            header_copy.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH)) {
      NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                  "NaClSrpcMessageChannelReceive:"
                  " other fragment descriptor check failed.\n");
      retval = -NACL_ABI_EIO;
      goto done;
    }
    NaClSrpcLog(3,
                "NaClSrpcMessageChannelReceive:"
                " next fragment, bytes %"NACL_PRIdNACL_SIZE
                ", descs %"NACL_PRIdNACL_SIZE".\n",
                fragment_size.byte_count,
                fragment_size.desc_count);
    processed_size.byte_count += fragment_size.byte_count;
    processed_size.desc_count += fragment_size.desc_count;
    ConsumeFragment(&header_copy, &fragment_size, 1);
  }
  NaClSrpcLog(3,
              "NaClSrpcMessageChannelReceive:"
              " succeeded, read %"NACL_PRIdS" bytes and %"
              NACL_PRIdNACL_SIZE" descs.\n",
              bytes_received,
              processed_size.desc_count);
  retval = (ssize_t) bytes_received;
  header->NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH =
      (nacl_abi_size_t) descs_received;
  header->flags = header_copy.flags;

 done:
  free(iovec);
  return retval;
}
/*
 * Peek a message from channel.  Reads the first fragment of the message and
 * leaves it available for future calls to Peek or Receive.
 */
ssize_t NaClSrpcMessageChannelPeek(struct NaClSrpcMessageChannel* channel,
                                   NaClSrpcMessageHeader* header) {
  /*
   * TODO(sehr): Most of this function is common with Receive.
   * Find a way to merge them.
   */
  struct NaClImcMsgIoVec* iovec = NULL;
  NaClSrpcMessageHeader header_copy;
  LengthHeader total_size;
  LengthHeader fragment_size;
  ssize_t imc_ret;
  ssize_t retval = -NACL_ABI_EINVAL;

  /* Append the fragment headers to the iov. */
  iovec = CopyAndAddIovs(header->iov, header->iov_length, 2);
  if (NULL == iovec) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelPeek: CopyAndAddIovs failed.\n");
    return -1;
  }
  header_copy.iov = iovec;
  header_copy.iov_length = header->iov_length + 2;
  header_copy.NACL_SRPC_MESSAGE_HEADER_DESCV =
      header->NACL_SRPC_MESSAGE_HEADER_DESCV;
  /* SRPC_DESC_MAX <= NACL_ABI_SIZE_T_MAX, so the cast is safe. */
  header_copy.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH = (nacl_abi_size_t)
      size_min(SRPC_DESC_MAX, header->NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH);
  header_copy.iov[0].base = &total_size;
  header_copy.iov[0].length = sizeof total_size;
  header_copy.iov[1].base = &fragment_size;
  header_copy.iov[1].length = sizeof fragment_size;
  header_copy.flags = 0;
  if (-1 == HeaderTotalBytes(&header_copy, 0)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelPeek: header size overflow.\n");
    goto done;
  }
  NaClSrpcLog(3,
              "NaClSrpcMessageChannelPeek: read message bytes %"
              NACL_PRIdS", descs %"NACL_PRIdS".\n",
              channel->byte_count,
              channel->desc_count);
  imc_ret = MessageChannelBufferRead(channel, &header_copy, 1);
  if (imc_ret < (ssize_t) kFragmentOverhead[FIRST_FRAGMENT]) {
    NaClSrpcLog(3,
                "NaClSrpcMessageChannelPeek: read failed (%"NACL_PRIdS").\n",
                imc_ret);
    retval = ErrnoFromImcRet(imc_ret);
    goto done;
  }
  header->flags = header_copy.flags;
  header->NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH =
      header_copy.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH;
  NaClSrpcLog(3,
              "NaClSrpcMessageChannelPeek: flags %x.\n",
              header->flags);
  if (!MessageLengthsAreSane(
           &total_size,
           &fragment_size,
           (size_t) imc_ret,
           header_copy.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelPeek: message length mismatch.\n");
    retval = -NACL_ABI_EIO;
    goto done;
  }
  /* Comparison above guarantees no underflow. */
  retval = imc_ret - kFragmentOverhead[FIRST_FRAGMENT];

 done:
  free(iovec);
  return retval;
}