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; }
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; }
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; }