int main(int argc, char* argv[]) { NaClHandle handle; UNREFERENCED_PARAMETER(argc); UNREFERENCED_PARAMETER(argv); /* Check not executable shared memory. */ handle = NaClCreateMemoryObject(MEMORY_SIZE, 0); if (NACL_INVALID_HANDLE == handle) { failWithErrno("NaClCreateMemoryObject(size, 0)"); } if (0 != NaClClose(handle)) { failWithErrno("NaClClose(0)"); } /* Check executable shared memory. */ handle = NaClCreateMemoryObject(MEMORY_SIZE, 1); #ifdef __native_client__ if (NACL_INVALID_HANDLE != handle) { fprintf(stderr, "NaClCreateMemoryObject(size, 1) returned a valid shm. " "It must return -1\n"); exit(EXIT_FAILURE); } #else if (NACL_INVALID_HANDLE == handle) { failWithErrno("NaClCreateMemoryObject(size, 1)"); } if (0 != NaClClose(handle)) { failWithErrno("NaClClose(1)"); } #endif return 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; }
static void NaClDescImcShmDtor(struct NaClRefCount *vself) { struct NaClDescImcShm *self = (struct NaClDescImcShm *) vself; (void) NaClClose(self->h); self->h = NACL_INVALID_HANDLE; vself->vtbl = (struct NaClRefCountVtbl const *) &kNaClDescVtbl; (*vself->vtbl->Dtor)(vself); }
void NaClDescImcBoundDescDtor(struct NaClRefCount *vself) { struct NaClDescImcBoundDesc *self = (struct NaClDescImcBoundDesc *) vself; NaClClose(self->h); self->h = NACL_INVALID_HANDLE; vself->vtbl = (struct NaClRefCountVtbl const *) &kNaClDescVtbl; (*vself->vtbl->Dtor)(vself); }
void NaClDescSyncSocketDtor(struct NaClDesc *vself) { struct NaClDescSyncSocket *self = ((struct NaClDescSyncSocket *) vself); NaClClose(self->h); self->h = NACL_INVALID_HANDLE; self->base.vtbl = (struct NaClDescVtbl *) NULL; NaClDescDtor(vself); }
static void NaClDescConnCapFdDtor(struct NaClRefCount *vself) { struct NaClDescConnCapFd *self = (struct NaClDescConnCapFd *) vself; (void) NaClClose(self->connect_fd); self->connect_fd = NACL_INVALID_HANDLE; vself->vtbl = (struct NaClRefCountVtbl const *) &kNaClDescVtbl; (*vself->vtbl->Dtor)(vself); return; }
int32_t NaClCommonDescMakeBoundSock(struct NaClDesc *pair[2]) { int32_t retval; struct NaClSocketAddress sa; struct NaClDescConnCap *ccp; NaClHandle h; struct NaClDescImcBoundDesc *idp; retval = -NACL_ABI_ENOMEM; ccp = NULL; idp = NULL; h = NACL_INVALID_HANDLE; /* * create NaClDescConnCap object, invoke NaClBoundSocket, create * an NaClDescImcDesc object. put both into open file table. */ ccp = malloc(sizeof *ccp); if (NULL == ccp) { goto cleanup; } idp = malloc(sizeof *idp); if (NULL == idp) { goto cleanup; } do { NaClGenerateRandomPath(&sa.path[0], NACL_PATH_MAX); h = NaClBoundSocket(&sa); NaClLog(3, "NaClCommonDescMakeBoundSock: sa: %s, h 0x%"NACL_PRIxPTR"\n", sa.path, (uintptr_t) h); } while (NACL_INVALID_HANDLE == h); if (!NaClDescConnCapCtor(ccp, &sa)) { goto cleanup; } if (!NaClDescImcBoundDescCtor(idp, h)) { NaClDescUnref((struct NaClDesc *) ccp); goto cleanup; } h = NACL_INVALID_HANDLE; /* idp took ownership */ pair[0] = (struct NaClDesc *) idp; idp = NULL; pair[1] = (struct NaClDesc *) ccp; ccp = NULL; retval = 0; cleanup: free(idp); free(ccp); if (NACL_INVALID_HANDLE != h) { (void) NaClClose(h); } return retval; }
static void NaClDescImcConnectedDescDtor(struct NaClRefCount *vself) { struct NaClDescImcConnectedDesc *self = ((struct NaClDescImcConnectedDesc *) vself); if (self->h != NACL_INVALID_HANDLE) { (void) NaClClose(self->h); } self->h = NACL_INVALID_HANDLE; vself->vtbl = (struct NaClRefCountVtbl const *) &kNaClDescVtbl; (*vself->vtbl->Dtor)(vself); }
int NaClDescImcShmAllocCtor(struct NaClDescImcShm *self, nacl_off64_t size, int executable) { NaClHandle h; int rv; if (size < 0 || SIZE_T_MAX < (uint64_t) size) { NaClLog(4, "NaClDescImcShmAllocCtor: requested size 0x%08"NACL_PRIx64 " (0x%08"NACL_PRId64") too large\n", size, size); return 0; } h = NaClCreateMemoryObject((size_t) size, executable); if (NACL_INVALID_HANDLE == h) { return 0; } if (0 == (rv = NaClDescImcShmCtor(self, h, size))) { (void) NaClClose(h); } return rv; }
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; }
int NaClSafeCloseNaClHandle(NaClHandle h) { if (NACL_INVALID_HANDLE != h) { return NaClClose(h); } return 0; }
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; }
/* set *out_desc to struct NaClDescIo * output */ int NaClDescIoInternalize(struct NaClDesc **out_desc, struct NaClDescXferState *xfer, struct NaClDescQuotaInterface *quota_interface) { int rv; NaClHandle h; int d; int flags; struct NaClHostDesc *nhdp; struct NaClDescIoDesc *ndidp; UNREFERENCED_PARAMETER(quota_interface); rv = -NACL_ABI_EIO; /* catch-all */ h = NACL_INVALID_HANDLE; nhdp = NULL; ndidp = NULL; nhdp = malloc(sizeof *nhdp); if (NULL == nhdp) { rv = -NACL_ABI_ENOMEM; goto cleanup; } ndidp = malloc(sizeof *ndidp); if (!ndidp) { rv = -NACL_ABI_ENOMEM; goto cleanup; } if (!NaClDescInternalizeCtor((struct NaClDesc *) ndidp, xfer)) { rv = -NACL_ABI_ENOMEM; goto cleanup; } if (xfer->next_handle == xfer->handle_buffer_end || xfer->next_byte + sizeof ndidp->hd->flags > xfer->byte_buffer_end) { rv = -NACL_ABI_EIO; goto cleanup_ndidp_dtor; } NACL_COMPILE_TIME_ASSERT(sizeof flags == sizeof(ndidp->hd->flags)); memcpy(&flags, xfer->next_byte, sizeof flags); xfer->next_byte += sizeof flags; h = *xfer->next_handle; *xfer->next_handle++ = NACL_INVALID_HANDLE; #if NACL_WINDOWS if (-1 == (d = _open_osfhandle((intptr_t) h, _O_RDWR | _O_BINARY))) { rv = -NACL_ABI_EIO; goto cleanup_ndidp_dtor; } #else d = h; #endif /* * We mark it as read/write, but don't really know for sure until we * try to make those syscalls (in which case we'd get EBADF). */ if ((rv = NaClHostDescPosixTake(nhdp, d, flags)) < 0) { goto cleanup_ndidp_dtor; } h = NACL_INVALID_HANDLE; /* nhdp took ownership of h */ if (!NaClDescIoDescSubclassCtor(ndidp, nhdp)) { rv = -NACL_ABI_ENOMEM; goto cleanup_nhdp_dtor; } /* * ndidp took ownership of nhdp, now give ownership of ndidp to caller. */ *out_desc = (struct NaClDesc *) ndidp; rv = 0; cleanup_nhdp_dtor: if (rv < 0) { if (0 != NaClHostDescClose(nhdp)) { NaClLog(LOG_FATAL, "NaClDescIoInternalize: NaClHostDescClose failed\n"); } } cleanup_ndidp_dtor: if (rv < 0) { NaClDescSafeUnref((struct NaClDesc *) ndidp); ndidp = NULL; } cleanup: if (rv < 0) { free(nhdp); free(ndidp); if (NACL_INVALID_HANDLE != h) { (void) NaClClose(h); } } return rv; }
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; }
static void CleanUp(void) { (void) NaClClose(g_front); }
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; }