static int NaClDescImcBoundDescAcceptConn(struct NaClDesc *vself,
                                          struct NaClDesc **result) {
  /*
   * See NaClDescConnCapConnectAddr code in nacl_desc_conn_cap.c
   */
  struct NaClDescImcBoundDesc *self;
  int                         retval;
  NaClHandle                  nh;
  int                         nbytes;
  struct NaClMessageHeader    conn_msg;
  struct NaClDescImcDesc      *peer;

  self = (struct NaClDescImcBoundDesc *) vself;

  if (NULL == (peer = malloc(sizeof *peer))) {
    return -NACL_ABI_ENOMEM;
  }

  conn_msg.iov = 0;
  conn_msg.iov_length = 0;
  conn_msg.handle_count = 1;
  conn_msg.handles = &nh;
  conn_msg.flags = 0;
  nh = NACL_INVALID_HANDLE;

  NaClLog(3,
          ("NaClDescImcBoundDescAcceptConn(0x%08"NACL_PRIxPTR"):"
           " h = %d\n"),
          (uintptr_t) vself,
          self->h);

  if (-1 == (nbytes = NaClReceiveDatagram(self->h, &conn_msg, 0))) {
    NaClLog(LOG_ERROR,
            ("NaClDescImcBoundDescAcceptConn:"
             " could not receive connection message, errno %d\n"),
            errno);
    retval = -NACL_ABI_EMFILE;
    goto cleanup;
  }
  if (0 != nbytes) {
    NaClLog(LOG_ERROR, ("NaClDescImcBoundDescAcceptConn:"
                        " connection message contains data?!?\n"));
    retval = -NACL_ABI_EMFILE;  /* TODO(bsy): better errno? */
    goto cleanup;
  }
  if (1 != conn_msg.handle_count) {
    NaClLog(LOG_ERROR, ("NaClDescImcBoundDescAcceptConn: connection"
                        " message contains %"NACL_PRIdS" descriptors?!?\n"),
            conn_msg.handle_count);
    retval = -NACL_ABI_EMFILE;  /* TODO(bsy): better errno? */
    goto cleanup;
  }
  if (!NaClDescImcDescCtor(peer, nh)) {
    retval = -NACL_ABI_EMFILE;
    goto cleanup;
  }
  nh = NACL_INVALID_HANDLE;

  *result = (struct NaClDesc *) peer;
  retval = 0;

cleanup:
  if (retval < 0) {
    if (NACL_INVALID_HANDLE != nh) {
      (void) NaClClose(nh);
      nh = NACL_INVALID_HANDLE;
    }
    free(peer);
  }
  return retval;
}
static int NaClDescConnCapFdConnectAddr(struct NaClDesc *vself,
                                        struct NaClDesc **out_desc) {
  struct NaClDescConnCapFd  *self = (struct NaClDescConnCapFd *) vself;
  NaClHandle                sock_pair[2];
  struct NaClDescImcDesc    *connected_socket;
  char                      control_buf[CMSG_SPACE_KHANDLE_COUNT_MAX_INTS];
  struct iovec              iovec;
  struct msghdr             connect_msg;
  struct cmsghdr            *cmsg;
  int                       sent;
  int                       retval;

  assert(CMSG_SPACE(sizeof(int)) <= CMSG_SPACE_KHANDLE_COUNT_MAX_INTS);

  sock_pair[0] = NACL_INVALID_HANDLE;
  sock_pair[1] = NACL_INVALID_HANDLE;
  connected_socket = (struct NaClDescImcDesc *) NULL;

  retval = -NACL_ABI_EINVAL;

  if (0 != NaClSocketPair(sock_pair)) {
    retval = -NACL_ABI_EMFILE;
    goto cleanup;
  }

  iovec.iov_base = "c";
  iovec.iov_len = 1;
  connect_msg.msg_iov = &iovec;
  connect_msg.msg_iovlen = 1;
  connect_msg.msg_name = NULL;
  connect_msg.msg_namelen = 0;
  connect_msg.msg_control = control_buf;
  connect_msg.msg_controllen = sizeof(control_buf);
  connect_msg.msg_flags = 0;

  cmsg = CMSG_FIRSTHDR(&connect_msg);
  cmsg->cmsg_len = CMSG_LEN(sizeof(int));
  cmsg->cmsg_level = SOL_SOCKET;
  cmsg->cmsg_type = SCM_RIGHTS;
  /*
   * We use memcpy() rather than assignment through a cast to avoid
   * strict-aliasing warnings
   */
  memcpy(CMSG_DATA(cmsg), &sock_pair[0], sizeof(int));
  /* Set msg_controllen to the actual size of the cmsg. */
  connect_msg.msg_controllen = cmsg->cmsg_len;

  sent = sendmsg(self->connect_fd, &connect_msg, 0);
  if (1 != sent) {
    retval = -NACL_ABI_EIO;
    goto cleanup;
  }

  if (NACL_OSX) {
    /*
     * Mac OS X has a kernel bug in which a socket descriptor that is
     * referenced only from the message queue of another socket can
     * get garbage collected.  This causes the socket descriptor not
     * to work properly.  To work around this, we don't close our
     * reference to the socket until we receive an acknowledgement
     * that it has been successfully received.
     *
     * We cannot receive the acknowledgement through self->connect_fd
     * because this FD could be shared between multiple processes.  So
     * we receive the acknowledgement through the socket pair that we
     * have just created.
     *
     * However, this creates a risk that we are left hanging if the
     * other process dies after our sendmsg() call, because we are
     * holding on to the socket that it would use to send the ack.  To
     * avoid this problem, we use poll() so that we will be notified
     * if self->connect_fd becomes unwritable.
     * TODO(mseaborn): Add a test case to simulate that scenario.
     *
     * See http://code.google.com/p/nativeclient/issues/detail?id=1796
     *
     * Note that we are relying on a corner case of poll() here.
     * Using POLLHUP in "events" is not meaningful on Linux, which is
     * documented as ignoring POLLHUP as an input argument and will
     * return POLLHUP in "revents" even if it not present in "events".
     * On Mac OS X, however, passing events == 0 does not work if we
     * want to get POLLHUP.  We are in the unusual situation of
     * waiting for a socket to become *un*writable.
     */
    struct pollfd poll_fds[2];
    poll_fds[0].fd = self->connect_fd;
    poll_fds[0].events = POLLHUP;
    poll_fds[1].fd = sock_pair[1];
    poll_fds[1].events = POLLIN;
    if (poll(poll_fds, 2, -1) < 0) {
      NaClLog(LOG_ERROR,
              "NaClDescConnCapFdConnectAddr: poll() failed, errno %d\n", errno);
      retval = -NACL_ABI_EIO;
      goto cleanup;
    }
    /*
     * It is not necessarily an error if POLLHUP fires on
     * self->connect_fd: The other process could have done
     * imc_accept(S) and then closed S.  This means it will have
     * received sock_pair[0] successfully, so we can close our
     * reference to sock_pair[0] and then receive the ack.
     * TODO(mseaborn): Add a test case to cover this scenario.
     */
  }

  (void) NaClClose(sock_pair[0]);
  sock_pair[0] = NACL_INVALID_HANDLE;

  if (NACL_OSX) {
    /* Receive the acknowledgement.  We do not expect this to block. */
    char ack_buffer[1];
    ssize_t received = recv(sock_pair[1], ack_buffer, sizeof(ack_buffer), 0);
    if (received != 1 || ack_buffer[0] != 'a') {
      retval = -NACL_ABI_EIO;
      goto cleanup;
    }
  }

  connected_socket = malloc(sizeof(*connected_socket));
  if (NULL == connected_socket ||
      !NaClDescImcDescCtor(connected_socket, sock_pair[1])) {
    retval = -NACL_ABI_ENOMEM;
    goto cleanup;
  }
  sock_pair[1] = NACL_INVALID_HANDLE;

  *out_desc = (struct NaClDesc *) connected_socket;
  connected_socket = NULL;
  retval = 0;

cleanup:
  NaClSafeCloseNaClHandle(sock_pair[0]);
  NaClSafeCloseNaClHandle(sock_pair[1]);
  free(connected_socket);

  return retval;
}
int NaClDescConnCapConnectAddr(struct NaClDesc *vself,
                               struct NaClDesc **out_desc) {
  /*
   * See NaClDescImcBoundDescAcceptConn code in
   * nacl_desc_imc_bound_desc.c
   */
  struct NaClDescConnCap      *self;
  int                         retval;
  NaClHandle                  nh[2];
  size_t                      ix;
  struct NaClMessageHeader    conn_msg;
  struct NaClDescImcDesc      *peer;

  NaClLog(3, "Entered NaClDescConnCapConnectAddr\n");
  self = (struct NaClDescConnCap *) vself;

  peer = NULL;
  for (ix = 0; ix < NACL_ARRAY_SIZE(nh); ++ix) {
    nh[ix] = NACL_INVALID_HANDLE;
  }

  NaClLog(4, " socket address %.*s\n", NACL_PATH_MAX, self->cap.path);

  if (NULL == (peer = malloc(sizeof *peer))) {
    retval = -NACL_ABI_ENOMEM;
    goto cleanup;
  }

  if (0 != NaClSocketPair(nh)) {
    retval = -NACL_ABI_EMFILE;
    goto cleanup;
  }

  conn_msg.iov_length = 0;
  conn_msg.iov = NULL;
  conn_msg.handles = &nh[0];
  conn_msg.handle_count = 1;  /* send nh[0], keep nh[1] */
  conn_msg.flags = 0;

  NaClLog(4, " sending connection message\n");
  if (-1 == NaClSendDatagramTo(&conn_msg, 0, &self->cap)) {
    NaClLog(LOG_ERROR, ("NaClDescConnCapConnectAddr:"
                        " initial connect message could not be sent.\n"));
    retval = -NACL_ABI_EIO;
    goto cleanup;
  }

  (void) NaClClose(nh[0]);
  nh[0] = NACL_INVALID_HANDLE;
  NaClLog(4, " creating NaClDescImcDesc for local end of socketpair\n");
  if (!NaClDescImcDescCtor(peer, nh[1])) {
    retval = -NACL_ABI_EMFILE;  /* TODO(bsy): is this the right errno? */
    goto cleanup;
  }
  nh[1] = NACL_INVALID_HANDLE;

  *out_desc = (struct NaClDesc *) peer;
  retval = 0;

cleanup:
  if (retval < 0) {
    NaClLog(4, " error return; cleaning up\n");
    if (NACL_INVALID_HANDLE != nh[0])
      (void) NaClClose(nh[0]);
    if (NACL_INVALID_HANDLE != nh[1])
      (void) NaClClose(nh[1]);
    /* peer is not constructed, so we need only to free the memory */
    free(peer);
  }
  return retval;
}
Example #4
0
int main(int argc, char* argv[]) {
  int result;
  NaClHandle pair[2];
#ifdef __native_client__
  int imc_desc[2];
#else
  struct NaClDescImcDesc* imc_desc[2];
#endif
  struct NaClThread thr;
  NaClSrpcChannel channel;
  NaClSrpcError error;
  struct ServiceThreadArgs* threadArg;

  UNREFERENCED_PARAMETER(argc);
  UNREFERENCED_PARAMETER(argv);

  NaClSrpcModuleInit();

  if (0 != NaClSocketPair(pair)) {
    failWithErrno("SocketPair");
  }

#ifdef __native_client__
  imc_desc[0] = pair[0];
  imc_desc[1] = pair[1];
#else
  NaClNrdAllModulesInit();

  imc_desc[0] = (struct NaClDescImcDesc*) calloc(1, sizeof(*imc_desc[0]));
  if (0 == imc_desc[0]) {
    failWithErrno("calloc");
  }
  imc_desc[1] = (struct NaClDescImcDesc*) calloc(1, sizeof(*imc_desc[1]));
  if (0 == imc_desc[1]) {
    failWithErrno("calloc");
  }

  if (!NaClDescImcDescCtor(imc_desc[0], pair[0])) {
    failWithErrno("NaClDescImcDescCtor");
  }

  if (!NaClDescImcDescCtor(imc_desc[1], pair[1])) {
    failWithErrno("NaClDescImcDescCtor");
  }
#endif

  threadArg = (struct ServiceThreadArgs*) calloc(1, sizeof(*threadArg));
  if (NULL == threadArg) {
    failWithErrno("calloc for threadArg");
  }
  threadArg->desc = (NaClSrpcImcDescType)imc_desc[0];

  if (!NaClThreadCreateJoinable(&thr, serviceThread, threadArg, 128*1024)) {
    failWithErrno("NaClThreadCtor");
  }

  if (!NaClSrpcClientCtor(&channel, (NaClSrpcImcDescType) imc_desc[1])) {
    failWithErrno("NaClSrpcClientCtor");
  }

  error = NaClSrpcInvokeBySignature(&channel, "getNum::i", &result);
  if (NACL_SRPC_RESULT_OK != error) {
    fprintf(stderr,
            "SRPC call to getNum::i failed: %s\n",
            NaClSrpcErrorString(error));
    exit(EXIT_FAILURE);
  }

  if (TEST_NUM != result) {
    fprintf(stderr, "expected: %d, got: %d\n", TEST_NUM, result);
    exit(EXIT_FAILURE);
  }

  NaClSrpcDtor(&channel);
#ifdef __native_client__
  close(imc_desc[1]);
#else
  NaClDescUnref((NaClSrpcImcDescType)imc_desc[1]);
#endif

  NaClThreadJoin(&thr);

  NaClSrpcModuleFini();
  return 0;
}
int NaClDescImcBoundDescAcceptConn(struct NaClDesc *vself,
                                   struct NaClDesc **result) {
  /*
   * See NaClDescConnCapConnectAddr code in nacl_desc_conn_cap.c
   */
  struct NaClDescImcBoundDesc *self = (struct NaClDescImcBoundDesc *) vself;
  int                         retval;
  NaClHandle                  received_fd;
  struct NaClDescImcDesc      *peer;
  struct iovec                iovec;
  struct msghdr               accept_msg;
  struct cmsghdr              *cmsg;
  char                        data_buf[1];
  char                        control_buf[CMSG_SPACE_KHANDLE_COUNT_MAX_INTS];
  int                         received;

  assert(CMSG_SPACE(sizeof(int)) <= CMSG_SPACE_KHANDLE_COUNT_MAX_INTS);

  peer = malloc(sizeof(*peer));
  if (peer == NULL) {
    return -NACL_ABI_ENOMEM;
  }

  iovec.iov_base = data_buf;
  iovec.iov_len = sizeof(data_buf);
  accept_msg.msg_iov = &iovec;
  accept_msg.msg_iovlen = 1;
  accept_msg.msg_name = NULL;
  accept_msg.msg_namelen = 0;
  accept_msg.msg_control = control_buf;
  accept_msg.msg_controllen = sizeof(control_buf);

  NaClLog(3,
          ("NaClDescImcBoundDescAcceptConn(0x%08"NACL_PRIxPTR", "
           " h = %d\n"),
          (uintptr_t) vself,
          self->h);

  received_fd = NACL_INVALID_HANDLE;

  received = recvmsg(self->h, &accept_msg, 0);
  if (received != 1 || data_buf[0] != 'c') {
    NaClLog(LOG_ERROR,
            ("NaClDescImcBoundDescAcceptConn:"
             " could not receive connection message, errno %d\n"),
            errno);
    retval = -NACL_ABI_EIO;
    goto cleanup;
  }

  retval = 0;
  /*
   * If we got more than one fd in the message, we must clean up by
   * closing those extra descriptors.  Given that control_buf is
   * CMSG_SPACE(sizeof(int)) in size, this shouldn't actualy happen.
   *
   * We follow the principle of being strict in what we send and
   * tolerant in what we accept, and simply discard the extra
   * descriptors without signaling an error (other than a log
   * message).  This makes it easier to rev the protocol w/o
   * necessarily requiring a protocol version number bump.
   */
  for (cmsg = CMSG_FIRSTHDR(&accept_msg);
       cmsg != NULL;
       cmsg = CMSG_NXTHDR(&accept_msg, cmsg)) {
    if (cmsg->cmsg_level == SOL_SOCKET &&
        cmsg->cmsg_type == SCM_RIGHTS &&
        cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
      if (NACL_INVALID_HANDLE != received_fd) {
        int bad_fd;

        /* can only be true on 2nd time through */
        NaClLog(LOG_ERROR, ("NaClDescImcBoundDescAcceptConn: connection"
                            " message contains more than 1 descriptors?!?\n"));
        memcpy(&bad_fd, CMSG_DATA(cmsg), sizeof bad_fd);
        (void) close(bad_fd);
        /*
         * If we want to NOT be tolerant about what we receive, we could
         * uncomment this:
         *
         * retval = -NACL_ABI_EIO;
         */
        continue;
      }
      /*
       * We use memcpy() rather than assignment through a cast to avoid
       * strict-aliasing warnings
       */
      memcpy(&received_fd, CMSG_DATA(cmsg), sizeof received_fd);
    }
  }
  if (0 != retval) {
    goto cleanup;
  }
  if (NACL_INVALID_HANDLE == received_fd) {
    NaClLog(LOG_ERROR, ("NaClDescImcBoundDescAcceptConn: connection"
                        " message contains NO descriptors?!?\n"));
    retval = -NACL_ABI_EIO;
    goto cleanup;
  }

  if (NACL_OSX) {
    /*
     * Send a message to acknowledge that we received the socket FD
     * successfully.  This is part of a workaround for a Mac OS X
     * kernel bug.
     * See http://code.google.com/p/nativeclient/issues/detail?id=1796
     */
    ssize_t sent = send(received_fd, "a", 1, 0);
    if (sent != 1) {
      retval = -NACL_ABI_EIO;
      goto cleanup;
    }
  }

  if (!NaClDescImcDescCtor(peer, received_fd)) {
    retval = -NACL_ABI_EMFILE;
    goto cleanup;
  }
  received_fd = NACL_INVALID_HANDLE;

  *result = (struct NaClDesc *) peer;
  retval = 0;

cleanup:
  if (retval < 0) {
    if (received_fd != NACL_INVALID_HANDLE) {
      (void) close(received_fd);
    }
    free(peer);
  }
  return retval;
}
Example #6
0
int main(int argc, char* argv[]) {
    nacl_abi_size_t char_array_count;
    char* char_array_carr;
    NaClHandle pair[2];
#ifdef __native_client__
    int imc_desc[2];
#else
    struct NaClDescImcDesc* imc_desc[2];
#endif
    struct NaClThread thr;
    NaClSrpcChannel channel;
    NaClSrpcError error;
    struct ServiceThreadArgs* threadArg;

    UNREFERENCED_PARAMETER(argc);
    UNREFERENCED_PARAMETER(argv);

    NaClSrpcModuleInit();

    if (0 != NaClSocketPair(pair)) {
        failWithErrno("SocketPair");
    }

#ifdef __native_client__
    imc_desc[0] = pair[0];
    imc_desc[1] = pair[1];
#else
    NaClNrdAllModulesInit();

    imc_desc[0] = (struct NaClDescImcDesc*) calloc(1, sizeof(*imc_desc[0]));
    if (0 == imc_desc[0]) {
        failWithErrno("calloc");
    }
    imc_desc[1] = (struct NaClDescImcDesc*) calloc(1, sizeof(*imc_desc[1]));
    if (0 == imc_desc[1]) {
        failWithErrno("calloc");
    }

    if (!NaClDescImcDescCtor(imc_desc[0], pair[0])) {
        failWithErrno("NaClDescImcDescCtor");
    }

    if (!NaClDescImcDescCtor(imc_desc[1], pair[1])) {
        failWithErrno("NaClDescImcDescCtor");
    }
#endif

    threadArg = (struct ServiceThreadArgs*) calloc(1, sizeof(*threadArg));
    if (NULL == threadArg) {
        failWithErrno("calloc for threadArg");
    }
    threadArg->desc = (NaClSrpcImcDescType)imc_desc[0];

    if (!NaClThreadCreateJoinable(&thr, serviceThread, threadArg, 128*1024)) {
        failWithErrno("NaClThreadCtor");
    }

    if (!NaClSrpcClientCtor(&channel, (NaClSrpcImcDescType) imc_desc[1])) {
        failWithErrno("NaClSrpcClientCtor");
    }

    char_array_count = TEST_ARRAY_LENGTH;
    char_array_carr = (char*) calloc(TEST_ARRAY_LENGTH, sizeof(char));
    if (!char_array_carr) {
        failWithErrno("calloc for char_array");
    }
    memset(char_array_carr, TEST_NUM, TEST_ARRAY_LENGTH);

    error = NaClSrpcInvokeBySignature(&channel,
                                      "putArr:C:",
                                      char_array_count,
                                      char_array_carr);

    if (NACL_SRPC_RESULT_OK != error) {
        fprintf(stderr,
                "SRPC call to putArr:C: failed: %s\n",
                NaClSrpcErrorString(error));
        exit(EXIT_FAILURE);
    }
    free(char_array_carr);

    NaClSrpcDtor(&channel);
#ifdef __native_client__
    close(imc_desc[1]);
#else
    NaClDescUnref((NaClSrpcImcDescType)imc_desc[1]);
#endif

    NaClThreadJoin(&thr);

    NaClSrpcModuleFini();
    return 0;
}