コード例 #1
0
int main(int argc, char* argv[]) {
  int result;
  NaClHandle pair[2];
  NaClMessageHeader header;
  NaClIOVec vec;
  char buffer[] = "Hello!";

  g_front = NaClBoundSocket(&test_address);
  if (g_front == NACL_INVALID_HANDLE) {
    PrintError("BoundSocket");
    exit(EXIT_FAILURE);
  }
  atexit(CleanUp);

  if (NaClSocketPair(pair) != 0) {
    PrintError("SocketPair");
    exit(EXIT_FAILURE);
  }

  vec.base = buffer;
  vec.length = sizeof buffer;

  /* Test SendDatagram */
  header.iov = &vec;
  header.iov_length = 1;
  header.handles = NULL;
  header.handle_count = 0;
  result = NaClSendDatagram(pair[0], &header, 0);
  assert(result == sizeof buffer);

  /* Test ReceiveDatagram */
  memset(buffer, 0, sizeof buffer);
  header.iov = &vec;
  header.iov_length = 1;
  header.handles = NULL;
  header.handle_count = 0;
  result = NaClReceiveDatagram(pair[1], &header, 0);
  assert(result == sizeof buffer);

  assert(strcmp(buffer, "Hello!") == 0);
  printf("%s\n", buffer);

  (void) NaClClose(pair[0]);
  (void) NaClClose(pair[1]);

  return 0;
}
コード例 #2
0
int main(int argc, char **argv) {
  struct NaClApp app[2];
  struct NaClDesc *nd;
  NaClHandle handle_pair[2];
  int i;
  char *domain1_args[] = {"prog", "domain1"};
  char *domain2_args[] = {"prog", "domain2"};
  int return_code;

  if (argc != 2)
    NaClLog(LOG_FATAL, "Expected 1 argument: executable filename\n");

  NaClAllModulesInit();

  NaClFileNameForValgrind(argv[1]);
  nd = (struct NaClDesc *) NaClDescIoDescOpen(argv[1], NACL_ABI_O_RDONLY, 0);
  CHECK(NULL != nd);

  for (i = 0; i < 2; i++) {
    CHECK(NaClAppCtor(&app[i]));

    /* Use a smaller guest address space size, because 32-bit Windows
       does not let us allocate 2GB of address space.  We don't do this
       for x86-64 because there is an assertion in NaClAllocateSpace()
       that requires 4GB. */
#if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
    app[i].addr_bits = 29; /* 512MB per process */
#endif

    /*
     * On x86-32, we cannot enable ASLR even when the address space is
     * 512MB.  In particular, when on Windows where the available
     * contiguous address space is tight, if the random choice for the
     * base of the first 512MB address space puts that address space
     * in the middle of the available address space region, the
     * address space allocation code might not be able to (randomly)
     * find another contiguous 512MB region for the second NaCl
     * module.
     */
    CHECK(NaClAppLoadFileAslr(nd, &app[i],
                              NACL_DISABLE_ASLR) == LOAD_OK);
    NaClAppInitialDescriptorHookup(&app[i]);
    CHECK(NaClAppPrepareToLaunch(&app[i]) == LOAD_OK);
  }

  /* Set up an IMC connection between the two guests.  This allows us
     to test communications between the two and also synchronise the
     output for the purpose of checking against the golden file. */
  CHECK(NaClSocketPair(handle_pair) == 0);
  NaClAddImcHandle(&app[0], handle_pair[0], SEND_DESC);
  NaClAddImcHandle(&app[1], handle_pair[1], RECEIVE_DESC);

  CHECK(NaClCreateMainThread(&app[0], 2, domain1_args, NULL));
  CHECK(NaClCreateMainThread(&app[1], 2, domain2_args, NULL));

  return_code = NaClWaitForMainThreadToExit(&app[0]);
  CHECK(return_code == 101);
  return_code = NaClWaitForMainThreadToExit(&app[1]);
  CHECK(return_code == 102);

  /*
   * Avoid calling exit() because it runs process-global destructors
   * which might break code that is running in our unjoined threads.
   */
  NaClExit(0);
}
コード例 #3
0
ファイル: nrd_xfer.c プロジェクト: Lind-Project/native_client
int32_t NaClCommonDescSocketPair(struct NaClDesc *pair[2]) {
  int32_t                         retval = -NACL_ABI_EIO;
  struct NaClDescXferableDataDesc *d0;
  struct NaClDescXferableDataDesc *d1;
  NaClHandle                      sock_pair[2];

  /*
   * mark resources to enable easy cleanup
   */
  d0 = NULL;
  d1 = NULL;
  sock_pair[0] = NACL_INVALID_HANDLE;
  sock_pair[1] = NACL_INVALID_HANDLE;

  if (0 != NaClSocketPair(sock_pair)) {
    NaClLog(1,
            "NaClCommonSysImc_Socket_Pair: IMC socket pair creation failed\n");
    retval = -NACL_ABI_ENFILE;
    goto cleanup;
  }
  if (NULL == (d0 = malloc(sizeof *d0))) {
    retval = -NACL_ABI_ENOMEM;
    goto cleanup;
  }
  if (NULL == (d1 = malloc(sizeof *d1))) {
    free((void *) d0);
    d0 = NULL;
    retval = -NACL_ABI_ENOMEM;
    goto cleanup;
  }
  if (!NaClDescXferableDataDescCtor(d0, sock_pair[0])) {
    free((void *) d0);
    d0 = NULL;
    free((void *) d1);
    d1 = NULL;
    retval = -NACL_ABI_ENFILE;
    goto cleanup;
  }
  sock_pair[0] = NACL_INVALID_HANDLE;  /* ctor took ownership */
  if (!NaClDescXferableDataDescCtor(d1, sock_pair[1])) {
    free((void *) d1);
    d1 = NULL;
    retval = -NACL_ABI_ENFILE;
    goto cleanup;
  }
  sock_pair[1] = NACL_INVALID_HANDLE;  /* ctor took ownership */

  pair[0] = (struct NaClDesc *) d0;
  d0 = NULL;

  pair[1] = (struct NaClDesc *) d1;
  d1 = NULL;

  retval = 0;

 cleanup:
  /*
   * pre: d0 and d1 must either be NULL or point to fully constructed
   * NaClDesc objects
   */
  if (NULL != d0) {
    NaClDescUnref((struct NaClDesc *) d0);
  }
  if (NULL != d1) {
    NaClDescUnref((struct NaClDesc *) d1);
  }
  if (NACL_INVALID_HANDLE != sock_pair[0]) {
    (void) NaClClose(sock_pair[0]);
  }
  if (NACL_INVALID_HANDLE != sock_pair[1]) {
    (void) NaClClose(sock_pair[1]);
  }

  free(d0);
  free(d1);

  return retval;
}
コード例 #4
0
int NaClDebugExceptionHandlerStandaloneAttach(const void *info,
                                              size_t info_size) {
  NaClHandle sockets[2];
  STARTUPINFOA startup_info;
  PROCESS_INFORMATION process_information;
  char sel_ldr_path[PATH_MAX];
  /*
   * 'args' only needs to be long enough to store the format string
   * below plus 2*sizeof(struct StartupInfo).  PATH_MAX is plenty.
   */
  char args[PATH_MAX];
  char *dest;
  size_t length;
  size_t index;
  char buffer[1];
  DWORD bytes_read;

  if (NaClSocketPair(sockets) != 0) {
    NaClLog(LOG_ERROR, "NaClDebugExceptionHandlerStandaloneAttach: "
            "Failed to create socket pair\n");
    return 0;
  }
  if (!SetHandleInformation(sockets[1], HANDLE_FLAG_INHERIT,
                            HANDLE_FLAG_INHERIT)) {
    NaClLog(LOG_ERROR, "NaClDebugExceptionHandlerStandaloneAttach: "
            "SetHandleInformation() failed\n");
    return 0;
  }
  memset(&startup_info, 0, sizeof(startup_info));
  startup_info.cb = sizeof(startup_info);
  memset(&process_information, 0, sizeof(process_information));

  /*
   * Note that the child process does not use argv[0] so we do not
   * need to provide the executable filename in that argument; it is
   * tricky to get the quoting right on Windows.
   */
  length = SNPRINTF(args, sizeof(args),
                    "argv0 --debug-exception-handler %i %i ",
                    GetCurrentProcessId(), (int)(intptr_t) sockets[1]);
  CHECK(length > 0 && length < sizeof(args));
  dest = args + length;
  for (index = 0; index < info_size; index++) {
    CHECK(dest + 3 < args + sizeof(args));
    CHECK(SNPRINTF(dest, 2, "%02x", ((uint8_t *) info)[index]) == 2);
    dest += 2;
  }
  *dest = 0;

  length = GetModuleFileNameA(NULL, sel_ldr_path, sizeof(sel_ldr_path));
  if (length == 0 || length >= sizeof(sel_ldr_path) - 1) {
    NaClLog(LOG_ERROR, "NaClDebugExceptionHandlerStandaloneAttach: "
            "Failed to get the executable's filename\n");
    return 0;
  }

  if (!CreateProcessA(sel_ldr_path, args, NULL, NULL,
                      /* bInheritHandles= */ TRUE, 0,
                      NULL, NULL, &startup_info, &process_information)) {
    NaClLog(LOG_ERROR, "NaClDebugExceptionHandlerStandaloneAttach: "
            "Failed to create debug exception handler process\n");
    return 0;
  }
  CloseHandle(sockets[1]);
  CloseHandle(process_information.hThread);
  CloseHandle(process_information.hProcess);

  /*
   * We wait for a message from the child process to indicate that it
   * has successfully attached to us as a debugger.
   */
  if (!ReadFile(sockets[0], buffer, sizeof(buffer), &bytes_read, NULL)) {
    NaClLog(LOG_ERROR, "NaClDebugExceptionHandlerStandaloneAttach: "
            "Failed to read message\n");
    return 0;
  }
  if (bytes_read != 1 || buffer[0] != 'k') {
    NaClLog(LOG_ERROR, "NaClDebugExceptionHandlerStandaloneAttach: "
            "Did not receive expected reply message\n");
    return 0;
  }
  CloseHandle(sockets[0]);
  return 1;
}
コード例 #5
0
ファイル: ncserver.c プロジェクト: Yen-Bill-HUANG/ncserver
int main(int argc, char **argv) {
  
  struct NaClApp app;
  struct NaClDesc *nd;
  NaClHandle handle_pair[2];
  char *parse_args[] = {"prog", "parse"};
  int return_code;
  char buffer[100];
  NaClMessageHeader header;
  NaClIOVec vec;

  struct ncwebserver_t server;
  struct ncwebserver_conf_t conf;

  if (argc < 2){
    usage(ncwebserver_usage_string);
  }

  NaClAllModulesInit();
  NaClFileNameForValgrind(argv[1]);
  nd = (struct NaClDesc *) NaClDescIoDescOpen(argv[1], NACL_ABI_O_RDONLY, 0);
  CHECK(NULL != nd);
  CHECK(NaClAppCtor(&app));
  /* Use a smaller guest address space size, because 32-bit Windows
     does not let us allocate 2GB of address space.  We don't do this
     for x86-64 because there is an assertion in NaClAllocateSpace()
     that requires 4GB. */
#if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
  app.addr_bits = 29; /* 512MB per process */
#endif
  /*
   * On x86-32, we cannot enable ASLR even when the address space is
   * 512MB.  In particular, when on Windows where the available
   * contiguous address space is tight, if the random choice for the
   * base of the first 512MB address space puts that address space
   * in the middle of the available address space region, the
   * address space allocation code might not be able to (randomly)
   * find another contiguous 512MB region for the second NaCl
   * module.
   */
  CHECK(NaClAppLoadFileAslr(nd, &app,
                            NACL_DISABLE_ASLR) == LOAD_OK);
  NaClAppInitialDescriptorHookup(&app);
  CHECK(NaClAppPrepareToLaunch(&app) == LOAD_OK);
  /* Set up an IMC connection between the host and guest. */
  CHECK(NaClSocketPair(handle_pair) == 0);
  NaClAddImcHandle(&app, handle_pair[0], SEND_DESC);
  CHECK(NaClCreateMainThread(&app, 2, parse_args, NULL));
  return_code = NaClWaitForMainThreadToExit(&app);
  CHECK(return_code == 101);

  // receive message sent from HTML parser
  vec.base = buffer;
  vec.length = sizeof buffer;
  memset(buffer, 0, sizeof buffer);
  header.iov = &vec;
  header.iov_length = 1;
  header.handles = NULL;
  header.handle_count = 0;
  return_code = NaClReceiveDatagram(handle_pair[1], &header, 0);
  assert(return_code == sizeof buffer);
  printf("Server received \"%s\" (with size = %d)\n", buffer+16, return_code);

  // run web server
  memset(&conf, 0, sizeof(conf));
  parse_argv(argc, argv, &conf);
  ncwebserver_init(&server, &conf);
  ncwebserver_start(&server);

  /*
   * Avoid calling exit() because it runs process-global destructors
   * which might break code that is running in our unjoined threads.
   */
  NaClExit(0);
}
コード例 #6
0
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;
}
コード例 #7
0
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;
}
コード例 #8
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;
}
コード例 #9
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;
}